8 February 2017
Dependency Injection (DI) is one of the core features of .NET Core that allows for cleaner, more maintainable code by promoting loose coupling and enhancing testability. In this post, we’ll dive into Dependency Injection, its importance, and how to configure it in .NET Core to make your applications more modular and easier to test.
1. What is Dependency Injection (DI)?
Dependency Injection is a design pattern used to implement Inversion of Control (IoC), which allows you to inject dependencies (such as services, repositories, etc.) into your classes rather than having them directly instantiated inside the class. This promotes loose coupling between components and makes your application more flexible and easier to maintain.
In .NET Core, DI is built into the framework and is supported out of the box. It allows you to manage services that your application depends on in a central location, and these services are automatically injected into controllers or other components when needed.
2. Why Is Dependency Injection Important?
Here are some reasons why Dependency Injection is a critical pattern in modern development:
- Testability: By injecting dependencies, you can easily mock or replace services during unit tests, making your code more testable.
- Maintainability: It promotes single responsibility and separation of concerns, which makes the codebase easier to maintain.
- Reusability: Components are more reusable because they don’t have direct dependencies on specific implementations.
- Flexibility: You can easily swap out or replace services without changing the classes that depend on them.
3. Setting Up Dependency Injection in .NET Core
In .NET Core, DI is configured in the Startup.cs file, specifically in the ConfigureServices
method. This method allows you to register services that you want to be injected into your controllers or other components.
Let’s start by looking at how you can configure DI in your Startup.cs file.
Example: Setting Up DI in Startup.cs
In the ConfigureServices method of Startup.cs, you can add services to the DI container. These services can then be injected into your controllers, services, or other classes. Here’s an example of how to set it up:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Hosting;
namespace MyFirstApi
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// Add services to the DI container
services.AddControllers();
// Register a custom service (MyService) with DI
services.AddSingleton<IMyService, MyService>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
In this example, we add a custom service IMyService
and its implementation MyService
to the DI container. This makes the MyService
class available for injection into any class that requires it.
4. Injecting Dependencies into Controllers
Once the service is registered with the DI container, we can inject it into a controller. Let’s create a simple service and inject it into a controller:
Example: Defining a Service
First, create an interface for your service and its implementation:
public interface IMyService
{
string GetMessage();
}
public class MyService : IMyService
{
public string GetMessage()
{
return "Hello from MyService!";
}
}
Now, let’s modify the ProductsController
from the previous post to inject this service:
using Microsoft.AspNetCore.Mvc;
namespace MyFirstApi.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ProductsController : ControllerBase
{
private readonly IMyService _myService;
// Constructor Injection of IMyService
public ProductsController(IMyService myService)
{
_myService = myService;
}
// GET: api/products
[HttpGet]
public ActionResult<string> Get()
{
// Use the injected service
var message = _myService.GetMessage();
return Ok(message);
}
}
}
In this controller, we inject IMyService
through the constructor. The dependency is automatically provided by the DI container when the controller is created. This allows us to call the GetMessage()
method from the service within our action.
5. Types of Service Lifetimes
In .NET Core, there are three main lifetimes for services:
- Transient: Services are created each time they are requested. Use this for lightweight, stateless services.
services.AddTransient<IMyService, MyService>();
- Scoped: Services are created once per request (or per scope). Use this for services that require maintaining state throughout a request.
services.AddScoped<IMyService, MyService>();
- Singleton: Services are created once and shared throughout the application’s lifetime. Use this for services that do not need to be recreated.
services.AddSingleton<IMyService, MyService>();
In the example above, we used AddSingleton
, meaning that the same instance of MyService
will be used across all requests. You should choose the appropriate lifetime based on your service’s requirements.
6. Conclusion
In this post, you’ve learned the basics of Dependency Injection in .NET Core. We’ve covered:
- What Dependency Injection is and why it’s important.
- How to configure DI in Startup.cs.
- How to inject dependencies into controllers.
- The different service lifetimes in .NET Core.
By using Dependency Injection, you’re able to write more modular, maintainable, and testable code, which is a key part of building high-quality applications. As you continue to develop in .NET Core, you’ll find that DI will become an invaluable tool in your software development toolkit.
Happy coding!