ASP.NET Core Dependency Injection

ASP.NET Core Dependency Injection with Example

In this article, I will discuss ASP.NET Core Dependency Injection with Examples. Please read our previous article before proceeding to this one, where we discussed ASP.NET Core MVC Views with Examples. The Dependency Injection Design Pattern is one of the most used design Patterns in Real-Time Applications. The good thing is that ASP.NET Core provides built-in support for dependency injection. As part of this article, we will discuss the following pointers in detail.

  1. Understanding the Need for ASP.NET Core Dependency Injection
  2. What is the Dependency Injection Design Pattern?
  3. How do you Register a Service with ASP.NET Core Dependency Injection Container?
  4. What are the different Methods ASP.NET Core Provides to Register a Service?
  5. Understanding the Singleton, Scoped, and Transient Methods
  6. What are the Advantages of using Dependency Injection?
Understanding the Need for ASP.NET Core Dependency Injection

ASP.NET Core Dependency Injection (DI) is a powerful feature that helps manage object dependencies within our application. DI is a design pattern used to achieve loose coupling in software development. Instead of creating dependencies (objects or services) directly within a class, dependencies are injected into the class from outside, typically through constructor parameters.

Let us understand the need for Dependency Injection Design Patterns in ASP.NET Core Applications with an example. First, create a new ASP.NET Core Application named FirstCoreMVCWebApplication with an Empty Project Template. Once you create the ASP.NET Core Empty Project, let us add our models. To do so, first, create a folder with the name Models. Within the Models folder, let us add a class file with the name Student.cs, and this Student class will be our model for this application. Then open the Student.cs class file and copy and paste the following code into it.

namespace FirstCoreMVCWebApplication.Models
{
    public class Student
    {
        public int StudentId { get; set; }
        public string? Name { get; set; }
        public string? Branch { get; set; }
        public string? Section { get; set; }
        public string? Gender { get; set; }
    }
}
Creating Service Interface:

Next, create an interface named IStudentRepository.cs within the Models folder. This interface will declare the methods or operations we can perform on the student data. So, open IStudentRepository.cs and copy and paste the following code. Here, you can see we have created the interface with two methods.

using System.Collections.Generic;
namespace FirstCoreMVCWebApplication.Models
{
    public interface IStudentRepository
    {
        Student GetStudentById(int StudentId);
        List<Student> GetAllStudent();
    }
}
Creating Service Implementation:

Next, create a class file named StudentRepository.cs within the same Models folder. Then open the StudentRepository.cs class file and copy-paste the following code. This class implements the IStudentRepository interface by implementing the two methods declared in the IStudentRepository interface. Here, we have hard-coded the student data, but you will get the student data from a database in real-time applications.

using System.Collections.Generic;
using System.Linq;

namespace FirstCoreMVCWebApplication.Models
{
    public class StudentRepository : IStudentRepository
    {
        public List<Student> DataSource()
        {
            return new List<Student>()
            {
                new Student() { StudentId = 101, Name = "James", Branch = "CSE", Section = "A", Gender = "Male" },
                new Student() { StudentId = 102, Name = "Smith", Branch = "ETC", Section = "B", Gender = "Male" },
                new Student() { StudentId = 103, Name = "David", Branch = "CSE", Section = "A", Gender = "Male" },
                new Student() { StudentId = 104, Name = "Sara", Branch = "CSE", Section = "A", Gender = "Female" },
                new Student() { StudentId = 105, Name = "Pam", Branch = "ETC", Section = "B", Gender = "Female" }
            };
        }

        public Student GetStudentById(int StudentId)
        {
            return DataSource().FirstOrDefault(e => e.StudentId == StudentId) ?? new Student();
        }

        public List<Student> GetAllStudent()
        {
            return DataSource();
        }
    }
}
Program.cs:

In the Program class, we need to do two things. First, we need to configure the required MVC service to the IoC Container, and then we need to add the MVC Middleware to the request processing pipeline. So, modify the Program class as shown below.

namespace FirstCoreMVCWebApplication
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            builder.Services.AddMvc();

            var app = builder.Build();

            app.UseRouting();

            app.MapControllerRoute(
                name: "default", 
                pattern: "{controller=Home}/{action=Index}/{id?}" 
            );

            app.Run();
        }
    }
}
Without Dependency Injection:

Create a folder named Controllers in your project. Then, add a class file named HomeController.cs within the Controllers folder and copy-paste the following code. Here, you can see that within the Index and GetStudentDetails action methods, we are creating an instance of the StudentRepository class and calling the respective methods.

using FirstCoreMVCWebApplication.Models;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;

namespace FirstCoreMVCWebApplication.Controllers
{
    public class HomeController : Controller
    {
        public JsonResult Index()
        {
            StudentRepository repository = new StudentRepository();
            List<Student> allStudentDetails = repository.GetAllStudent();
            return Json(allStudentDetails);
        }

        public JsonResult GetStudentDetails(int Id)
        {
            StudentRepository repository = new StudentRepository();
            Student studentDetails = repository.GetStudentById(Id);
            return Json(studentDetails);
        }
    }
}

With the above changes in place, now run the application and check the above two methods. it should work as expected, as shown in the image below.

What is the Problem in the above implementation?

Let’s first understand the problem in the above implementation. Then, we will discuss how to overcome it by using the Dependency Injection Design Pattern in the ASP.NET Core application.

What is the Problem with the above implementation?

As you can see in the above HomeController class, it depends on an instance of the StudentRepository class to get student data. Here, within the HomeController class, we create an instance of the StudentRepository class and invoke the GetStudentById() and GetAllStudent methods. This is tight coupling because the HomeController class is now tightly coupled with the StudentRepository concrete class.

Tomorrow, if the implementation class of the IStudentRepository is changed from StudentRepository to TestStudentRepository, then we also need to make the changes to the code in the HomeController class, as they are both tightly coupled. We can overcome this problem by implementing the Dependency Injection Design Pattern.

What is the Dependency Injection (DI) Design Pattern?

Dependency Injection is the process of injecting the dependency object into a class that depends on it. It is the most commonly used design pattern nowadays to remove the dependencies between objects, allowing us to develop loosely coupled software components. Let us discuss the step-by-step procedure for implementing dependency injection in the ASP.NET Core MVC application.

The ASP.NET Core Framework is designed from scratch to provide built-in support for dependency injection design patterns. It injects dependency objects into a class through a constructor or method using the built-in IoC (Inversion of Control) container. The built-in IoC (Inversion of Control) container is represented by IServiceProvider implementation, which supports default constructor injection. The types (i.e., classes) managed by built-in IoC containers are called services.

Types of Services in ASP.NET Core:

There are two types of services in ASP.NET Core. They are as follows:

  1. Framework Services: Services that are a part of the ASP.NET Core Framework, such as IApplicationBuilder, IHostingEnvironment, ILoggerFactory, etc.
  2. Application Services: The services (custom types or classes) you create as a programmer for your application.

To let the IoC container automatically inject our application services, we must first register them with the IoC container.

How do you Register a Service with ASP.NET Core Dependency Injection Container?

We need to register a service with the ASP.NET Core Dependency Injection Container within the Main method of the Program class. Before we discuss how to Register a service with the Dependency Injection Container, it is important to understand the service’s lifetime. Setting the lifetime of the dependency object determines how many times it needs to be created.

What are the Different Methods ASP.NET Core Provides to Register a Service with the Dependency Injection Container?

ASP.NET Core provides 3 methods for registering a service with the ASP.NET Core Dependency Injection container. The method we use to register a service will determine its lifetime.

Singleton:

Services registered as Singleton are instantiated once per application and shared throughout its lifetime. It is Ideal for configuration services, logging, or other services where a single instance is sufficient and expensive to create. All requests for the service within the application receive the same instance. The container ensures thread safety when accessing the singleton instance. Exists for the entire duration of the application. This can be achieved by adding the service as a singleton through the AddSingleton method of the IServiceCollection.

Transient:

Services registered as Transient are created each time they are requested. This method is suitable for lightweight, stateless services with short-lived and transient behavior. A new instance is created every time the service is requested, and instances are not shared among different consumers. This can be achieved by adding the service through the AddTransient method of the IServiceCollection.

Scoped:

Services registered as Scoped are created once per request (in a web application, this typically means per HTTP request). It is used for services that should be reused within a single request, maintaining state across different components of the request, but should not be shared across different requests. Instances are disposed of at the end of the request. Common examples include data access operations within a single transaction. This can be achieved by adding the service through the AddScoped method of the IServiceCollection.

Registering the StudentRepository with ASP.NET Core Dependency Injection 

We need to register the service to the built-in dependency injection container with the program class. The following code shows how to register a service with different lifetimes:

Registering the StudentRepository Object

What is the ServiceDescriptor Class in .NET Core?

The ServiceDescriptor Class is part of the Dependency Injection (DI) framework provided by Microsoft in the ASP.NET Core framework. It describes how a service (an interface or a concrete type) should be registered with the DI container. It encapsulates information about the service’s type, implementation, and lifetime (singleton, transient, scoped). Essentially, it defines the configuration that the DI container uses to resolve dependencies.

Extension Methods for Registration

ASP.NET Core framework includes extension methods for each type of lifetime: AddSingleton(), AddTransient(), and AddScoped() methods for singleton, transient, and scoped lifetime, respectively. The following example shows how to register types (services) using extension methods.

Extension Methods for Registration

Let us use the service’s Singleton Instance in this example. Modify the Main method of the Program class as shown below. Which approach do you want to use to register the service? It is your preference.

namespace FirstCoreMVCWebApplication
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // Add Framework MVC services to the Container.
            builder.Services.AddMvc();

            //Application Service
            builder.Services.AddSingleton<IStudentRepository, StudentRepository>();
            //builder.Services.AddSingleton(typeof(IStudentRepository), typeof(StudentRepository));
            //builder.Services.AddTransient<IStudentRepository, StudentRepository>();
            //builder.Services.AddTransient(typeof(IStudentRepository), typeof(StudentRepository));
            //builder.Services.AddScoped<IStudentRepository, StudentRepository>();
            //builder.Services.AddScoped(typeof(IStudentRepository), typeof(StudentRepository));

            //Add Application Service to the Container.
            //builder.Services.Add(new ServiceDescriptor(typeof(IStudentRepository),
            //    new StudentRepository())); // by default singleton
            //builder.Services.Add(new ServiceDescriptor(typeof(IStudentRepository),
            //    typeof(StudentRepository), ServiceLifetime.Singleton)); // singleton
            //builder.Services.Add(new ServiceDescriptor(typeof(IStudentRepository),
            //    typeof(StudentRepository), ServiceLifetime.Transient)); // Transient
            //builder.Services.Add(new ServiceDescriptor(typeof(IStudentRepository),
            //    typeof(StudentRepository), ServiceLifetime.Scoped));    // Scoped

            var app = builder.Build();
            app.UseRouting();

            app.MapControllerRoute(
                name: "default", 
                pattern: "{controller=Home}/{action=Index}/{id?}" 
            );

            app.Run();
        }
    }
}
Constructor Injection in ASP.NET Core MVC Application

Once we register the service, the IoC container automatically performs constructor injection if a service type is included as a parameter in a constructor. Let us modify the HomeController, as shown below, to use Constructor Dependency Injection in the ASP.NET Core MVC Application.

using FirstCoreMVCWebApplication.Models;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;

namespace FirstCoreMVCWebApplication.Controllers
{
    public class HomeController : Controller
    {
        //Create a reference variable of IStudentRepository
        private readonly IStudentRepository? _repository = null;

        //Initialize the variable through constructor
        public HomeController(IStudentRepository repository)
        {
            _repository = repository;
        }

        public JsonResult Index()
        {
            List<Student>? allStudentDetails = _repository?.GetAllStudent();
            return Json(allStudentDetails);
        }

        public JsonResult GetStudentDetails(int Id)
        {
            Student? studentDetails = _repository?.GetStudentById(Id);
            return Json(studentDetails);
        }
    }
}
Code Explanation:

In the above example, the IoC container will automatically inject an instance of the StudentRepository into the constructor of HomeController. We don’t need to do anything else. The IoC container will create and dispose of an instance of the IStudentRepository based on the registered lifetime. Injecting the dependency object through a constructor is called a constructor dependency injection.

We created the _ repository variable as read-only, ensuring that once we inject the dependency object, that value can never be changed. At this point, run the application, and you should get the output as expected, as shown in the below image.

Constructor Injection in ASP.NET Core MVC Application

Action Method Injection in ASP.NET Core Application

Sometimes, we only need a dependency object in a single action method. In that case, we need to use the [FromServices] attribute. For a better understanding, please modify the HomeController class as follows. We are using the [FromServices] attribute within the Index action method. So, at runtime, the IoC Container will inject the dependency object into the IStudentRepository repository reference variable. Injecting the dependency object through a method is called method dependency injection.

using FirstCoreMVCWebApplication.Models;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;

namespace FirstCoreMVCWebApplication.Controllers
{
    public class HomeController : Controller
    {
        public JsonResult Index([FromServices] IStudentRepository repository)
        {
            List<Student> allStudentDetails = repository.GetAllStudent();
            return Json(allStudentDetails);
        }
    }
}

Run the application, and you will get the expected output, as shown below.

Action Method Injection in ASP.NET Core Application

What is the FromServices in ASP.NET Core Dependency Injection?

The [FromServices] attribute in ASP.NET Core explicitly indicates that a parameter of an action method should be resolved from the Dependency Injection (DI) container. This attribute is useful when we need to inject services directly into action methods instead of through the constructor. When you decorate a parameter in an action method with the [FromServices] attribute, ASP.NET Core’s dependency injection system will resolve and inject the specified service directly into the action method.

Property Dependency Injection in ASP.NET Core

The Built-in IoC container does not support property injection. You will have to use a third-party IoC container.

Get Services Manually in ASP.NET Core

We can also manually access the services configured with built-in IoC containers using the RequestServices property of HttpContext. The GetService method in ASP.NET Core Dependency Injection (DI) is used to retrieve a service from the DI container. It is provided by the IServiceProvider interface and allows for dynamic service resolution at runtime. So, modify the HomeController class as shown below to access the dependent service manually.

using FirstCoreMVCWebApplication.Models;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;

namespace FirstCoreMVCWebApplication.Controllers
{
    public class HomeController : Controller
    {
        public JsonResult Index()
        {
            var services = this.HttpContext.RequestServices;
           
            IStudentRepository? _repository = (IStudentRepository?)services.GetService(typeof(IStudentRepository));
            List<Student>? allStudentDetails = _repository?.GetAllStudent();
            return Json(allStudentDetails);
        }

        public JsonResult GetStudentDetails(int Id)
        {
            var services = this.HttpContext.RequestServices;
            IStudentRepository? _repository = (IStudentRepository?)services.GetService(typeof(IStudentRepository));
            Student? studentDetails = _repository?.GetStudentById(Id);
            return Json(studentDetails);
        }
    }
}

With the above changes in place, run the application, and you should get the output as expected as in the previous examples. It is recommended to use constructor injection instead of getting it using RequestServices.

What are the Advantages of using ASP.NET Core Dependency Injection?

The ASP.NET Core Dependency Injection allows us to develop loosely coupled software components. Here are some of the advantages of using dependency injection in ASP.NET Core:

  1. Loose Coupling: Using dependency injection, we can separate our classes from their dependencies, resulting in simpler code to maintain and test.
  2. Testability: By using dependency injection, we can increase the testability of our code by easily replacing dependencies with mock objects during unit testing.
  3. Extensibility: Using Dependency injection enhances the extensibility of our code by offering the flexibility to switch out dependencies conveniently.
  4. Reusability: Using dependency injection makes our code more reusable since we can conveniently share dependencies among various classes.

In the next article, I will discuss Singleton vs. Scoped vs. Transient Services in ASP.NET Core Application with Examples. In this article, I try to explain the ASP.NET Core Dependency Injection with examples. I hope this article will help you understand the Dependency Injection Concept in ASP.NET Core MVC Applications. 

12 thoughts on “ASP.NET Core Dependency Injection”

  1. blank

    Finally a clear and educational tutorial as I needed to assimilate this notion. Thank you very much and well done

  2. blank

    Setting the lifetime of the dependency object determines how many times the dependency object needs to be created.

    refer above sentence it i from your blog of “https://dotnettutorials.net/lesson/asp-net-core-dependency-injection” page

    this many time or much time?
    Please confirm

Leave a Reply

Your email address will not be published. Required fields are marked *