Introduction
Dependency Injection (DI) is a design pattern that promotes loose coupling and enhances testability and maintainability in your applications. It is a key feature in modern C# development, especially with frameworks like ASP.NET Core. This post will cover the basics of dependency injection, how to set it up in a C# application, and provide practical examples to illustrate its usage.
What is Dependency Injection?
Dependency Injection is a design pattern that deals with how components get their dependencies. Instead of the component creating its dependencies, they are provided to the component by an external source.
Key Benefits:
- Loose Coupling: Reduces dependencies between components.
- Enhanced Testability: Makes it easier to test components in isolation.
- Maintainability: Simplifies changing dependencies without modifying the component’s code.
Setting Up Dependency Injection
To use dependency injection in a C# application, you need a DI container. In ASP.NET Core, the built-in DI container is used.
Adding Services to the DI Container
You typically configure the DI container in the Startup class of an ASP.NET Core application.
Example: Configuring DI in ASP.NET Core
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IMyService, MyService>();
services.AddScoped<IOtherService, OtherService>();
services.AddSingleton<ISingletonService, SingletonService>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
public interface IMyService
{
void DoWork();
}
public class MyService : IMyService
{
public void DoWork()
{
Console.WriteLine("MyService is doing work.");
}
}
public interface IOtherService
{
void PerformTask();
}
public class OtherService : IOtherService
{
public void PerformTask()
{
Console.WriteLine("OtherService is performing a task.");
}
}
public interface ISingletonService
{
void Execute();
}
public class SingletonService : ISingletonService
{
public void Execute()
{
Console.WriteLine("SingletonService is executing.");
}
}
Explanation:
- ConfigureServices: Adds services to the DI container with different lifetimes (Transient, Scoped, Singleton).
- IMyService, IOtherService, ISingletonService: Interfaces for the services.
- MyService, OtherService, SingletonService: Implementations of the interfaces.
Injecting Dependencies
Once services are registered in the DI container, you can inject them into your classes via constructors.
Example: Injecting Dependencies into a Controller
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("[controller]")]
public class MyController : ControllerBase
{
private readonly IMyService _myService;
private readonly IOtherService _otherService;
public MyController(IMyService myService, IOtherService otherService)
{
_myService = myService;
_otherService = otherService;
}
[HttpGet]
public IActionResult Get()
{
_myService.DoWork();
_otherService.PerformTask();
return Ok("Services executed successfully.");
}
}
Explanation:
- MyController: A controller that uses dependency injection to get instances of
IMyServiceandIOtherService. - Constructor Injection: The services are injected via the constructor.
Service Lifetimes
When registering services in the DI container, you can specify their lifetimes.
- Transient: A new instance is created each time it is requested.
services.AddTransient<IMyService, MyService>();
- Scoped: A new instance is created per scope (e.g., per web request).
services.AddScoped<IOtherService, OtherService>();
- Singleton: A single instance is created and shared throughout the application’s lifetime.
services.AddSingleton<ISingletonService, SingletonService>();
Full Example Code
Here’s the complete code for setting up dependency injection, registering services, and injecting them into a controller:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Mvc;
using System;
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IMyService, MyService>();
services.AddScoped<IOtherService, OtherService>();
services.AddSingleton<ISingletonService, SingletonService>();
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
public interface IMyService
{
void DoWork();
}
public class MyService : IMyService
{
public void DoWork()
{
Console.WriteLine("MyService is doing work.");
}
}
public interface IOtherService
{
void PerformTask();
}
public class OtherService : IOtherService
{
public void PerformTask()
{
Console.WriteLine("OtherService is performing a task.");
}
}
public interface ISingletonService
{
void Execute();
}
public class SingletonService : ISingletonService
{
public void Execute()
{
Console.WriteLine("SingletonService is executing.");
}
}
[ApiController]
[Route("[controller]")]
public class MyController : ControllerBase
{
private readonly IMyService _myService;
private readonly IOtherService _otherService;
public MyController(IMyService myService, IOtherService otherService)
{
_myService = myService;
_otherService = otherService;
}
[HttpGet]
public IActionResult Get()
{
_myService.DoWork();
_otherService.PerformTask();
return Ok("Services executed successfully.");
}
}
Conclusion
Dependency Injection is a fundamental design pattern that enhances the modularity, testability, and maintainability of your code. By understanding how to set up and use dependency injection in C#, you can build more robust and flexible applications.
