Dependency Injection in ASP.NET Core

09 Jan 2024
Intermediate
143K Views
9 min read     Source Code

Dependency Injection in ASP.NET Core: An Overview

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, and 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 implement this interface to inject the dependency. In this type of DI, we can use either constructor injection or setter injection.

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 into 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 by ActivatorUtilities. The service required always a public constructor. ASP.net core only supports the single constructor for the controller class that requests the service. If we have more than one constructor, ASP.net core MVC raises the error.

Inject the dependency in the controller action

Sometimes, we require dependency on the particular controller action method not throughout the controller. ASP.net core MVC allows us to inject the dependency to a 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 the 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 lifetime. So we do not care about cleaning these dependencies, it will take care of the ASP.net core framework. There are three types of lifetimes.

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 for injecting 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 be injected and the service instance name that is used to access the service method.

There are multiple ways to define or consume the data in the View layer of the MVC and the ways are there such as ViewModel, ViewBag, ViewData, etc. Concerning 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 the dropdown. The directive @inject is also 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 is the design pattern that allows us to inject the dependency into the class from the outer world rather than creating it in class. This will help us to create loosely coupled applications so that they provide greater maintainability, testability, and also reusability. There is built-in support for dependency injection in ASP.net Core. This support is not limited to middleware but also supports Controllers, views, and models as well. Please don't forget to share your feedback on this article. Enjoy Coding..!

FAQs

Q1. What is default dependency injection in .NET Core?

A built-in part of the framework, along with configuration, logging, and the options pattern.

Q2. Are there 3 types of dependency injection?

Yes, constructor injection, method injection, and property injection.

Q3. Which type of dependency injection is best?

Constructor-Based Dependency Injection.
Share Article
About Author
Jignesh Trivedi (Microsoft MVP and Technical Lead)

Jignesh Trivedi is working as a software developer with a leading organization and having more than 11 years of experience. He is very passionate about Microsoft Technologies. He is author, speaker and MVP.

He has the experience to develop enterprise application using Microsoft technologies such as ASP.NET Core, C#, SQL Server, etc. and other technologies such as JavaScript, Angular, Node.js, etc. He loves building great products and POC (proof of concepts) using the best available technologies. He loves to share his knowledge by contributing to the Developer community.

Learn to Crack Your Technical Interview

Accept cookies & close this