Learn ASP.NET Core 3.1 with mini project : MVC, Razor Pages, Web API, Entity Framework Core, and Blazor. Hands-on online training courses. Click here to know more.

Multiple implementations of an interface in ASP.NET Core DI container

If you worked with ASP.NET Core before chances are you used ASP.NET Core's dependency injection features. Most commonly you register a single implementation of an interface as a service type. However, at times you may want to register multiple implementations of an interface as service types. In this article you learn how to accomplish the task.

Let's first understand what we are trying to accomplish.

Suppose you have an interface as shown below:

public interface IHelloService { string SayHello(); }

The IHelloService interface contains just a single method named SayHello().

Let's further assume that you have three implementations of IHelloService with you as outlined below:

public class HelloA:IHelloService { public string SayHello() { return "Hello World!"; } } public class HelloB : IHelloService { public string SayHello() { return "Hello Galaxy!"; } } public class HelloC : IHelloService { public string SayHello() { return "Hello Universe!"; } }

The three implementations namely HelloA, HelloB, and HelloC simply return three different strings to the caller - Hello World!, Hello Galaxy!, and Hello Universe!.

How do you register these three concrete types with the ASP.NET Core's DI container?

One of the ways is this :

services.AddScoped<HelloA>(); services.AddScoped<HelloB>(); services.AddScoped<HelloC>();

In your ConfigureServices() you add the above code and register the concrete types HelloA, HelloB, and HelloC with the DI container. Although all of them implement IHelloService, while registering them they are registered as independent concrete types.

To grab one or more implementations of IHelloService you can use constructor injection like this :

public IndexModel(HelloA hello1, HelloB hello2, HelloC hello3) { string msg1 = hello1.SayHello(); string msg2 = hello2.SayHello(); string msg3 = hello3.SayHello(); }

The above code shows a constructor of Index razor page. The constructor has three parameters - one for each concrete type. In this case the DI container will supply the respective types as expected and the three string variables will hold - Hello World!, Hello Galaxy!, and Hello Universe! respectively.

So far so good.

Now, change the way you registered the services in the ConfigureServices().

services.AddScoped<IHelloService, HelloA>(); services.AddScoped<IHelloService, HelloB>(); services.AddScoped<IHelloService, HelloC>();

Note that AddScoped() now registers multiple implementations of IHelloService. To accommodate this change you also modify the constructor as shown below:

public IndexModel(IHelloService hello1, IHelloService hello2, IHelloService hello3) { string msg1 = hello1.SayHello(); string msg2 = hello2.SayHello(); string msg3 = hello3.SayHello(); }

What's the outcome?

You will find that all the string variables contain Hello Universe! This is because DI container injects only HelloC objects in the constructor since it's the last implementation of IHelloService registered in the ConfigureServices().

To inject all the implementations of IHelloServices interface you need to modify the constructor like this:

public IndexModel(IEnumerable<IHelloService> hello) { foreach(var obj in hello) { string msg = obj.SayHello(); } }

As you can see, the constructor now takes an IEnumerable<IHelloService>. This way all the registered implementations of IHelloService are injected into the constructor. Inside, you can loop through the supplied implementations and invoke SayHello() on each. This time you will find that SayHello() correctly returns Hello World!, Hello Galaxy!, and Hello Universe! during the corresponding iterations. You can also check the type of obj to decide whether to use that implementation of IHelloService or not.

That's it for now! Keep coding!!