Dependency Injection in ASP.NET Core

22 Aug 2022
Intermediate
70.9K Views

Dependency Injection is the design pattern that helps us to create an application which loosely coupled. This means that objects should only have those dependencies that are required to complete tasks. The main advantage of DI (Dependency Injection) is our application is loosely coupled and has provided greater maintainability, testability, and also re-usability. It is loosely coupled because dependency required by the class is injected from the outer world rather than created themselves directly win-in code.

There are three types of DI: Construction Injection, Setter Injection, Interface based Injection. The Construction Injection type of DI accepts their dependency at the constructor level which means that when creating an object of the class, their dependency passes through the constructor of the class. It provides a strong dependency contract between objects. The Setter Injection is also known as property injection. In this type of dependency injection, dependency passes through public property instead of the constructor. It allows us to pass the dependencies when required. It does not provide a strong dependency contract between objects. The interface-based dependency injection can be achieved by creating the common interface and other classes are implements this interface to inject the dependency. In this type of DI, we can use either constructor injection or setter injection.

There is a built-in support of dependency injection in ASP.net Core. This supports is not limited to middleware, but also support in Controllers, views, and model as well. There are two types of service containers provided by the ASP.net core: Framework Services and Application Services. The framework services are service that is provided by the ASP.net core such as ILoggerFactory etc. The application services are the custom services created based on our requirements.

Dependency injection into controllers

The dependency required by the ASP.net MVC controller is requested explicitly via their constructors (Constructor injection type) and this dependency is available for the controller. Some of the dependencies are injected to only the controller action as a parameter. ASP.net core has built-in support for constructor-based dependency. The dependency required by the controller is simply adding a service type to the controller in the constructor. The ASP.net core will identify the service type and try to resolve the type. It would be a good design if the service was defined using interfaces but it is not always true.

Example

In the following example, I have created the HelloWorld service. This service has a method called "SaysHello” that simply returns the "Hello " string. I have also implemented this service user interface.

namespace DepedencyInjectionExample.Service
{
 public interface IHelloWorldService
 {
 string SaysHello();
 }

 public class HelloWorldService : IHelloWorldService
 {
 public string SaysHello()
 {
 return "Hello ";
 }
 }
}

The next step is to add this service to the service container so that when the controller is requested for service, it is available to use. We can add the service to the service container in the ConfigureServices method of the startup class. There are three different life options available: Transient, Scoped, and Singleton. We will discuss this later part of the article.

public void ConfigureServices(IServiceCollection services)
{
 ….
 …
 services.AddTransient<IHelloWorldService, HelloWorldService>();
 …
 …
}

If we do not register the service to the ASP.net core service container, it will throw the exception as follows.

Now this “HelloWorld” service is available to use in the controller. We can inject this service as a dependency in the constructor.

using DepedencyInjectionExample.Models;
using Microsoft.AspNetCore.Mvc;
using DepedencyInjectionExample.Service;

namespace DepedencyInjectionExample.Controllers
{
 public class HomeController : Controller
 {
 IHelloWorldService _helloWorldService;
 public HomeController(IHelloWorldService helloWorldService)
 {
 _helloWorldService = helloWorldService;
 }
 }
}

Once the service has been configured correctly and injected into the controller, it should display a Hello message as expected.

The constructor dependency injection behavior resolved the service by either IServiceProvider or ActivatorUtilities. The ActivatorUtilities allows the creation of the object without service registration in the DI. The model binder, tag helper, and controller service are used ActivatorUtilities. The service required always a public constructor. ASP.net core only supports the single constructor for the controller class which requests the service. If we have more than one constructor, ASP.net core MVC raised the error.

Inject the dependency in the controller action

Sometimes, we required dependency on the particular controller action method not to throughout the controller. ASP.net core MVC allows us to inject the dependency to particular action using the "FromServices" attribute. This attribute tells the ASP.net core framework that parameters should be retrieved from the service container.

using DepedencyInjectionExample.Service;
using Microsoft.AspNetCore.Mvc;

namespace DepedencyInjectionExample.Controllers
{
 public class DemoController : Controller
 {
 public IActionResult Index([FromServices] IHelloWorldService helloWorldService)
 {
 ViewData["MyText"] = helloWorldService.SaysHello() + "Jignesh!";
 return View();
 }
 }
}

The property injection is not supported by the ASP.net core but we call the service instance manually and called service methods.

Get the service instance manually

There is another way to get dependency services from the service container. In this method, the service is not injected in the controller constructor or in the action method as a parameter. Using method "GetService" of the "HttpContext.RequestServices" property, we can get dependent services configured with the Service container. This is also known as property injection. Following is the example.

public IActionResult Index1()
{
 var helloWorldService = (IHelloWorldService)this.HttpContext.RequestServices.GetService(typeof(IHelloWorldService));
 ViewData["MyText"] = helloWorldService.SaysHello() + "Jignesh Trivedi!";
 return View("index");
}

Service Lifetime

ASP.net core allows us to specify the lifetime for registered services. The service instance gets disposed of automatically based on a specified life-time. So we do not care about the cleaning these dependencies, it will take care of the ASP.net core framework. There are three types of life-times.

Singleton

ASP.net core will create and share a single instance of the service through the application life. The service can be added as a singleton using the AddSingleton method of IServiceCollection. ASP.net core creates a service instance at the time of registration and subsequence requests use this service instance. Here, we do not require implementing a singleton design pattern and single instance maintained by the ASP.net core itself.

public void ConfigureServices(IServiceCollection services)
{
 ….
 …
 services.AddSingleton<IHelloWorldService, HelloWorldService>();
 ….
 …
}

Transient

ASP.net core will create and share an instance of the service every time to the application when we ask for it. The service can be added as Transient using the AddTransient method of IServiceCollection. This lifetime can be used in stateless service. It is a way to add lightweight service.

In other words, the transient service will be created every time as soon as it will get the request for the creation.

public void ConfigureServices(IServiceCollection services)
{
 ….
 …
 services.AddTransient<IHelloWorldService, HelloWorldService>();
 ….
 …
}

Scoped

ASP.net core will create and share an instance of the service per request to the application. It means that a single instance of service is available per request. It will create a new instance in a new request. The service can be added as scoped using the AddScoped method of IServiceCollection. We need to take care while, the service is registered via Scoped in middleware and inject the service in the Invoke or InvokeAsync methods. If we inject dependency via the constructor, it behaves like a singleton object.

public void ConfigureServices(IServiceCollection services)
{
 ….
 …
 services.AddScoped<IHelloWorldService, HelloWorldService>();
 ….
 …
}

Dependency injection into Views

ASP.net core can also able to inject the dependency to View. This is very useful to inject service related views such as localization. This method will bypass the controller call and fetch data directly from the service. We can inject the service dependency into the view using the @inject directive. The syntax is as follows to use this directive. Here we need to pass the service type that needs to inject and the service instance name that is used to access the service method.

There are multiple ways to define or consume the data to the View layer of the MVC and the ways are there such as ViewModel, ViewBag, ViewData, etc. With respect to ASP.NET Core, One of the suitable ways to supply the data is by creating a custom service that is used via the DI (dependency injection). However, it is not a best practice and depends on certain parameters according to the component configurations.

@inject <type> <instance name>
Example

In the following example, I have used the same service that was created in the preceding section and injected it into the view using the @inject directive, and using the service instance, we can call the service method into the view.

@{
 ViewData["Title"] = "DIToView";
}
@inject DepedencyInjectionExample.Service.IHelloWorldService helloWorldService

<h4>DI To View</h4>

<h5>
 @helloWorldService.SaysHello() Reader!!!
</h5>

View injection can be used to populate the UI elements such as dropdown. The common dropdown such city/state dropdown can be populated from the service. Rendering such things from the service is a standard approach in ASp.net core MVC. Alternatively, we can use view bag and Viewdata to populate dropdown. The directive @inject is also be used to override the injected service. For example, we are using the Html helper service for rendering the Html tags such as dropdown, textbox, etc. We can replace this service with our own service using the @inject directive.

Summary

Dependency Injection (DI) is one of the frequently used design patterns that will help you to decouple the different parts of their applications effectively. DI provides a mechanism or a way for the construction of the strict dependency graphs while having the independence of the complete class definitions.

Dependency injection is the design pattern that allows us to inject the dependency into the class from the outer world rather than creating with in class. This will help us to create a loosely coupled applications so that it has provided greater maintainability, testability, and also reusability. There is a built-in support of dependency injection in ASP.net Core. This supports is not limited to middleware, but also support in Controllers, views, and model as well. There are three easy steps to use Dependency injection into ASP.net core MVC application.

  • Create the service

  • Register the service into the ConfigureService method of the startup class, so that is available to use

  • Inject the service that you want to use

    ASP.net core allows us to specify the lifetime for registered services based on our requirement to use the service. The service can either register as Singleton, Transient, or Scoped.

Learn to Crack Your Technical Interview
Accept cookies & close this