Understanding Dependency Injection in ASP.NET Core Web API

Introduction

Dependency Injection (DI) is a design pattern used to achieve Inversion of Control (IoC) between classes and their dependencies. In ASP.NET Core, DI is a fundamental part of the framework, making it easier to manage dependencies and improve the modularity, testability, and maintainability of your applications.

In this article, we will cover the three primary DI lifecycles: Singleton, Transient, and Scoped. We will demonstrate how to register and use these services in an ASP.NET Core Web API application.

Prerequisites

To follow along, you should have

  • Basic knowledge of ASP.NET Core.
  • .NET SDK installed on your machine.

Step 1. Create a New ASP.NET Core Web API Project

First, create a new ASP.NET Core Web API project.

dotnet new webapi -n DependencyInjectionDemo
cd DependencyInjectionDemo

Step 2. Define the Services

Create interfaces and their implementations for Singleton, Transient, and Scoped services.

Interfaces

Create a new folder called Services and add the following interfaces.

// File: Services/ISingletonService.cs
public interface ISingletonService
{
    Guid GetOperationId();
}
// File: Services/ITransientService.cs
public interface ITransientService
{
    Guid GetOperationId();
}
// File: Services/IScopedService.cs
public interface IScopedService
{
    Guid GetOperationId();
}

Implementations: Implement these interfaces in a single class.

// File: Services/OperationService.cs
public class OperationService : ISingletonService, ITransientService, IScopedService
{
    private readonly Guid _operationId;
    public OperationService()
    {
        _operationId = Guid.NewGuid();
    }
    public Guid GetOperationId()
    {
        return _operationId;
    }
}

Step 3. Register the Services

Register these services in the Startup.cs file within the ConfigureServices method.

// File: Startup.cs
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();

        // Register the services
        services.AddSingleton<ISingletonService, OperationService>();
        services.AddTransient<ITransientService, OperationService>();
        services.AddScoped<IScopedService, OperationService>();
    }
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        app.UseRouting();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

Step 4. Use the Services in a Controller

Create a controller that uses these services to demonstrate the differences in their lifecycles.

// File: Controllers/OperationsController.cs
using Microsoft.AspNetCore.Mvc;
using DependencyInjectionDemo.Services;

[ApiController]
[Route("api/[controller]")]
public class OperationsController : ControllerBase
{
    private readonly ISingletonService _singletonService;
    private readonly ITransientService _transientService;
    private readonly IScopedService _scopedService;

    public OperationsController(ISingletonService singletonService,
                                ITransientService transientService,
                                IScopedService scopedService)
    {
        _singletonService = singletonService;
        _transientService = transientService;
        _scopedService = scopedService;
    }

    [HttpGet]
    public IActionResult Get()
    {
        return Ok(new
        {
            Singleton = _singletonService.GetOperationId(),
            Transient = _transientService.GetOperationId(),
            Scoped = _scopedService.GetOperationId()
        });
    }
}

Step 5. Run and Test the Application

Run the application.

dotnet run

Output

You should see an output similar to this.

{
    "Singleton": "e1f3525e-2a92-4f5f-8d18-4e3c6e0326f2",
    "Transient": "a4d4a7b5-4088-4f8f-9b44-5e5e8d5e5b0c",
    "Scoped": "f61c76b5-786b-4eb0-a0d4-f7f73ed3a1f2"
}

Make multiple requests and observe the following behaviors.

  • Singleton: The Singleton GUID remains the same across all requests, indicating that the same instance is used throughout the application's lifetime.
  • Transient: The Transient GUID changes with every request, indicating a new instance is created each time.
  • Scoped: The Scoped GUID changes with each new request but remains consistent within the same request, indicating the same instance is used within the scope of a single request.

API

Notice that the Singleton GUID remains the same, the Transient GUID changes with every request, and the Scoped GUID changes with each new request but remains consistent within the same request.

GitHub Project Link

https://github.com/SardarMudassarAliKhan/DI-InAspNetCore-WebAPI

Conclusion

In this article, we demonstrated how to use dependency injection in an ASP.NET Core Web API application with Singleton, Transient, and Scoped services. Understanding these lifecycles helps you manage service instances appropriately, improving the performance and maintainability of your applications. Use Singleton for application-wide shared services, Transient for stateless services, and Scoped for per-request services.


Similar Articles