Understanding Dependency Injection (DI) in .NET Core with Examples

What is Dependency Injection (DI)?

Dependency Injection (DI) is a design pattern used to achieve loose coupling between classes and their dependencies.

Instead of a class creating its own dependencies, they are injected from the outside, usually by a framework like .NET Core. This promotes better maintainability, testability, and modularity in application design.

Why Use DI?

Improves testability (easily mock dependencies)

Enhances flexibility and maintainability

Supports SOLID principles, especially:

  • Inversion of Control (IoC)
  • Single Responsibility Principle

How DI Works in .NET Core?

.NET Core comes with a built-in IoC container that supports three main service lifetimes:

Lifetime Description Example Use Case
Singleton One instance for the entire application Caching, config, loggers
Scoped One instance per HTTP request User/session-level services
Transient New instance every time it's requested Lightweight, stateless logic

Step-by-Step Example: Injecting a Service

1. Define an Interface

public interface IMessageService
{
    string GetMessage();
}

2. Create a Concrete Implementation

public class HelloMessageService : IMessageService
{
    public string GetMessage()
    {
        return "Hello from DI!";
    }
}

3. Register the Service in the Program.cs (.NET 6+)

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<IMessageService, HelloMessageService>(); // DI registration

4. Inject the Service in a Controller

[ApiController]
[Route("[controller]")]
public class HomeController : ControllerBase
{
    private readonly IMessageService _messageService;

    public HomeController(IMessageService messageService)
    {
        _messageService = messageService;
    }

    [HttpGet]
    public string Get() => _messageService.GetMessage();
}

Output

GET /home
=> "Hello from DI!"

Real-World Use Case

In a recent project, we used DI to inject:

  • ILoggingService
  • IEmailService
  • IUserRepository

This allowed us to easily swap implementations during unit testing and to mock external services such as SendGrid and SQL Server, enabling a much more testable and scalable architecture.

Summary

  • DI is built into .NET Core via IServiceCollection.
  • Encourages clean, testable, and modular code.
  • Supports different service lifetimes (Singleton, Scoped, Transient).
  • Use constructor injection as the standard approach.