Introduction
There can be situations where you need to register multiple implementations of the same dependency for your applications. For example, if you want to add features to your application without changing the existing code, a good way to do it is to add a new implementation for that. Your application needs to be written in a certain way to do this correctly. We have a really simple example that demonstrates this in this article. But when you eventually need to do this, ASP.Net Core built-in Dependency Injection providers capabilities to achieve this. In this article, we will look at how to do this with an example.
Registering Multiple Implementations of a Dependency
As an example, let’s take an online e-commerce website. This site gives Discounts to the customers on certain occasions and when certain conditions are met. I have an interface called IDiscount where it defines a discount and the logic to calculate it. For each type of discount, you want to provide in your e-commerce application, you can have an implementation of the IDiscount interface. Then I have an interface called IDiscountProcessor where the implementation of this interface handles calling all the implementations of IDiscount to calculate the final discount for a given Order.
You have a couple of ways to register multiple implementations of a dependency in ASP.Net Core. One way is to just use the provided extension methods on IServiceCollection to register your implementations with the desired lifetime.
- services.AddScoped<IDiscountProcessor, OrderDiscountProcessor>();
- services.AddScoped<IDiscount, SeasonalDiscount>();
- services.AddScoped<IDiscount, LargeOrderDiscount>();
- services.AddScoped<IDiscount, ThreeOrModeDiscount>();
Here, I have registered the OrderDiscountProcessor implementation of IDiscountProcessor and 3 implementations of IDiscount interface.
This will work fine when we eventually resolve all the implementations of IDiscount interface we can calculate the total discount. But the problem comes when you have multiple registrations of the same implementation. For example, let’s say one of the developers accidentally registered SeasonalDiscount implementation twice. What will happen is that the Seasonal discount will be applied twice for all the orders, costing the organization money.
A better way of registering multiple implementations is to use TryAddEnumerable extension method given in the Microsoft.Extensions.DependencyInjection.Extensions namespace. This will not register any duplicate implementations making multiple implementation registration safe. The registration is a bit different where you need to use ServiceDescriptors to register the dependency. The modified implementation looks like this.
- services.AddScoped<IDiscountProcessor, OrderDiscountProcessor>();
- services.TryAddEnumerable(new[]
- {
- ServiceDescriptor.Scoped<IDiscount, SeasonalDiscount>(),
- ServiceDescriptor.Scoped<IDiscount, LargeOrderDiscount>(),
- ServiceDescriptor.Scoped<IDiscount, ThreeOrModeDiscount>()
- });
Injecting and Using Multiple Implementations of a Dependency
Once your implementations are registered with the Dependency Injection container, they are ready to be used. To inject all the registered implementations, you need to use IEnumerable<> of the implementation type in your constructor. So, your constructor would look something like this. This is our OrderDiscountProcessor implementation.
- public class OrderDiscountProcessor : IDiscountProcessor
- {
- private readonly IEnumerable<IDiscount> _discounts;
-
- public OrderDiscountProcessor(IEnumerable<IDiscount> discounts)
- {
- _discounts = discounts;
- }
-
- }
Here I am injecting IEnumerable<IDiscount> where it injects all the registered implementations to my class. Then in my ProcessDiscount() method I can use the implementations like this.
- public (double, List<string>) ProcessDiscount(OrderViewModel order)
- {
- var discountDiscroptoons = new List<string>();
- var totalDiscount = 0.0;
-
- foreach (var discount in _discounts)
- {
- var addedDiscount = discount.CalculateDiscount(order);
- if (addedDiscount > 0)
- {
- discountDiscroptoons.Add(discount.Description);
- }
- totalDiscount += addedDiscount;
- }
-
- return (totalDiscount, discountDiscroptoons);
- }
I can now iterate through all the implementations of IDiscount interface and call its CalculateDiscount() method to calculate the discount for the given Order.
Note that you can only use IEnumerable<> for your injections of multiple implementations. Any other type like IList<>, ICollection<> will not work in this case.
Summary
In this article, we looked into the process of how to register multiple implementations of the same dependency in ASP.NET Core and how to use these dependencies in our classes by injecting them. The simple sample application used to demonstrate this usage is available for download with this article or you can find the source code on GitHub under the following repository