Introduction
In the realm of software development, Dependency Injection (DI) has emerged as a powerful design pattern for managing dependencies between components of an application. It promotes loose coupling, improves testability, and enhances maintainability by decoupling classes from their dependencies. In the context of .NET Core applications, Dependency Injection is not just a best practice but a core feature deeply integrated into the framework.
Understanding Dependency Injection
At its core, Dependency Injection is a technique where the dependencies of a class are provided from the outside rather than created internally. This inversion of control allows for more flexible and modular code. In .NET Core, this is achieved through the built-in DI container, which manages the instantiation and lifetime of dependencies.
Implementing Dependency Injection in .NET Core
To begin using Dependency Injection in your .NET Core application, you first need to set up the DI container. This typically involves registering services (dependencies) and configuring the container to resolve them when needed.
Step 1: Add Model
First, create a folder with the name Models. Within the Models folder, let us add a class file with the name Customer.cs and this Customer class will be our model for this application.
namespace DIDemo.Models
{
public class Customer
{
public int Id { get; set; }
public string? Name { get; set; }
public string? Gender { get; set; }
}
}
Step 2: Creating Service Interface
Create an interface with the name ICustomerService.cs. This interface will declare the list of methods or operations we can perform on the customer data.
using System.Collections.Generic;
namespace DIDemo.Services
{
public interface ICustomerService
{
List<Customer> Customers();
}
}
Step 3: Creating Service Implementation
Create a class file named CustomerService.cs. This class implements the ICustomerService interface by implementing the methods declared in the ICustomerService interface.
using System.Collections.Generic;
using System.Linq;
namespace DIDemo.Services
{
public class CustomerService : ICustomerService
{
private static List<Customer> customerList = new List<Customer>()
{
new Customer() { Id = 1, Name = "Siddhant", Gender = "Male" },
new Customer() { Id = 2, Name = "Sumit", Gender = "Male" },
new Customer() { Id = 3, Name = "Anushka", Gender = "Female" }
};
public List<Customer> GetAllCustomer()
{
return customerList();
}
}
}
Step 4: Register the Service with DI Container
To begin using Dependency Injection in your .NET Core application, you first need to set up the DI container. This typically involves registering services (dependencies) and configuring the container to resolve them when needed.
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:
builder.Services.AddScoped<ICustomerService, CustomerService>();
ICustomerService is an interface representing a service, and CustomerService is its implementation. By calling AddScoped, we register CustomerService with the DI container and specify that it should be scoped to the lifetime of the HTTP request.
Step 5: Constructor Injection
Once services are registered with the DI container, they can be injected into other classes through constructor injection. This is the most common way to leverage DI in .NET Core applications.
using DIDemo.Models;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
namespace DIDemo.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class CustomerController : Controller
{
//Create a reference variable of ICustomerService
private readonly ICustomerService _customerService;
//Initialize the variable through constructor
public CustomerController(ICustomerService customerService)
{
_customerService = customerService;
}
[HttpGet]
public ActionResult<List<Customer>> GetCustomer()
{
return OK(_customerService.GetAllCustomer());
}
}
}
By declaring the ICustomerService dependency in the constructor of CustomerController, the DI container automatically resolves and injects the appropriate implementation (CustomerService) when an instance of CustomerController is created.
Scoped, Transient, and Singleton Lifetime
In .NET Core, services can be registered with different lifetimes:
- Transient: A new instance is created every time the service is requested.
- Scoped: A single instance is created per HTTP request.
- Singleton: A single instance is created for the lifetime of the application.
Choosing the appropriate lifetime for a service depends on factors such as thread safety, resource usage, and the desired behavior of the application.
Benefits of Dependency Injection
Implementing Dependency Injection in your .NET Core application offers several benefits:
- Decoupling: Components become loosely coupled, making the codebase more modular and easier to maintain.
- Testability: Dependency injection facilitates unit testing by allowing dependencies to be easily mocked or replaced with test doubles.
- Flexibility: Components can be easily swapped or upgraded without affecting other parts of the application.
- Performance: DI containers manage the lifetime of dependencies efficiently, optimizing resource usage.
Conclusion
Dependency Injection is a fundamental concept in modern software development, and .NET Core provides robust support for implementing DI in applications. By leveraging the built-in DI container, developers can write cleaner, more maintainable code that is easier to test and extend. Whether you’re building web applications, APIs, or microservices, mastering Dependency Injection in .NET Core is a skill that will benefit you and your team in the long run.