Dependency Injection in .NET

Learn how to implement and leverage Dependency Injection (DI) in your .NET applications for better maintainability, testability, and scalability.

What is Dependency Injection?

Dependency Injection (DI) is a design pattern used in object-oriented programming where a class receives other classes it depends on (its dependencies) from an external source, rather than creating them itself. This process of "injecting" dependencies makes code more modular, flexible, and easier to test.

In .NET, DI is a first-class citizen, especially with ASP.NET Core, where it's built into the framework.

Core Concepts

Types of Dependency Injection

DI can be achieved through various means:

Implementing Dependency Injection in .NET

The .NET Generic Host and the built-in DI container simplify the implementation of DI. Here's a common workflow:

1. Define Services and Interfaces

It's good practice to define your services using interfaces. This promotes loose coupling.

// --- IService.cs --- public interface IMessageService { string GetMessage(); } // --- MessageService.cs --- public class MessageService : IMessageService { public string GetMessage() { return "Hello from Dependency Injection!"; } }

2. Register Services with the DI Container

In your application's startup configuration (e.g., `Program.cs` in modern .NET apps), you register your services with the DI container.

// --- Program.cs (ASP.NET Core 6+ Minimal API) --- var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddRazorPages(); // Register IMessageService with its implementation MessageService // Service Lifetime Options: // - AddTransient: A new instance is created every time it's requested. // - AddScoped: A new instance is created once per client request (in web apps). // - AddSingleton: A single instance is created and reused throughout the application's lifetime. builder.Services.AddTransient(); var app = builder.Build(); // ... Configure the HTTP request pipeline. app.Run();
Note: For older .NET versions (e.g., .NET Core 3.1), service registration typically happens in the `Startup.cs` file within the `ConfigureServices` method.

3. Inject Services into Consumers

Inject the registered service into classes that need it, typically via constructor injection.

// --- IndexModel.cs (Razor Pages Example) --- public class IndexModel : PageModel { private readonly IMessageService _messageService; public string CurrentMessage { get; private set; } public IndexModel(IMessageService messageService) { _messageService = messageService ?? throw new ArgumentNullException(nameof(messageService)); } public void OnGet() { CurrentMessage = _messageService.GetMessage(); } }

Benefits of Dependency Injection

Tip: Always favor constructor injection for mandatory dependencies. Use property injection for optional dependencies or when dealing with frameworks that require it.

Common DI Scenarios

Key Takeaway: Dependency Injection is a powerful pattern that, when used effectively with .NET's built-in DI container, leads to more robust, maintainable, and testable applications.

Further Reading