Dependency Injection in ASP.NET

Dependency Injection (DI) is a design pattern used to implement inversion of control (IoC) and is a common technique used in many modern ASP.NET applications. Instead of a component creating its own dependencies (other objects it needs to function), these dependencies are "injected" from an external source.

Why Use Dependency Injection?

  • Improved Testability: DI makes it easier to substitute mock dependencies for real ones during unit testing.
  • Increased Modularity: Components become more decoupled, making them easier to understand, modify, and reuse.
  • Better Maintainability: Changes in one component are less likely to ripple through the entire application.
  • Simplified Configuration: The setup and configuration of dependencies can be managed centrally.

Core Concepts

The three fundamental principles of DI are:

  1. Dependency: An object that another object needs.
  2. Injection: The process of providing a dependency to an object. This can be done via constructor, property, or method.
  3. Injector: The object responsible for creating and injecting dependencies. In ASP.NET, this is typically the built-in DI container.

ASP.NET Core's Built-in DI Container

ASP.NET Core includes a lightweight, built-in dependency injection container. It supports the most common DI patterns:

  • Transient: A new instance is created every time an instance is requested.
  • Scoped: A new instance is created once per client request.
  • Singleton: A single instance is created for the entire application.

Registering Dependencies

Dependencies are registered in the ConfigureServices method of your Startup.cs file (or the equivalent in newer .NET versions).


using Microsoft.Extensions.DependencyInjection;
using YourApp.Services; // Assuming you have services in this namespace

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // Registering a transient service
        services.AddTransient<IMyService, MyService>();

        // Registering a scoped service
        services.AddScoped<IAnotherService, AnotherService>();

        // Registering a singleton service
        services.AddSingleton<ISingletonService, SingletonService>();

        // Other service registrations...
    }

    // ... other methods
}
                

Resolving Dependencies

Dependencies are typically resolved via constructor injection in controllers, services, or other managed components.


using YourApp.Services;

public class HomeController : Controller
{
    private readonly IMyService _myService;
    private readonly IScopedService _scopedService;

    public HomeController(IMyService myService, IScopedService scopedService)
    {
        _myService = myService;
        _scopedService = scopedService;
    }

    public IActionResult Index()
    {
        ViewBag.Message = _myService.GetData();
        ViewBag.ScopedMessage = _scopedService.GetScopedData();
        return View();
    }
}
                
Tip: Constructor injection is the most recommended way to inject dependencies in ASP.NET Core as it ensures that all required dependencies are available when an object is created.

Advanced Scenarios

The built-in container also supports:

  • Factory Registrations: Using lambda expressions to create instances.
  • Named Registrations: Differentiating between multiple registrations of the same service type.
  • Open Generic Types: Registering generic types.
  • Third-Party DI Containers: While the built-in container is powerful, you can replace it with more feature-rich containers like Autofac, Ninject, or StructureMap if needed.