Using Implementation Factories To Register Dependencies In ASP.NET Core Dependency Injection

Introduction

Automatic dependency creation is always preferred in ASP.NET Core Dependency Injection but when you work with legacy code or other libraries, you may not always have that option. Implementation Factories in ASP.NET Core Dependency Injection offer more control over how the dependency is created. For example, when you use Builders and Factories to create objects of the dependencies, you need to use some other option than automatic dependency creation. Let’s look at how we can use Implementation Factories in ASP.NET Core Dependency Injection.

Using Implementation Factories to register dependency

For this example, let’s take the same e-commerce website example we took for the previous article on Registering and using multiple implementations. I have done some minor back-end changes to the services I have registered. I have an OrderDiscountProcessor that is responsible for processing the discount for the order. Then, I have a DiscountBuilder that is responsible for building a Discount object that describes the discount amount and the conditions for that discount to be applicable. The OrderDiscountProcessor is dependent on the Discount object to be injected in order to calculate the discount for the order. Simple enough, right?

The problem we have in this example is to registering Discount class as a dependency for IDiscountProcessor. The Discount object needs to be constructed using the extension methods provided by the DiscountBuilder. So, in this scenario, to register Discount as a dependency, we need to use Implementation Factories.

The methods that are available in the IServiceCollection, that are used to register dependencies allow you to define a delegate that is responsible for constructing the instance of the dependency we want to register. Not only methods like AddScoped(), AddSingleton() extension methods in IServiceCollection, but methods like TryAddScoped() and even if you are using ServiceDescriptor class directly with Static methods or helper methods, they all support the use of delegate for dependency construction.

In this example, I have used AddScoped() method to register the Discount dependency.

  1. services.AddScoped<IDiscountProcessor, OrderDiscountProcessor>();  
  2. services.AddScoped<Discount>(sp =>  
  3. {  
  4.     return new DiscountBuilder()  
  5.         .WithMinimumBillAmount(1000)  
  6.         .WithMinimumItemCount(3)  
  7.         .WithPercentage(10)  
  8.         .Build();  
  9. });  

Now, I have the dependency registered. In my OrderDiscountProcessor implementation, I can depend on the Discount object injected in, to calculate the discount for a given order.

  1. public class OrderDiscountProcessor : IDiscountProcessor  
  2. {  
  3.     private readonly Discount _discount;  
  4.   
  5.     public OrderDiscountProcessor(Discount discount)  
  6.     {  
  7.         _discount = discount;  
  8.     }  
  9.   
  10.     public double ProcessDiscount(OrderViewModel order)  
  11.     {  
  12.         double discount = 0.0;  
  13.         double totalBill = order.Quantity * order.Price;  
  14.   
  15.         if (order.Quantity >= _discount.MinimumItemCount && totalBill >= _discount.MinimumBillAmount)  
  16.         {  
  17.             discount = (totalBill * _discount.Percentage) / 100.0;  
  18.         }  
  19.   
  20.         return discount;  
  21.     }  
  22. }  

Summary

In this short article, we looked into how to use Implementation Factories when you need more control over how the dependencies are instantiated; especially when you have no control over the dependency you are relying upon. This is useful when you work with legacy code or third-party libraries that are less flexible. 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