Registering multiple implementations of the same interface in ASP.NET Core

Multiple implementations of an interface in .NET Core Dependency Injection

Starting from it's initial release ASP .NET Core comes with built in dependency injection (DI) to help implement Inversion Of Control design pattern. This is not something new in .NET and apart from built-in DI in .NET Core, there are other libraries which provide same and in some cases even more advanced dependency injection implementation. Some of these libraries like AutoFac are available for both .NET Framework and .NET Core.

Built-in .NET Core dependency injection container is definitely not the replacement for highly advanced AutoFac but in most cases you will find it suitable for most of the requirements. I find it suitable for most of the cases where I use DI in .NET Core.

Apart from every day tasks where in most of the cases you just need to inject specific interface implementations in one way or another, there are those moments where you hit the dead end with ASP.NET Core DI container. One of those cases is when you need to register multiple implementations of the same interface. Not as bad as it seems, there are several ways to overcome this limitation and here are some of them which may or may not work for your specific case, but definitely some ideas how to solve your problem.

So the initial idea is to have once interface and multiple implementations of it and manage to inject more then one of them in DI container. In code, this should look something like this

    public interface IService
    {
    }

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

Ideally we would setup the DI container in startup like the following

public class Startup
{
....
// This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddScoped<IService, ServiceA>();
            services.AddScoped<IService, ServiceB>();
            services.AddScoped<IService, ServiceC>();

            services.AddMvc();
        }
		....
    }
    

Now, ideally we would expect to access interface implementation instances in a controller constructor

    [Route("api/[controller]")]
    [ApiController]
    public class ValuesController : ControllerBase
    {
        public ValuesController(IService serviceA, IService serviceB, IService serviceC)
        {

        }

}

    

Unfortunately this is not how it will work. If you try to run this code and put the breakpoint on the constructor, you will see that all three parameters are actually instances of ServiceC class as it is the lat one injected and interface IService will be resolved to the last one set which is ServiceC class.

Di Multiple Impl

Let's see what are some of the options to to resolve this problem and be able to access the proper interface implementation class instance.

Using generic type for each interface implementation

This approach is useful in cases where you need to produce or handle different types by the same interface structure. One of real -life scenarios for this is handling message queues for example and implementing Publish-subscribe or Event Message Bus patterns.

We'll have to update our service to use generic type which will reflect to code changes in implementation classes as well

    public interface IService<T>
    {
    }

    public class ServiceA : IService<int>
    {
    }
    public class ServiceB : IService<String>
    {
    }
    public class ServiceC : IService<Boolean>
    {
    }
    

Now in dependency injection container setup, we can add implementation for every specific generic type we are going to use.

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddScoped<IService<int>, ServiceA>();
            services.AddScoped<IService<String>, ServiceB>();
            services.AddScoped<IService<Boolean>, ServiceC>();

            services.AddMvc();
        }
    

Ideally you would use a POCO class as a generic type, for example when reading from the message queue, but for the simplicity I used int, String and Boolean types. Now you can have multiple controllers to handle multiple types. Let's say you have three message queues and you need an enpoint to push the messages to a different queue. 

    [Route("api/[controller]")]
    [ApiController]
    public class ValuesController : ControllerBase
    {
        public ValuesController(IService<int> service)
        {

        }
}
    

Di Multiple Generic Type

From the debug you can see that when we referenced IService<int> we got the actual implementation class for this generic type of the IService interface which is class ServiceA. 

Using a delegate to select specific interface implementation

This method uses DI to register a Func which return and interface implementation depending on a specific key. The key can be a simple type, but because you need to access a limited scope of values, I prefer to use Enum as a key for the registered interface implementations. So let's start with it by declaring this enumeration. To keep things simple, I'll just use the same implementation class name with capital letter suffix at the end and for enum, I'll use those capital letter suffixes.

    public enum ServiceEnum
    {
        A,
        B,
        C
    }
    

Now we need to use Func<ServiceEnum,IService> which will act as a factory for the IService different implementations.

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddScoped<ServiceA>();
            services.AddScoped<ServiceB>();
            services.AddScoped<ServiceC>();

            services.AddTransient<Func<ServiceEnum, IService>>(serviceProvider => key =>
            {
                switch (key)
                {
                    case ServiceEnum.A:
                        return serviceProvider.GetService<ServiceA>();
                    case ServiceEnum.B:
                        return serviceProvider.GetService<ServiceB>();
                    case ServiceEnum.C:
                        return serviceProvider.GetService<ServiceC>();
                    default:
                        return null; 
                }
            });

            services.AddMvc();
        }
    

With this kid of registering interface implementations in DI container in .NET Core we cannot access implementation class directly. Instead, we need to reference our Func as a parameter in a constructor and us it to resolve the interface implementation.

    [Route("api/[controller]")]
    [ApiController]
    public class ValuesController : ControllerBase
    {
        public ValuesController(Func<ServiceEnum, IService> serviceResolver)
        {
            var service = serviceResolver(ServiceEnum.A);
        }
	}
	
    

Now when we have this checked in a debug, we'll see that IService interface i properly resolved to ServiceA class instance type

Di Multiple Func

Register interface implementations as an IEnumerable collection

Now we are back to ConfigureServices method from the beginning of this article where for the same interface we register multiple class types. As much as it looked wrong in the first place, .NET Core dependency injection container can handle this king of interface implementation registration. 

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddScoped<IService, ServiceA>();
            services.AddScoped<IService, ServiceB>();
            services.AddScoped<IService, ServiceC>();

            services.AddMvc();
        }
    

From the initial case, where we tried to access injected type instances we were getting always the last registered implementation class. The thing is we were trying to access injected interface implementations in a wrong way. If we reference interface implementations as an IEnumerable<IService> we will have all three implementation class instances available in a collection. 

    [Route("api/[controller]")]
    [ApiController]
    public class ValuesController : ControllerBase
    {
        public ValuesController(IEnumerable<IService> services)
        {

        }
	}
    

 Di Enumerable 

This might be useful if you need your interface implementations used in a Chain of responsibility like pattern. IEnumerable<IServices> can be your custom pipeline for processing values in your controller. I can't remember more cases where this is used because you do not actually have access to particular implementations, rather you have all of them in a list.

The good thing is that this comes out of the box and no additional coding is needed. 

References

Disclaimer

Purpose of the code contained in snippets or available for download in this article is solely for learning and demo purposes. Author will not be held responsible for any failure or damages caused due to any other usage.


About the author

DEJAN STOJANOVIC

Dejan is a passionate Software Architect/Developer. He is highly experienced in .NET programming platform including ASP.NET MVC and WebApi. He likes working on new technologies and exciting challenging projects

CONNECT WITH DEJAN  Loginlinkedin Logintwitter Logingoogleplus Logingoogleplus

JavaScript

read more

SQL/T-SQL

read more

Umbraco CMS

read more

PowerShell

read more

Comments for this article