Dependency injection
Overview
Dependency Injection is a design pattern and a core part of the Inversion of Control (IoC) principle, where an objectโs dependencies (services it needs to function) are supplied from the outside rather than created by the object itself.
It promotes:
- Loose coupling
- Testability
- Separation of concerns
- Scalability and maintainability
"Dependency Injection is a way to achieve loose coupling between classes by delegating the creation and lifecycle of dependencies to an external container. It allows for easier testing (via mocking), better separation of concerns, and more maintainable code. In modern C# applications, DI is usually built into the framework (e.g., ASP.NET Core) and configured via a service container."
Types of Dependency Injection
- Constructor Injection โ Most common and preferred in .NET
- Property Injection โ Optional dependencies or framework support
- Method Injection โ Dependencies passed during method calls
๐งช C# Example: Constructor Injection
// Define an interface
public interface INotificationService
{
void Send(string message);
}
// Implement the interface
public class EmailNotificationService : INotificationService
{
public void Send(string message)
{
Console.WriteLine($"Sending email: {message}");
}
}
// A class that depends on INotificationService
public class OrderProcessor
{
private readonly INotificationService _notificationService;
// Dependency is injected via constructor
public OrderProcessor(INotificationService notificationService)
{
_notificationService = notificationService;
}
public void ProcessOrder()
{
// Business logic...
_notificationService.Send("Order processed.");
}
}
Registering and Using DI in ASP.NET Core
// In Program.cs or Startup.cs
builder.Services.AddScoped<INotificationService, EmailNotificationService>();
builder.Services.AddScoped<OrderProcessor>();
// Resolving from the DI container (e.g., in a controller)
public class OrdersController : ControllerBase
{
private readonly OrderProcessor _processor;
public OrdersController(OrderProcessor processor)
{
_processor = processor;
}
public IActionResult PlaceOrder()
{
_processor.ProcessOrder();
return Ok("Order placed.");
}
}
Benefits
- Testability โ Inject mock implementations for unit testing
- Maintainability โ Swap implementations without changing consumer code
- Readability โ Explicit dependencies in constructors make intent clear
- Framework-supported โ Built into ASP.NET Core and .NET MAUI
Pitfalls to Avoid
- Over-injection โ Too many dependencies โ consider refactoring
- Service locator anti-pattern โ Hides dependencies
- Tight coupling to DI container โ Use abstractions, not direct container calls
Summary
Dependency Injection allows you to design decoupled, testable systems by injecting required services into your classes rather than creating them inside.
In .NET, DI is seamlessly integrated, typically uses constructor injection combined with scoped or singleton lifetimes registered in the service container.
Dependency Injection plays a key role in modern architectures like microservices, clean architecture, and test-driven development."