Dependency Injection In ASP.NET Core

This article explains the dependency injection feature available out of the box in ASP.NET core (ASP.NET 5). It will also cover some of the basics of dependency injection so that everyone can get the most out this article.

What is dependency Injection

Dependency Injection is the process of “injecting” the “dependency” whenever the dependency is requested by a client, where the dependency may be another service which the client (requester) may have no means of knowing how to create.

As an analogy, imagine a person (client) going to office carrying his lunch cooked by himself. In this scenario, the person has a “dependency” on food. But he had to know how to cook his food. But honestly, not everyone (client) knows to cook, but people do need food (dependency). This is where restaurants play the role of dependency Injection. They can supply food (“inject dependency”) to the people (client) without the person (client) needing to know how to cook.

This is also called as Inversion of control. Since the control for creation of the service has been passed from the requester to another entity which takes care of creating the dependencies needed by a class to perform its tasks. The entity which takes care of creating these requested dependencies and injecting them automatically to the client is known as the DI (dependency Injection) container.

Dependency Injection in ASP.NET Core

Ok, enough said about what dependency injection is. I am sure most of you knew it. If not, this is a great feature to know and a great tool to add to your tool belt! Let us now look at how to implement dependency injection out of the box in ASP.NET core.

ASP.NET core applications can leverage built in framework support for implementing dependency injection. It has support for the following types of lifetimes for configured services (injected dependencies).

  1. Transient – A new instance of the service is created each time it is requested. It can be used for stateless and light weight services.
  2. Scoped – A single instance is created once per request.
  3. Singleton – Created only once the first time they are requested.

Now for some code!

Use case

Implement a UniqueKeyProvider service. For demonstrating the lifetimes, we will create three differently named interfaces (one for each type of lifetime) for UniqueKeyprovider, all inheriting from the same parent interface having a single property, UniqueKey.

Also, to further illustrate the scoped and singleton lifetime, we will create another service, SomeService, which will have a dependency on all the three different UniqueKeyProvider services (the three different lifetime services).

1. Service Interface definition

Please note the three differently named interfaces, one for demonstrating each type of lifetime.

  1. namespace DependencyInjectionASPNetCore.Services  
  2. {  
  3.     public interface IUniqueKeyProvider  
  4.     {  
  5.         Guid UniqueKey  
  6.         {  
  7.             get;  
  8.             set;  
  9.         }  
  10.     }  
  11.     public interface ITransientUniqueKeyProvider: IUniqueKeyProvider  
  12.     {}  
  13.     public interface IScopedUniqueKeyProvider: IUniqueKeyProvider  
  14.     {}  
  15.     public interface ISingletonUniqueKeyProvider: IUniqueKeyProvider  
  16.     {}  
  17. }  
2. Service Class definition
  1. namespace DependencyInjectionASPNetCore.Services  
  2. {  
  3.     public class UniqueKeyProvider: ITransientUniqueKeyProvider,  
  4.     ISingletonUniqueKeyProvider,  
  5.     IScopedUniqueKeyProvider  
  6.     {  
  7.         public Guid UniqueKey  
  8.         {  
  9.             get;  
  10.             set;  
  11.         }  
  12.         public UniqueKeyProvider(Guid uniqueKey)  
  13.         {  
  14.             UniqueKey = uniqueKey;  
  15.         }  
  16.     }  
  17. }  
3. Registering the services in the startup.cs

Now register your services in the startup.cs file. This is the step where you are actually configuring your dependency injection container. You are basically instructing your dependency injection container that if the user requests for the service, ITransientUniqueKeyProvider, then please provide with a transient scoped instance of UniqueKeyProviderclass and similarly for the other services.

You need to register your services in the ConfigureServices method in the startup.cs file. Please refer to the code below for registration,
  1. services.AddTransient<ITransientUniqueKeyProvider>(provider =>newUniqueKeyProvider(Guid.NewGuid()));  
  2. services.AddScoped<IScopedUniqueKeyProvider>(provider =>newUniqueKeyProvider(Guid.NewGuid()));  
  3. services.AddSingleton<ISingletonUniqueKeyProvider>(provider =>newUniqueKeyProvider(Guid.NewGuid()));  
  4. services.AddTransient<ISomeService, SomeService>();  
Note that I am also registering another service, ISomeServicewhich I am just using for demo purposes for showing the nature of transient and singleton lifetime. Also note that SomeService implementation requires dependency on the three Unique Key providers, which will be injected through dependency injection.Implementation of ISomeservice provided below,

4. IsomeService and Someservice Implementation
  1. public class ISomeService  
  2. {  
  3.     ITransientUniqueKeyProvider TransientUniquekeyProvider  
  4.     {  
  5.         get;  
  6.         set;  
  7.     }  
  8.     IScopedUniqueKeyProvider ScopedUniquekeyProvider  
  9.     {  
  10.         get;  
  11.         set;  
  12.     }  
  13.     ISingletonUniqueKeyProvider SingletonUniquekeyProvider  
  14.     {  
  15.         get;  
  16.         set;  
  17.     }  
  18. }  
  19. public class SomeService: ISomeService  
  20. {  
  21.     public ITransientUniqueKeyProvider TransientUniquekeyProvider  
  22.     {  
  23.         get;  
  24.         set;  
  25.     }  
  26.     public IScopedUniqueKeyProvider ScopedUniquekeyProvider  
  27.     {  
  28.         get;  
  29.         set;  
  30.     }  
  31.     public ISingletonUniqueKeyProvider SingletonUniquekeyProvider  
  32.     {  
  33.         get;  
  34.         set;  
  35.     }  
  36.     public SomeService(ITransientUniqueKeyProvider transientprovider,  
  37.         IScopedUniqueKeyProvider scopedprovider,  
  38.         ISingletonUniqueKeyProvider singletonprovider)  
  39.     {  
  40.         TransientUniquekeyProvider = transientprovider;  
  41.         ScopedUniquekeyProvider = scopedprovider;  
  42.         SingletonUniquekeyProvider = singletonprovider;  
  43.     }  
  44. }  
Now it is time to wrap it all up and test it through a controller. I am using the Home controller’s Index action. Please refer to the below code from the controller and view.

5. Home Controller
  1. public class HomeController: Controller  
  2. {   
  3.     private ITransientUniqueKeyProvider _transientUniquekeyProvider;  
  4.     private IScopedUniqueKeyProvider _scopedUniquekeyProvider;  
  5.     private ISingletonUniqueKeyProvider _singletonUniquekeyProvider;  
  6.     private ISomeService _someserviceProvider;  
  7.     public HomeController(ITransientUniqueKeyProvider transientprovider,  
  8.         IScopedUniqueKeyProvider scopedprovider,  
  9.         ISingletonUniqueKeyProvider singletonprovider, ISomeService someserviceprovider)  
  10.     {  
  11.         _transientUniquekeyProvider = transientprovider;  
  12.         _scopedUniquekeyProvider = scopedprovider;  
  13.         _singletonUniquekeyProvider = singletonprovider;  
  14.         _someserviceProvider = someserviceprovider;  
  15.     }  
  16.     public IActionResult Index()  
  17.     {  
  18.         ViewBag.transientID = _transientUniquekeyProvider.UniqueKey;  
  19.         ViewBag.scopedID = _scopedUniquekeyProvider.UniqueKey;  
  20.         ViewBag.singletonID = _singletonUniquekeyProvider.UniqueKey;  
  21.         ViewBag.someServiceProvider = _someserviceProvider;  
  22.         return View();  
  23.     }  
  24. }  
Note the constructor which takes in the dependencies. So when the request comes to this page, the framework notes that it needs an implementation for these services. It checks the DI container and finds the registration, and based on that the valid instance is passed to the constructor. The same happens in case of the ISomeService dependency which the controller’s constructor needs.

6. Index View

In the view file, we are just displaying the values that we set in the view bag. Results provided in the next section.
  1. <div style="margin-top:35px">  
  2. lt;pstyle="font-weight:bold;text-decoration:underline">  
  3. These are the values that were injected into the home controller  
  4. </p>  
  5. <p>  
  6.     Transient value of Guid: @ViewBag.transientID  
  7. </p>  
  8. <p>  
  9.     Scoped value of Guid: @ViewBag.scopedID  
  10. </p>  
  11. <p>  
  12.     Singleton value of Guid: @ViewBag.singletonID  
  13. </p>  
  14. </div>  
  16. <div>  
  17.     <p style="font-weight:bold;text-decoration:underline">  
  19.         These are the values that were injected into Someservice Implementation  
  20.         </p>  
  21.         <p>  
  22.             Transient value of Guid: @ViewBag.someServiceProvider.TransientUniquekeyProvider.UniqueKey  
  23.         </p>  
  24.         <p>  
  25.             Scoped value of Guid: @ViewBag.someServiceProvider.ScopedUniquekeyProvider.UniqueKey  
  26.         </p>  
  27.         <p>  
  28.             Singleton value of Guid: @ViewBag.someServiceProvider.SingletonUniquekeyProvider.UniqueKey  
  29.         </p>  
  30. </div>  
That’s it! Results given below:

Output in screen

Page load first time,

Page load second time,

I really hope it was useful and fun. Happy coding!