.NET Core  

Mastering Dependency Injection in .NET Core: A Complete Beginner-to-Advanced Guide

Introduction

Dependency Injection (DI) is a cornerstone of modern software architecture in .NET Core and beyond. It promotes loose coupling, testability, and maintainability, making applications easier to build and scale.

In this article, you’ll learn what Dependency Injection is, how it works in .NET Core, and how to manage it efficiently—from basic service registration to advanced patterns like scoped lifetimes and third-party containers.

๐ŸŒฑ What is Dependency Injection?

Dependency Injection is a design pattern that allows classes to receive their dependencies from external sources rather than creating them internally. It follows the Inversion of Control (IoC) principle.

Why use DI?

  • Reduces tight coupling
  • Increases testability (easy to mock dependencies)
  • Promotes code reuse and cleaner architecture

โš™๏ธ How Dependency Injection Works in .NET Core

.NET Core comes with a built-in dependency injection container out of the box, and it’s integrated into the ASP.NET Core pipeline.

Step 1. Register Services

You register services in the Program.cs file (or Startup.cs in older versions):

builder.Services.AddTransient<IMyService, MyService>();
builder.Services.AddScoped<IRepository, Repository>();
builder.Services.AddSingleton<ILogger, Logger>();

Step 2. Inject Services

Services are injected via constructor injection (recommended):

public class HomeController : Controller
{
    private readonly IMyService _service;

    public HomeController(IMyService service)
    {
        _service = service;
    }

    public IActionResult Index()
    {
        var data = _service.GetData();
        return View(data);
    }
}

๐Ÿ” Understanding Service Lifetimes

.NET Core supports three built-in lifetimes:

Lifetime Description Use Case
Transient New instance every time Lightweight, stateless services
Scoped One instance per request Services with request-specific state
Singleton One instance for the whole app Shared/global services like caching
services.AddTransient<IMyService, MyService>();
services.AddScoped<IUserSession, UserSession>();
services.AddSingleton<IConfigProvider, ConfigProvider>();

๐Ÿงช Testing with Dependency Injection

DI makes testing easier by allowing you to inject mocked services:

var mockService = new Mock<IMyService>();
mockService.Setup(s => s.GetData()).Returns("Mocked Data");

var controller = new HomeController(mockService.Object);

๐Ÿงฐ Advanced Scenarios

1. Factory-Based Registration

services.AddSingleton<IMyService>(provider =>
{
    var config = provider.GetRequiredService<IConfiguration>();
    return new MyService(config["ApiKey"]);
});

2. Conditional Registration

if (env.IsDevelopment())
    services.AddSingleton<IMyService, DevService>();
else
    services.AddSingleton<IMyService, ProdService>();

3. Using Third-Party Containers (e.g., Autofac)

For more flexibility:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .UseServiceProviderFactory(new AutofacServiceProviderFactory())
        .ConfigureContainer<ContainerBuilder>(builder =>
        {
            builder.RegisterType<MyService>().As<IMyService>();
        });

๐Ÿ“Œ Best Practices for Managing DI in .NET Core

  • Prefer interfaces over concrete classes

  • Avoid service locator pattern (don’t use IServiceProvider.GetService<T>() unless necessary)

  • Use scoped services for per-request data

  • Group registrations by module or feature

  • Use extension methods for cleaner Program.cs

public static class ServiceExtensions
{
    public static IServiceCollection AddMyFeatureServices(this IServiceCollection services)
    {
        services.AddScoped<IMyService, MyService>();
        return services;
    }
}

โœ… Conclusion

Dependency Injection in .NET Core is powerful, flexible, and easy to manage once you understand the fundamentals. Whether you're building small APIs or large-scale enterprise systems, proper DI practices will ensure your app stays modular and maintainable.