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

Using Akka.NET with ASP.NET Core – Creating a Quiz API

This is a template and quick start guide for Akka.NET with ASP.NET Core. You can grab the concepts of using Akka.NET with ASP.NET Core and how Akka.NET actor model can be used in a simple quiz or survey based scenario.

But at the same time, this post will not provide all the fundamentals of actor model programming or Akka.NET. It assumes that you already have the understanding of the actor model and reactive programming basics, along with the some practical experience with the concepts of Akka.NET.

Scenario :  A quiz engine has many quizzes and users can attend the quizzes. Each user can attend many quizzes as possible at the same time. So each user session is associated with a quiz. One user can have many quiz sessions at the same time. A simplest session key is a combination of quiz Id and user Id. This combination is unique and referred as a session Id. Each session is an actor.

Also a template actor provides the quiz templates during the session creation. Each session actor gets the fresh copy of the quiz during session creation.

The below diagram shows the actor system used in this scenario.

Akka.NET actor model for quiz engine

Step by step explanation

  • In the ASP.NET Core Startup class the actor system (QuizActorSystem) is instantiated.
  • QuizMasterActor is created in the context of QuizActorSystem and the QuizActorSystem is added to the ASP.NET Core services collection, to be consumed by the controllers.
  • QuizMasterActor creates QuizSessionCoordinatorActor and QuizTemplateActor under its context.
  • For simplicity the QuizController of ASP.NET Core has two actions.
    1. GetQuestion – This gets session Id and question Id. The controller asks for the session actor from QuizSessionCoordinatorActor. If the session actor is already available it will be returned else QuizSessionCoordinatorActor will create a new session actor under its context. QuizSessionActor loads the quiz from the QuizTemplateActor in the initial creation, gets the fresh copy of the quiz and returns the requested question. Consequent requests will be served directly by the QuizSessionActor.
    2. GetAnswer – This action methods takes the session Id and the answer for the question and pass it to the right QuizSessionActor for the update.

The entire QuizSessionActor tree is created upon the request for a question under a specific session and this is quite safe and straight forward.

You can download the source code from this Github repo.