.NET dependency injection explained with example

dotnet DI

.NET dependency injection explained with example

Dependency Injection container (DI container)

  • Services (like email service, database service) are used generally by the application logic to interface with the external systems
  • DI container is a collection of dependencies (aka services)
  • Application logic can request an instance of a required service from the DI container

Service Lifetimes

When requesting a service instance from a DI container

  • A new service instance is created every time if it is a Transient service
  • A single instance of a service is reused every time if it is a Singleton service
  • A service instance is reused through out a http request if it is a Scoped service. Scoped service is applicable only in web applications

Inversion of Control for Dependency Injection

  • While adding a service to the DI container, the service signature can be mentioned as an interface. An example is shown below.
services.AddSingleton<IMyService, MyService>():

  • In this example, MyService class should implement IMyService interface in order to be registered.
  • This is called Inversion of Control (IOC) / Dependency Inversion since the service (dependency) should comply to the application’s interface.
  • The service can be retrieved from the DI container by specifying the interface instead of concrete class
var srv = serviceProvider.GetService<IMyService>();

  • Dependency Inversion makes testing and code maintenance easy since the application is concerned only with the service interface instead of its implementation. The service implementation can be swapped with another implementation, and the application code will not change since it is concerned with the interface.

DI in console apps

  • The followig example demonstrates how to use DI container in console apps.
  • Install Microsoft.Extensions.DependencyInjection package
using Microsoft.Extensions.DependencyInjection;

//setup DI container
var serviceProvider = new ServiceCollection()
    .AddSingleton<IMyService, MyService>()
    .BuildServiceProvider();

//get a service instance from DI container and use it
var srv = serviceProvider.GetService<IMyService>();
srv?.DoSomething();

Console.WriteLine("Completed!");

public interface IMyService
{
    void DoSomething();
}

public class MyService : IMyService
{
    public void DoSomething()
    {
        Console.WriteLine("MyService is doing something.");
    }
}

DI in web apps

  • ASP.NET Core web application builder provides access to DI container using the Services property
  • For example builder.Services.AddSingleton<IMessageWriter, MessageWriter>(); will add a Singleton service to the DI container
// Program.cs
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorPages();

// Add required service to the container
builder.Services.AddSingleton<IMessageWriter, MessageWriter>();

// adding service in more readable way using extension method
//builder.Services.AddMessageWriter();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see <https://aka.ms/aspnetcore-hsts>.
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseRouting();

app.UseAuthorization();

app.MapStaticAssets();
app.MapRazorPages()
   .WithStaticAssets();

app.Run();

public interface IMessageWriter
{
    void Write(string message);
}

public class MessageWriter : IMessageWriter
{
    public void Write(string message)
    {
        Console.WriteLine(message);
    }
}

public static class MessageWriterExtensions
{
    public static IServiceCollection AddMessageWriter(this IServiceCollection services)
    {
        services.AddSingleton<IMessageWriter, MessageWriter>();
        return services;
    }
}

  • The service instance can be accessed in razor pages or any middleware from constructor. In this example the IMessageWriter service instance can be accessed from Index page as follows
// Pages/Index.cshtml.cs
using DIWebAppDemo.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace DIWebAppDemo.Pages;

public class IndexModel(IMessageWriter writer) : PageModel
{
    public IActionResult OnGet()
    {
        writer.Write("Index page reached");
        return Page();
    }
}

References

Comments

Popular posts from this blog

ClaimsPrincipal, ClaimsIdentity, Claims explained in dotnet authentication

Manage packages in dotnet applications

Implicit usings, top level statements, file scoped namespaces in dotnet