Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I have services that are derived from the same interface.

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

Typically, other IoC containers like Unity allow you to register concrete implementations by some Key that distinguishes them.

In ASP.NET Core, how do I register these services and resolve them at runtime based on some key?

I don't see any Add Service methods that take a key or name parameter, which would typically be used to distinguish 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 the Factory pattern the only option here?

Update1
I have gone though the article here that shows how to use the factory pattern to get service instances when we have multiple concrete implementations. However, it is still not a complete solution. When I call the _serviceProvider.GetService() method, I cannot inject data into the constructor.

For example consider this:

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 can _serviceProvider.GetService() inject the appropriate connection string? In Unity, or any other IoC library, we can do that at type registration. I can use IOption, however, that will require me to inject all settings. I cannot inject a particular connection string into the service.

Also note that I am trying to avoid using other containers (including Unity) because then I have to register everything else (e.g., Controllers) with the new container as well.

Also, using the factory pattern to create service instances is against DIP, as it increases the number of dependencies a client has details here.

So, I think the default DI in ASP.NET Core is missing two things:

  1. The ability to register instances using a key
  2. The ability to inject static data into constructors during registration
like image 559
LP13 Avatar asked Aug 26 '16 21:08

LP13


People also ask

Can an interface have multiple implementations?

Java does not support "multiple inheritance" (a class can only inherit from one superclass). However, it can be achieved with interfaces, because the class can implement multiple interfaces. Note: To implement multiple interfaces, separate them with a comma (see example below).

CAN interface have multiple implementations C#?

Multiple implementations with generic Language limitation, but probably in the future, C# allows you to do it. This interface has three implementations, Cat, Dog, and Human class. To register, add this code.

What is difference between AddTransient and AddScoped?

AddTransient() - This method creates a Transient service. A new instance of a Transient service is created each time it is requested. AddScoped() - This method creates a Scoped service. A new instance of a Scoped service is created once per request within the scope.

What is dependency injection in .NET core?

ASP.NET Core supports the dependency injection (DI) software design pattern, which is a technique for achieving Inversion of Control (IoC) between classes and their dependencies. For more information specific to dependency injection within MVC controllers, see Dependency injection into controllers in ASP.NET Core.


Video Answer


1 Answers

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.

like image 69
Miguel A. Arilla Avatar answered Oct 11 '22 16:10

Miguel A. Arilla