.NET Dependency Injection Patterns
By Sandeep Mhaske | January 2025
Your Ad Here
Table of Contents
Introduction
Dependency Injection (DI) is a design pattern that plays a pivotal role in achieving loosely coupled and maintainable applications. With .NET Core, Microsoft introduced a built-in DI container, enabling developers to leverage DI seamlessly in their applications.
This blog delves into the core concepts of DI, explores different injection patterns, and provides practical examples and tutorials to help you master Dependency Injection in .NET.
Understanding Dependency Injection (DI)
Dependency Injection is a technique for providing dependencies to a class rather than allowing the class to create them. By delegating the responsibility of dependency creation, DI promotes:
- Loose Coupling: Classes are not tightly bound to their dependencies.
- Improved Testability: Dependencies can be easily mocked for unit testing.
- Better Maintainability: Changes in dependencies do not require changes in dependent classes.
Dependency Injection Patterns Overview
DI patterns dictate how dependencies are provided to a class. The most commonly used patterns are:
- Constructor Injection: Dependencies are passed via the constructor.
- Property Injection: Dependencies are assigned to properties.
- Method Injection: Dependencies are passed as method parameters.
Constructor Injection
Constructor Injection is the most widely used DI pattern. Dependencies are provided through the constructor when the class is instantiated.
public class ProductService { private readonly IRepository _repository; public ProductService(IRepository repository) { _repository = repository; } public IEnumerable<Product> GetAllProducts() { return _repository.GetProducts(); } }
In the above example, the IRepository
dependency is injected into the ProductService
class via its constructor.
Property Injection
Property Injection involves setting dependencies via public properties. This pattern is useful when the dependency is optional.
public class ProductService { public IRepository Repository { get; set; } public IEnumerable<Product> GetAllProducts() { return Repository.GetProducts(); } }
Method Injection
Method Injection provides dependencies as parameters to specific methods. This approach is ideal when the dependency is only needed for specific actions.
public class ProductService { public IEnumerable<Product> GetAllProducts(IRepository repository) { return repository.GetProducts(); } }
Service Lifetimes in DI
.NET Core DI supports three service lifetimes:
- Transient: A new instance is created each time the service is requested.
- Scoped: A single instance is created per request.
- Singleton: A single instance is created and shared across the application lifecycle.
Real-World Example with Code
Below is an example of configuring DI in a .NET Core application:
using Microsoft.Extensions.DependencyInjection; var services = new ServiceCollection(); services.AddTransient<IRepository, Repository>(); services.AddScoped<IService, Service>(); services.AddSingleton<ILogger, Logger>(); var provider = services.BuildServiceProvider(); var service = provider.GetService<IService>();
Best Practices for Dependency Injection
- Use Constructor Injection as the default pattern.
- Avoid injecting too many dependencies into a single class.
- Leverage the built-in DI container in .NET Core for simplicity and consistency.
- Use scoped services for web applications to handle per-request dependencies.
Conclusion
Dependency Injection is a fundamental concept for designing flexible, maintainable, and testable .NET applications. By understanding the various DI patterns and their appropriate use cases, you can improve the overall architecture of your projects.