Coder Perfect

In Asp.Net Core, how do you register several implementations of the same interface?

Problem

I have a set of services that are all based on the same interface.

public interface IService { }
public class ServiceA : IService { }
public class ServiceB : IService { } 
public class ServiceC : IService { }

Other IoC containers, such as Unity, typically allow you to register concrete implementations with a unique Key.

How can I register these services in ASP.NET Core and resolve them at runtime based on a key?

There are no Add Service methods that take a key or name parameter, which would normally be used to differentiate the concrete implementation.

    public void ConfigureServices(IServiceCollection services)
    {            
         // How do I register services of the same interface?            
    }


    public MyController:Controller
    {
       public void DoSomething(string key)
       { 
          // How do I resolve the service by key?
       }
    }

Is it true that the Factory pattern is the only one available?

Update1 I read this article, which explains how to use the factory approach to obtain service instances when there are several concrete implementations. It is, however, not a comprehensive solution. I’m unable to inject data into the constructor when using the _serviceProvider.GetService() method.

Consider the following scenario:

public class ServiceA : IService
{
     private string _efConnectionString;
     ServiceA(string efconnectionString)
     {
       _efConnecttionString = efConnectionString;
     } 
}

public class ServiceB : IService
{    
   private string _mongoConnectionString;
   public ServiceB(string mongoConnectionString)
   {
      _mongoConnectionString = mongoConnectionString;
   }
}

public class ServiceC : IService
{    
    private string _someOtherConnectionString
    public ServiceC(string someOtherConnectionString)
    {
      _someOtherConnectionString = someOtherConnectionString;
    }
}

How does _serviceProvider.GetService() know what connection string to use? We can accomplish this at type registration in Unity or any other IoC library. I can use IOption, but that will necessitate injecting all settings. I’m unable to use a specific connection string in the service.

Also, I’m trying to avoid utilizing other containers (including Unity) because that would need me to register everything else (for example, controllers) with the new container.

Using the factory approach to build service instances is also against DIP because it adds to the number of dependencies a client has.

As a result, I believe the default DI in ASP.NET Core is lacking in two areas:

Asked by LP13

Solution #1

When I was in this scenario, I used Func to create a simple solution.

Declare a shared delegate first:

public delegate IService ServiceResolver(string key);

Then create various concrete registrations and a manual mapping of those types in Startup.cs:

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 you may use it from any DI-registered class:

public class Consumer
{
    private readonly IService _aService;

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

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

Keep in mind that the key for resolution in this example is a string for simplicity’s sake and because the OP specifically requested this situation.

However, because you don’t normally want a massive n-case switch spoiling your code, you might use any custom resolution type as key. It is dependent on how well your app scales.

Answered by Miguel A. Arilla

Solution #2

Another alternative is to utilize Microsoft’s GetServices extension function. Extensions.DependencyInjection.

Assign your services to the following:

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

After that, resolve with a little 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 “Name” string attribute)

Make certain that Microsoft.Extensions is installed. DependencyInjection;

GetServices is an AspNet 2.1 source.

Answered by rnrneverdies

Solution #3

A factory approach is undoubtedly feasible. Another option is to utilize inheritance to construct unique interfaces that inherit from IService, then implement the inherited interfaces in your IService implementations and register the inherited interfaces instead of the base interfaces. It depends on who you ask whether adding an inheritance hierarchy or factories is the “correct” pattern. When dealing with numerous database providers in the same application that uses a generic, such as IRepositoryT>, as the foundation for data access, I frequently have to use this technique.

The following are some examples of 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>();

Answered by jlc397

Solution #4

I simply inject an IEnumerable into the code.

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;
    }

Answered by T Brown

Solution #5

I’m a little late to the party, but here’s what I came up with:…

If you’re using a Generic Handler, use Startup.cs or Program.cs…

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

T Interface Setup IMyInterface

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

T’s IMyInterface implementations in practice

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

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

Getting to a controller’s services

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;
    }
}

If there is a problem with doing things this way, hopefully someone will show out why it is the wrong way to do it.

Answered by Gray

Post is based on https://stackoverflow.com/questions/39174989/how-to-register-multiple-implementations-of-the-same-interface-in-asp-net-core