How to register multiple implementations of the same interface in Asp.Net Core?

Solution 1:

I did a simple workaround using Func when I found myself in this situation.

Firstly declare a shared delegate:

public delegate IService ServiceResolver(string key);

Then in your Startup.cs, setup the multiple concrete registrations and a manual mapping of those types:

services.AddTransient<ServiceA>();
services.AddTransient<ServiceB>();
services.AddTransient<ServiceC>();

services.AddTransient<ServiceResolver>(serviceProvider => key =>
{
    switch (key)
    {
        case "A":
            return serviceProvider.GetService<ServiceA>();
        case "B":
            return serviceProvider.GetService<ServiceB>();
        case "C":
            return serviceProvider.GetService<ServiceC>();
        default:
            throw new KeyNotFoundException(); // or maybe return null, up to you
    }
});

And use it from any class registered with DI:

public class Consumer
{
    private readonly IService _aService;

    public Consumer(ServiceResolver serviceAccessor)
    {
        _aService = serviceAccessor("A");
    }

    public void UseServiceA()
    {
        _aService.DoTheThing();
    }
}

Keep in mind that in this example the key for resolution is a string, for the sake of simplicity and because OP was asking for this case in particular.

But you could use any custom resolution type as key, as you do not usually want a huge n-case switch rotting your code. Depends on how your app scales.

Solution 2:

Another option is to use the extension method GetServices from Microsoft.Extensions.DependencyInjection.

Register your services as:

services.AddSingleton<IService, ServiceA>();
services.AddSingleton<IService, ServiceB>();
services.AddSingleton<IService, ServiceC>();

Then resolve with a little of Linq:

var services = serviceProvider.GetServices<IService>();
var serviceB = services.First(o => o.GetType() == typeof(ServiceB));

or

var serviceZ = services.First(o => o.Name.Equals("Z"));

(assuming that IService has a string property called "Name")

Make sure to have using Microsoft.Extensions.DependencyInjection;

Update

AspNet 2.1 source: GetServices

Solution 3:

A factory approach is certainly viable. Another approach is to use inheritance to create individual interfaces that inherit from IService, implement the inherited interfaces in your IService implementations, and register the inherited interfaces rather than the base. Whether adding an inheritance hierarchy or factories is the "right" pattern all depends on who you speak to. I often have to use this pattern when dealing with multiple database providers in the same application that uses a generic, such as IRepository<T>, as the foundation for data access.

Example interfaces and implementations:

public interface IService 
{
}

public interface IServiceA: IService
{}

public interface IServiceB: IService
{}

public interface IServiceC: IService
{}

public class ServiceA: IServiceA 
{}

public class ServiceB: IServiceB
{}

public class ServiceC: IServiceC
{}

Container:

container.Register<IServiceA, ServiceA>();
container.Register<IServiceB, ServiceB>();
container.Register<IServiceC, ServiceC>();

Solution 4:

I just simply inject an IEnumerable

ConfigureServices in Startup.cs

Assembly.GetEntryAssembly().GetTypesAssignableFrom<IService>().ForEach((t)=>
                {
                    services.AddScoped(typeof(IService), t);
                });

Services Folder

public interface IService
{
    string Name { get; set; }
}

public class ServiceA : IService
{
    public string Name { get { return "A"; } }
}

public class ServiceB : IService
{    
    public string Name { get { return "B"; } }
}

public class ServiceC : IService
{    
    public string Name { get { return "C"; } }
}

MyController.cs

public class MyController
{
    private readonly IEnumerable<IService> _services;
    public MyController(IEnumerable<IService> services)
    {
        _services = services;
    }
    public void DoSomething()
    {
        var service = _services.Where(s => s.Name == "A").Single();
    }
...
}

Extensions.cs

    public static List<Type> GetTypesAssignableFrom<T>(this Assembly assembly)
    {
        return assembly.GetTypesAssignableFrom(typeof(T));
    }
    public static List<Type> GetTypesAssignableFrom(this Assembly assembly, Type compareType)
    {
        List<Type> ret = new List<Type>();
        foreach (var type in assembly.DefinedTypes)
        {
            if (compareType.IsAssignableFrom(type) && compareType != type)
            {
                ret.Add(type);
            }
        }
        return ret;
    }

Solution 5:

Bit late to this party, but here is my solution:...

Startup.cs or Program.cs if Generic Handler...

services.AddTransient<IMyInterface<CustomerSavedConsumer>, CustomerSavedConsumer>();
services.AddTransient<IMyInterface<ManagerSavedConsumer>, ManagerSavedConsumer>();

IMyInterface of T Interface Setup

public interface IMyInterface<T> where T : class, IMyInterface<T>
{
    Task Consume();
}

Concrete implementations of IMyInterface of T

public class CustomerSavedConsumer: IMyInterface<CustomerSavedConsumer>
{
    public async Task Consume();
}

public class ManagerSavedConsumer: IMyInterface<ManagerSavedConsumer>
{
    public async Task Consume();
}

Accessing the services in a controller

public class MyController
{
    private readonly IMyInterface<CustomerSavedConsumer> _customerSavedConsumer;
    private readonly IMyInterface<ManagerSavedConsumer> _managerSavedConsumer;

    public MyController(IMyInterface<CustomerSavedConsumer> customerSavedConsumer, IMyInterface<ManagerSavedConsumer> managerSavedConsumer)
    {
        _customerSavedConsumer = customerSavedConsumer;
        _managerSavedConsumer = managerSavedConsumer;
    }
}

Hopefully if there is any issue with doing it this way, someone will kindly point out why this is the wrong way to do this.