.NET Core  

Dependency Injection in .NET Core

🤔 What is Dependency Injection?

Dependency Injection means providing an object's dependencies rather than having it construct them itself.

Instead of:

var service = new EmailService();
var controller = new NotificationController(service);

We let a container (like the built-in DI in .NET Core) handle this:

services.AddScoped<IEmailService, EmailService>();

🧱 Types of Dependency Injection

.NET Core supports three main types:

  1. Constructor Injection (most common)
  2. Method Injection
  3. Property Injection

Let's focus on Constructor Injection since it’s most widely used.

🛠 Built-in Dependency Injection in .NET Core

When you create an ASP.NET Core application, the DI container is automatically set up.

The default container is lightweight but powerful. You register services in Program.cs (or Startup.cs in older versions) and the framework injects them where needed.

✍️ Step-by-Step Example

Let’s build a small service and inject it.

1. Define an Interface

public interface IMessageService
{
    string SendMessage(string message);
}

2. Create the Implementation

public class EmailService : IMessageService
{
    public string SendMessage(string message)
    {
        return $"Email sent with message: {message}";
    }
}

3. Register the Service in Program.cs

var builder = WebApplication.CreateBuilder(args);

// Register service
builder.Services.AddScoped<IMessageService, EmailService>();

var app = builder.Build();

// Configure the HTTP request pipeline, etc.
app.Run();

AddScoped means a new instance will be created per request. Other options are AddSingleton and AddTransient.

4. Inject the Service in a Controller

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

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

    [HttpGet]
    public IActionResult Send()
    {
        var result = _messageService.SendMessage("Hello from DI!");
        return Ok(result);
    }
}

🔁 Service Lifetimes Explained

Method Lifetime Description
AddSingleton One instance for the entire app Use for stateless services
AddScoped One per request Ideal for web apps
AddTransient New every time requested Lightweight, stateless

🧪 Unit Testing with Dependency Injection

Thanks to DI, testing is simple. You can mock dependencies easily.

[Fact]
public void SendMessage_ReturnsExpectedString()
{
    // Arrange
    var mockService = new Mock<IMessageService>();
    mockService.Setup(s => s.SendMessage("Hi")).Returns("Mocked message");

    var controller = new NotificationController(mockService.Object);

    // Act
    var result = controller.Send() as OkObjectResult;

    // Assert
    Assert.Equal("Mocked message", result.Value);
}

🧩 Injecting Configuration and Logging

You can also inject:

private readonly IConfiguration _config;
private readonly ILogger<SomeClass> _logger;

public SomeClass(IConfiguration config, ILogger<SomeClass> logger)
{
    _config = config;
    _logger = logger;
}

⚠️ Common Mistakes to Avoid

  • Registering services after builder.Build() – won’t work.
  • Mixing lifetimes inappropriately (e.g., injecting scoped into singleton).
  • Not using interfaces makes testing and swapping implementations harder.

✅ Best Practices

  • Always use interfaces for services.
  • Prefer constructor injection over method/property.
  • Understand service lifetimes before choosing one.
  • Use built-in DI unless your app truly needs a more advanced container.

🏁 Conclusion

Dependency Injection is a first-class citizen in .NET Core. It promotes clean architecture, separation of concerns, and makes unit testing straightforward.

By understanding service lifetimes and the registration process, you can build maintainable, testable, and scalable applications with ease.