Technical Insights: Azure, .NET, Dynamics 365 & EV Charging Architecture

Month: April 2025

Mastering SOLID Principles in C# Development

SOLID Pattern Object Oriented Design and How to Use It in C#

  • Enhances maintainability and scalability of applications.
  • Guides developers in crafting robust software systems.
  • Encourages extensible software architectures.
  • Improves reliability and promotes clean design.
  • Facilitates easier testing and mocking through abstraction.

Table of Contents

Understanding SOLID Principles

The SOLID acronym comprises five principles:

  1. Single Responsibility Principle (SRP)
  2. Open/Closed Principle (OCP)
  3. Liskov Substitution Principle (LSP)
  4. Interface Segregation Principle (ISP)
  5. Dependency Inversion Principle (DIP)

While these principles are applicable across various programming languages, they align exceptionally well with C# due to its robust type system and object-oriented capabilities. Let’s delve into each principle in detail.

Single Responsibility Principle (SRP)

Definition: A class should have only one reason to change, meaning it should only have one job or responsibility.

Implementation in C#:

Consider the following implementation where a class violates SRP by performing multiple roles:


// Bad example - multiple responsibilities
public class UserService
{
    public void RegisterUser(string email, string password)
    {
        // Register user logic
        // Send email logic
        // Log activity
    }
}

In contrast, adhering to the Single Responsibility Principle leads to a more maintainable structure:


// Better example - single responsibility
public class UserRegistration
{
    private readonly EmailService _emailService;
    private readonly LoggingService _loggingService;
    
    public UserRegistration(EmailService emailService, LoggingService loggingService)
    {
        _emailService = emailService;
        _loggingService = loggingService;
    }
    
    public void RegisterUser(string email, string password)
    {
        // Only handle user registration
        var user = new User(email, password);
        SaveUserToDatabase(user);
        
        _emailService.SendWelcomeEmail(email);
        _loggingService.LogActivity("User registered: " + email);
    }
}

Benefits of SRP:

  • Improved maintainability as each class has a distinct responsibility.
  • Easier collaboration; team members can work on separate functionalities with minimal overlap.

Open/Closed Principle (OCP)

Definition: Software entities should be open for extension but closed for modification.

Implementation in C#:

Let’s assess a traditional approach that violates the OCP:


// Bad approach
public class AreaCalculator
{
    public double CalculateArea(object shape)
    {
        if (shape is Rectangle rectangle)
            return rectangle.Width * rectangle.Height;
        else if (shape is Circle circle)
            return Math.PI * circle.Radius * circle.Radius;
        
        throw new NotSupportedException("Shape not supported");
    }
}

By implementing the OCP, we can extend functionality without altering existing code:


// Better approach using OCP
public interface IShape
{
    double CalculateArea();
}

public class Rectangle : IShape
{
    public double Width { get; set; }
    public double Height { get; set; }
    
    public double CalculateArea()
    {
        return Width * Height;
    }
}

public class Circle : IShape
{
    public double Radius { get; set; }
    
    public double CalculateArea()
    {
        return Math.PI * Radius * Radius;
    }
}

// Now we can add new shapes without modifying existing code

Benefits of OCP:

  • Encourages the development of extensible software architectures.
  • Reduces the risk of introducing bugs to existing functionalities.

Liskov Substitution Principle (LSP)

Definition: Objects of a superclass should be replaceable with objects of its subclasses without affecting the correctness of the program.

Implementation in C#:

Let’s critique this implementation which violates LSP:


// Violation of LSP
public class Rectangle
{
    public virtual int Width { get; set; }
    public virtual int Height { get; set; }
    
    public virtual int GetArea()
    {
        return Width * Height;
    }
}

public class Square : Rectangle
{
    public override int Width 
    { 
        get { return base.Width; }
        set { 
            base.Width = value;
            base.Height = value; // This breaks LSP
        }
    }
}

To adhere to LSP, we separate shape behavior into correct implementations:


// Better approach adhering to LSP
public interface IShape
{
    int GetArea();
}

public class Rectangle : IShape
{
    public int Width { get; set; }
    public int Height { get; set; }
    
    public int GetArea()
    {
        return Width * Height;
    }
}

public class Square : IShape
{
    public int Side { get; set; }
    
    public int GetArea()
    {
        return Side * Side;
    }
}

Benefits of LSP:

  • Promotes a reliable hierarchy, ensuring placeholder objects work seamlessly in place of base class instances.

Interface Segregation Principle (ISP)

Definition: Clients should not be forced to depend on interfaces they do not use.

Implementation in C#:

This example showcases a common mistake by violating ISP:


// Violation of ISP
public interface IWorker
{
    void Work();
    void Eat();
    void Sleep();
}

// Better approach with segregated interfaces
public interface IWorkable
{
    void Work();
}

public interface IEatable
{
    void Eat();
}

public interface ISleepable
{
    void Sleep();
}

Benefits of ISP:

  • Reduces side effects and promotes clean design, enhancing modularity.
  • Developers work with specific interfaces relevant to their implementations.

Dependency Inversion Principle (DIP)

Definition: High-level modules should not depend on low-level modules; both should depend on abstractions.

Implementation in C#:

Examine this flawed approach under DIP:


// Violation of DIP
public class NotificationService
{
    private readonly EmailSender _emailSender;
    
    public NotificationService()
    {
        _emailSender = new EmailSender();
    }
    
    public void SendNotification(string message, string recipient)
    {
        _emailSender.SendEmail(message, recipient);
    }
}

Implementing DIP effectively allows for a more flexible design:


// Better approach using DIP
public interface IMessageSender
{
    void SendMessage(string message, string recipient);
}

public class EmailSender : IMessageSender
{
    public void SendMessage(string message, string recipient)
    {
        // Email sending logic
    }
}

public class SMSSender : IMessageSender
{
    public void SendMessage(string message, string recipient)
    {
        // SMS sending logic
    }
}

public class NotificationService
{
    private readonly IMessageSender _messageSender;
    
    public NotificationService(IMessageSender messageSender)
    {
        _messageSender = messageSender;
    }
    
    public void SendNotification(string message, string recipient)
    {
        _messageSender.SendMessage(message, recipient);
    }
}

Benefits of DIP:

  • Enhances the flexibility and reusability of code.
  • Facilitates easier testing and mocking through abstraction.

Conclusion

Incorporating the SOLID principles in C# development results in several benefits, such as improved maintainability, enhanced testability, increased flexibility, better code organization, and reduced technical debt. As applications grow in scale and complexity, consciously applying these principles will contribute to producing robust, maintainable, and adaptable software systems.

By prioritizing SOLID principles in your coding practices, you won’t just write C# code— you’ll create software that stands the test of time.

If you’re interested in exploring further implementation examples, feel free to connect with me on LinkedIn or check out my GitHub. Happy coding!

FAQ

What are the SOLID principles?

The SOLID principles are five design principles that help software developers create more maintainable and flexible systems.

How does SRP improve code quality?

SRP enhances code quality by ensuring that a class has only one reason to change, making it easier to manage and understand.

What advantages does OCP provide?

OCP allows developers to extend functionalities without changing existing code, reducing bugs and improving code safety.

Can LSP help avoid bugs?

Yes, adhering to LSP promotes a reliable class hierarchy and helps to avoid bugs that can arise from unexpected behavior in subclasses.

Why is Dependency Inversion important?

DIP is crucial for reducing coupling and enhancing flexibility, making it easier to change or replace components without affecting high-level modules.

Architecting Scalable OCPP Compliant EV Charging Platforms

Architecting Scalable OCPP Compliant EV Charging Platforms

  • Understanding OCPP: A pivotal standard for interoperability in charging networks.
  • Benefits: Highlights include hardware agnosticism, interoperability, and enhanced security.
  • Key Components: Focuses on backend design, CSMS, and certification compliance.
  • Real-World Examples: Showcases implementations by EV Connect and AMPECO.
  • Future Considerations: Emphasizes upgradeability, scalability, and evolving security needs.

Table of Contents

Understanding OCPP

The Open Charge Point Protocol (OCPP) serves as the communication backbone between EV chargers and Charging Station Management Systems (CSMS). By facilitating interoperability, OCPP allows network operators to seamlessly integrate different brands of charging stations into a unified ecosystem. As a widely embraced standard, OCPP is crucial in establishing cohesive charging networks without being constrained by vendor-specific technologies.

Currently, multiple versions of OCPP are in play:

  • OCPP 1.5: The initial version that introduced basic functionalities for communication between chargers and CSMS.
  • OCPP 1.6: A more robust version adding features like improved error handling and enhanced security protocols.
  • OCPP 2.0.1: The latest iteration emphasizing advanced security and additional capabilities, which offers certifications for core and advanced modules through the Open Charge Alliance (OCA).

With the impending rollout of more certification modules in March 2025, OCPP compliance is set to become an industry-standard requirement that platform architects must consider when designing scalable charging solutions.

Benefits of OCPP-Based Architecture

Hardware Agnosticism

One of the standout features of OCPP is its ability to enable hardware-agnostic charging platforms. Network operators can integrate any OCPP-compliant charger, independent of the manufacturer. For instance, AMPECO’s platform claims compatibility with over 70 leading charging station manufacturers, emphasizing OCPP’s flexibility and adaptability. This characteristic allows businesses to scale their operations without being locked into a specific vendor’s ecosystem, providing freedom for future growth and innovation.

Interoperability and Future-Proofing

Adopting OCPP standards is pivotal for ensuring that charging networks remain compatible across generations of equipment. By focusing on OCPP compliance, operators mitigate the risk of fragmented systems that could render investments obsolete when technology advances. This forward-thinking approach is essential for maintaining competitive advantages in a fast-evolving marketplace.

Security Enhancements

With OCPP 2.0.1, security is elevated to new heights. The implementation of advanced security modules helps safeguard charging networks against emerging threats. For example, EV Connect’s OCPP 2.0.1 certification signifies a commitment to robust security measures, ensuring that as charging infrastructures scale, they retain their integrity and protection against potential vulnerabilities.

Key Components for Scalable Architecture

Architecting a scalable, OCPP-compliant platform necessitates careful consideration of several key components:

Backend System Design

A robust backend design is crucial for supporting multiple OCPP versions concurrently. Given that charging networks often incorporate a mix of equipment operating on different protocol versions, the architecture must be flexible and capable of handling various communication standards. For instance, AMPECO’s platform supports a triad of versions: OCPP 1.5, 1.6, and 2.0.1, demonstrating the importance of backward compatibility in charging network design.

Charging Station Management System (CSMS)

The CSMS acts as the nerve center for the entire charging network, directing communication between connected charging stations and managing their operational status. This component must be designed for horizontal scalability, enabling additional charging points to be integrated seamlessly as demand grows.

Certification Compliance

Pursuing official OCPP certification through the OCA is vital for ensuring interoperability and long-term viability. A certified platform is not only a mark of quality; it also guarantees adherence to global standards, laying the foundation for seamless integration with certified charging hardware. This compliance is fundamental for engendering trust among network operators and users alike.

Real-World Implementation Examples

EV Connect’s OCPP 2.0.1 Implementation

In March 2025, EV Connect announced its achievement of OCPP 2.0.1 certification for both Core and Advanced Security modules. This milestone illustrates their dedication to open standards and the interoperability of their solutions. By leveraging OCPP compliance, EV Connect enhances user experiences through a reliable and efficient charging ecosystem, marking a significant step toward long-term stability and adaptability in the industry.

AMPECO’s Multi-Version Support

AMPECO’s EV Charging Platform stands out as a prime example of scalable architecture capable of supporting multiple OCPP versions simultaneously. Their hardware-agnostic approach allows them to integrate diverse manufacturers through OCPP compliance, proving the viability and flexibility of their solution. Such an adaptable architecture is essential for operators seeking to broaden their network without compromising on service quality.

Future Considerations

When designing scalable OCPP-compliant platforms, architects and engineers must contemplate several key future-oriented factors:

  • Future Upgradeability: Establish a framework that allows for seamless upgrades to future OCPP versions without requiring a complete overhaul.
  • Backward Compatibility: Ensure that newer systems can still interact with older OCPP implementations, preserving existing investments.
  • Scalability: Design systems that can efficiently handle thousands to millions of charging sessions, accommodating growth trajectories as EV adoption rises significantly.
  • Evolving Security Protocols: Regularly update security measures to keep pace with emerging threats and standards in the cybersecurity landscape.
  • Integration with Energy Management Systems: Explore the potential for integrating charging platforms with broader energy management infrastructures for optimized performance and resource utilization.

Summary

In conclusion, designing scalable OCPP-compliant EV charging platforms involves intricate knowledge of the OCPP standard and its implications for interoperability, security, and future-proofing. As the EV market continues its rapid expansion, architects must emphasize the importance of building robust, flexible, and certification-compliant systems that can support a diverse ecosystem of charging stations.

By leveraging OCPP standards, businesses can forge ahead in developing agile, adaptable charging infrastructures that are not only capable of handling present demands but are also well-prepared for future innovations in the electric vehicle landscape.

If you’d like to discuss innovative approaches to OCPP compliance or explore architectural strategies for your next project, connect with me on LinkedIn, or check out my GitHub for implementation examples!

FAQs

What is OCPP?

OCPP stands for Open Charge Point Protocol, which is a communication standard that allows for interoperability between electric vehicle chargers and management systems.

Why is security important in OCPP?

Security in OCPP is vital to protect charging networks from cyber threats and to ensure the integrity and reliability of EV charging systems.

How does hardware agnosticism benefit operators?

Hardware agnosticism allows operators to choose among various OCPP-compliant chargers without being locked into a specific manufacturer, enhancing efficiency and scalability.

What are the key features of OCPP 2.0.1?

Key features of OCPP 2.0.1 include enhanced security protocols, better error handling, and the ability to support a broader range of functionalities for charging stations.

Fixing “spawn npx ENOENT” in Windows 11 When Adding MCP Server with Node/NPX

If you’re running into the error:

spawn npx ENOENT

while configuring an MCP (Multi-Context Plugin) server on Windows 11, you’re not alone. This error commonly appears when integrating tools like @upstash/context7-mcp using Node.js environments that rely on NPX, especially in cross-platform development.

This post explains:

  • What causes the “spawn npx ENOENT” error on Windows
  • The difference between two MCP server configuration methods
  • A working fix using cmd /c
  • Why this issue is specific to Windows

The Problem: “spawn npx ENOENT”

Using this configuration in your .mcprc.json or a similar setup:

{
  "mcpServers": {
    "context7": {
      "command": "npx",
      "args": ["-y", "@upstash/context7-mcp@latest"]
    }
  }
}

will cause the following error on Windows:

spawn npx ENOENT

This indicates that Node.js tried to spawn npx but couldn’t locate it in the system’s PATH.

Root Cause: Windows vs Unix Shell Behavior

On UNIX-like systems (Mac/Linux), spawn can run shell commands like npx directly. But Windows behaves differently:

  • Windows expects a .exe file to be explicitly referenced when spawning a process.
  • npx is not a native binary executable; it requires a shell to interpret and run it.
  • Node’s child_process.spawn does not invoke a shell by default unless specifically instructed.

In the failing example, the system tries to invoke npx directly as if it were a standalone executable, which doesn’t work on Windows.

The Fix: Wrapping with cmd /c

This configuration solves the issue:

{
  "context7": {
    "command": "cmd",
    "args": [
      "/c",
      "npx",
      "-y",
      "@upstash/context7-mcp@latest"
    ]
  }
}

Explanation

  • "cmd" invokes the Windows Command Prompt.
  • "/c" tells the shell to execute the command that follows.
  • The rest of the line (npx -y @upstash/context7-mcp@latest) is interpreted and executed properly by the shell.

This ensures that npx is resolved correctly and executed within a compatible environment.

Technical Comparison

Configuration Style Works on Windows? Shell Used? Reason
"command": "npx" No No Tries to execute npx directly without shell
"command": "cmd", "args": ["/c", "npx", ...] Yes Yes Executes the command within the Windows shell, allowing proper resolution

Best Practices

When using Node.js-based CLI tools across platforms:

  • Wrap shell commands using cmd /c (Windows) or sh -c (Unix)
  • Avoid assuming that commands like npx are executable as binaries
  • Test your scripts in both Windows and Unix environments when possible

Conclusion

If you’re encountering the spawn npx ENOENT error when configuring MCP servers on Windows 11, the fix is straightforward: use cmd /c to ensure shell interpretation. This small change ensures compatibility and prevents runtime errors across different operating systems.

Powered by WordPress & Theme by Anders Norén