In the world of software development, Dependency Injection (DI) stands as a fundamental concept, often hailed as a cornerstone of good design and modular architecture. But, for many developers, especially those newer to the field, the term can evoke confusion or difficult to understand. I hope that the blog will help you understand DI better.
1. Understanding Dependency Injection
- Modules do not communicate directly with each other, but through an interface. The low-level module will implement the interface, the high-level module will call the low-level module through the interface.
- For example: To communicate with customer service, we have the ICustomerService interface, the low-level modules are CustomerService. The high-level module CustomerController will only use the ICustomerService interface.
- Initialization of low-level modules will be performed by DI Container. For example: In the CustomerController, we will not initialize ICustomerService service = new CustomerService (), this will be done by DI Container. The CustomerController will not know anything about the class CustomerService
- Which Module is attached to which interface will be configured in the class Program.cs
2. The Three Types of Dependency Injection
- Constructor Injection: In this approach, dependencies are provided through a class's constructor. This ensures that all required dependencies are available when an object is instantiated, promoting immutability and simplifying testing.
- Setter Injection: Also known as property injection, Setter Injection involves providing dependencies through setter methods. While not as preferred as Constructor Injection due to the potential for objects to be in an invalid state, Setter Injection can be useful for optional dependencies.
- Interface Injection: This approach is less common and involves providing dependencies through an interface that the client class implements. However, this method can introduce tight coupling between the client class and the injector, making it less flexible compared to Constructor Injection.
3. Advantages and disadvantages of Dependency Injection
Advantage
- Reduce adhesion between modules
- Code is easy to maintain, easy to replace modules
- Very easy to test and write Unit Test
Disadvantages
- The concept of DI is quite difficult to understand new developers will have difficulty learning it
- Objects are completely initialized from the beginning, which can reduce performance Increases code complexity
4. Using DI in .NET CORE
- Use an interface or base class to abstract implementation dependencies.
- Register the dependency in the service container. ASP.NET Core allows us to register our application services with the IoC container, in the Program.cs class use IServiceCollection to register application services
- Include the service in the constructor of the class in which it is used. The framework will create an instance of the dependency and remove it when it is no longer needed.
public interface ICustomerService { void SendMessage(string message); } public class CustomerAService : ICustomerService { public void SendMessage(string message) { Console.WriteLine($"CustomerAService.SendMessage Message: {message}"); }}var builder = WebApplication.CreateBuilder(args);builder.Services.AddScoped<ICustomerAService, CustomerAService>();var app = builder.Build();- Transient: Instance is initialized each time a service is created
- Scoped: Instance is initialized per scope. (Scope here is each request sent to the application). In the same scope, the service will be reused.
- Singleton: The service instance is created uniquely at application launch and is used everywhere
public class CustomerController : PageModel { private readonly ICustomerService _customerService; public Index2Model(ICustomerService customerService) { _customerService = customerService; } public void OnSendMesasge() { _customerService.SendMessage("Send message"); } } Cover image from freepik.com