Introduction

In modern software development, the use of interfaces is crucial for achieving clean architecture and adhering to the principles of dependency injection. Often, you may find yourself in a scenario where multiple implementations of a single interface are needed. In .NET Core, registering multiple services with a single interface can be accomplished seamlessly using the built-in Dependency Injection (DI) container.

Why Register Multiple Services with a Single Interface?

Before diving into the implementation, let’s understand why you might need to register multiple services under a single interface:

  1. Separation of Concerns: Different implementations can handle different concerns while adhering to the same contract.
  2. Flexibility: Switching between different implementations at runtime based on configuration or other criteria.
  3. Testing: Easily mock or stub different implementations for unit testing.

Example Scenario

Let's consider a scenario where you have an interface INotificationService with multiple implementations such as EmailNotificationService, SMSNotificationService, and PushNotificationService.

1. Define the Interface

First, define the INotificationService interface:

public interface INotificationService
{
    void SendNotification(string message);
}

2. Implement the Interface

Next, create multiple implementations of the INotificationService interface:

public class EmailNotificationService : INotificationService
{
    public void SendNotification(string message)
    {
        Console.WriteLine($"Email: {message}");
    }
}

public class SMSNotificationService : INotificationService
{
    public void SendNotification(string message)
    {
        Console.WriteLine($"SMS: {message}");
    }
}

public class PushNotificationService : INotificationService
{
    public void SendNotification(string message)
    {
        Console.WriteLine($"Push: {message}");
    }
}

3. Register the Services in the DI Container

In the Startup.cs or Program.cs (for .NET 6 and later) file, register the services with the DI container:

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<INotificationService, EmailNotificationService>();
    services.AddTransient<INotificationService, SMSNotificationService>();
    services.AddTransient<INotificationService, PushNotificationService>();
}

4. Resolve the Services

To resolve all implementations of INotificationService, you can inject an IEnumerable<INotificationService>:

public class NotificationHandler
{
    private readonly IEnumerable<INotificationService> _notificationServices;

    public NotificationHandler(IEnumerable<INotificationService> notificationServices)
    {
        _notificationServices = notificationServices;
    }

    public void NotifyAll(string message)
    {
        foreach (var service in _notificationServices)
        {
            service.SendNotification(message);
        }
    }
}

5. Use the NotificationHandler

Inject the NotificationHandler wherever needed:

public class SomeController : ControllerBase
{
    private readonly NotificationHandler _notificationHandler;

    public SomeController(NotificationHandler notificationHandler)
    {
        _notificationHandler = notificationHandler;
    }

    public IActionResult SendNotifications(string message)
    {
        _notificationHandler.NotifyAll(message);
        return Ok();
    }
}

Additional Tips

Conclusion

Registering multiple services with a single interface in .NET Core is straightforward and enhances the flexibility and testability of your application. By leveraging the built-in DI container, you can easily manage different implementations and switch between them as needed. This approach promotes a clean, maintainable, and scalable architecture in your .NET Core applications.