ASP.NET Core Dependency Injection

Introduction

ASP.NET Core comes with an inbuilt Dependency Injection (DI) module. We can register custom DI modules as well. This post explains the fundamentals of the inbuilt DI module available in the ASP.NET Core.

Exploring deeper into the Service Registrations

Get the Code for the below experiment from my GitHib

ASP.NET Core provides 3 generic service registration types for custom services.

  • Singleton – One instance of the dependency to serve across all the requests.
  • Transient – Different instances for each dependent call, thus creating different instances of the injected service in a single request call flow.
  • Scoped – Single instance of the dependency in a single call flow. Within the single request call one same instance will be used.

Apart from the above 3 generic service registrations ASP.NET Core provides other inbuilt service registration methods.  Let’ see how the these three generic service registration types work and how the lifecycle of the instances are handled.

 

Let’s have one common interface IMyService and create 3 different types of interfaces from the common type interface, each for the different service type registrations.


public interface IMyService
{ 
Guid Id { get; set; }
}

public interface IMySingeltonService : IMyService
{
}

public interface IMyTransientService : IMyService
{
}

public interface IMyScopedService : IMyService
{
}

Then let’s implement the above interfaces with three different classes. All these classes will create a new Guid in the constructor.


public class MySingletonService : IMySingeltonService
{
public Guid Id { get; set; }

public MySingletonService()
{
Id = Guid.NewGuid();
}
}

public class MyTransientService : IMyTransientService
{
public Guid Id { get; set; }

public MyTransientService()
{
Id = Guid.NewGuid();
}
}

public class MyScopedService : IMyScopedService
{
public Guid Id { get; set; }

public MyScopedService()
{
Id = Guid.NewGuid();
}
}

In the constructors of the implemenations we generate a Guid and we’ll print this in the View to see how many times the service instances are being instantiated. In ordert to do that let’s register the services with the right generic service registration type method respective to their implmentation name. (the generics syntax < > is not getting formatted with the wordpress code tag so I pasted the image for the below snippet. )

 

blog code 1

We can inject the services into the HomeController with the following constructir and will print the Id of each service in the View.


private readonly IMySingeltonService _singletonService;
private readonly IMyTransientService _transientService;
private readonly IMyScopedService _scopedService;

public HomeController(IMySingeltonService singletonService, IMyTransientService transientService, 
 IMyScopedService scopedService)
 {
 _singletonService = singletonService;
 _transientService = transientService;
 _scopedService = scopedService;
 }

public IActionResult Index()
 {
 ViewBag.Singleton = _singletonService.Id;
 ViewBag.Transient = _transientService.Id;
 ViewBag.Scoped = _scopedService.Id;

return View(ViewBag);
 }

When we run the application we will get the  below results. 2 different requests are compared.

blog 2

You can note, the Singleton implementation is same across different requests. Only one instance of service which is registered as Singleton available across the requests.

The above implementation does not give a full picture to compare the difference between Transient and Scoped service registrations as they both have difference instances in different requests. In order to understand the behavior of them we need to implement another service.


public interface IMyAnotherService
{
Guid SingletonId { get; set; }
Guid TransientId { get; set; }
Guid ScopedId { get; set; }

}

public class MyAnotherService : IMyAnotherService
{
private readonly IMySingeltonService _singletonService;
private readonly IMyTransientService _transientService;
private readonly IMyScopedService _scopedService;

public Guid SingletonId { get; set; }
public Guid TransientId { get; set; }
public Guid ScopedId { get; set; }

public MyAnotherService(IMySingeltonService singletom, IMyTransientService transient, IMyScopedService scoped)
{
_singletonService = singletom;
_transientService = transient;
_scopedService = scoped;

SingletonId = singletom.Id;
TransientId = transient.Id;
ScopedId = scoped.Id;
}
}

 

Do the requried changes in the Controller to accpet IMyAnotherService.


private readonly IMySingeltonService _singletonService;
private readonly IMyTransientService _transientService;
private readonly IMyScopedService _scopedService;
private readonly IMyAnotherService _anotherService;

public HomeController(IMySingeltonService singletonService, IMyTransientService transientService,
IMyScopedService scopedService, IMyAnotherService anotherService)
{
_singletonService = singletonService;
_transientService = transientService;
_scopedService = scopedService;
_anotherService = anotherService;
}

public IActionResult Index()
{
ViewBag.Singleton = _singletonService.Id;
ViewBag.Transient = _transientService.Id;
ViewBag.Scoped = _scopedService.Id;

ViewBag.AnotherSingleton = _anotherService.SingletonId;
ViewBag.AnotherTransient = _anotherService.TransientId;
ViewBag.AnotherScoped = _anotherService.ScopedId;

return View(ViewBag);
}

Now we can register the IMyAnother service in different modes and check the instance ouptuts. The below figure explains the instance lifetime. For the same instance the similar color is maintained.

blog 4

In a simpler form we summarize this like below. How many times a construcotr is being called.

  • Singleton – Once in the application lifetime.
  • Transient – Everytime the instance is requested regardless of the request.
  • Scoped – Once per request regardless of how many services use it.

DI figure

When IMyAnohterService is added as a Scoped service the below image shows two different requests.

Singleton service remains same across all the requests.

Transient service changes between HomeController and IMyAnotherService within the same request.

Scoped service does not change in the same request as it’s the same instance for both the HomeController and IMyAnotherService but between requests it changes.

 

blog 5

Interesting Scenrio IHttpContextAccessor 

In ASP.NET Core DI model the framework also provides some additional injection methods for some known scenarios. Like registering EF DbContext using the AddDbContext method. This method by default injects the DbContext in the Scoped mode.

But the interesting scenario is registering IHttpContextAccessor as Singleton as shown below.

blog 6

This service is used to access the HttpContext of the request, so registering this service as Singleton based on the official documentation collides with the experiement we did above, because having the Singleton registration would not give the flexibility to get the HttpContext per request.

But the framework handles it and this is explained well in this blog post

Conclusion

We have the understanding of the DI in ASP.NET Core and some special in built framework DI methods.

In the business logic services it’s good we add them as Scoped, unless we have a generic implementation of some functions like email.

Advertisement