Dependency Injection in ASP.NET Core
On this page
Dependency Injection (DI) is a design pattern that implements the Inversion of Control (IoC) principle. In ASP.NET Core, DI is a fundamental part of the framework, enabling loose coupling between components and making applications more modular, testable, and maintainable.
What is Dependency Injection?
Instead of a component creating its own dependencies (objects it needs to function), those dependencies are "injected" into the component from an external source, typically a DI container.
Consider a service, EmailService, that needs an ISmtpClient. Without DI, EmailService might create its own SmtpClient instance:
public class EmailService
{
private readonly SmtpClient _client;
public EmailService()
{
_client = new SmtpClient("smtp.example.com"); // Tightly coupled
}
public void SendEmail(string to, string subject, string body)
{
// ... implementation using _client ...
}
}
With DI, the ISmtpClient is injected:
public interface ISmtpClient { /* ... */ }
public class SmtpClient : ISmtpClient { /* ... */ }
public class EmailService
{
private readonly ISmtpClient _client;
// Dependency is injected via the constructor
public EmailService(ISmtpClient client)
{
_client = client;
}
public void SendEmail(string to, string subject, string body)
{
// ... implementation using _client ...
}
}
Benefits of DI
- Loose Coupling: Components depend on abstractions (interfaces) rather than concrete implementations, making it easier to swap implementations.
- Testability: Dependencies can be easily replaced with mock objects during testing, isolating the component under test.
- Reusability: Components become more independent and can be reused across different parts of the application or in other projects.
- Maintainability: Changes to a dependency's implementation don't necessarily require changes in the components that use it, as long as the interface remains the same.
- Single Responsibility Principle: DI encourages classes to have a single, well-defined responsibility.
Service Lifecycles
The DI container manages the lifetime of registered services. The most common lifecycles are:
- Singleton: A single instance of the service is created and reused throughout the application's lifetime.
- Scoped: A single instance of the service is created per client request (or scope). This is ideal for services that need to maintain state within a single HTTP request, like database contexts.
- Transient: A new instance of the service is created every time it's requested. This is suitable for lightweight, stateless services.
Registering Services
Services are registered with the DI container in the Program.cs (or Startup.cs in older versions) file using the WebApplicationBuilder.
Example:
// Program.cs
using Microsoft.Extensions.DependencyInjection;
var builder = WebApplication.CreateBuilder(args);
// Register a service as a singleton
builder.Services.AddSingleton();
// Register a service as scoped
builder.Services.AddScoped();
// Register a service as transient
builder.Services.AddTransient();
var app = builder.Build();
// ... rest of the application setup ...
app.Run();
The most common methods are:
AddSingleton<TInterface, TImplementation>()AddScoped<TInterface, TImplementation>()AddTransient<TInterface, TImplementation>()
AddSingleton<MyConcreteService>(), but it's generally recommended to program against abstractions.
Consuming Services
Registered services can be consumed by:
- Controllers/Page Models: By declaring them as constructor parameters.
- Other Services: By declaring them as constructor parameters of other registered services.
- Middleware: By requesting them from the
HttpContext.RequestServices.
Example: Consuming a service in an MVC Controller
public class HomeController : Controller
{
private readonly IMyScopedService _scopedService;
private readonly IMyTransientService _transientService;
public HomeController(IMyScopedService scopedService, IMyTransientService transientService)
{
_scopedService = scopedService;
_transientService = transientService;
}
public IActionResult Index()
{
ViewData["ScopedValue"] = _scopedService.GetId();
ViewData["TransientValue"] = _transientService.GetId();
return View();
}
}
Built-in Services
ASP.NET Core registers many useful services by default, such as:
ILogger<T>for loggingIHttpClientFactoryfor creatingHttpClientinstancesDbContext(if using Entity Framework Core)IConfigurationfor accessing application settingsIHostEnvironmentfor accessing environment information
You can inject these services directly into your components without explicit registration.
Understanding and leveraging Dependency Injection is key to building robust and scalable applications with ASP.NET Core.