Dependency Injection là gì?

ASP.NET Core MVC controllers request dependencies một cách explicit thông qua constructors. ASP.NET Core có built-in support cho dependency injection (DI). DI giúp ứng dụng dễ testdễ bảo trì.

Constructor Injection

Nguyên tắc cơ bản

Services được thêm như constructor parameters, và runtime resolve service từ service container. Services thường được định nghĩa dùng interfaces.

Ví dụ: Interface và Implementation

public interface IDateTime
{
    DateTime Now { get; }
}

public class SystemDateTime : IDateTime
{
    public DateTime Now => DateTime.Now;
}

Đăng ký Service

public void ConfigureServices(IServiceCollection services)
{
    // Đăng ký service với DI container
    services.AddSingleton<IDateTime, SystemDateTime>();

    services.AddControllersWithViews();
}

Controller sử dụng Constructor Injection

public class HomeController : Controller
{
    private readonly IDateTime _dateTime;

    // Service được inject tự động qua constructor
    public HomeController(IDateTime dateTime)
    {
        _dateTime = dateTime;
    }

    public IActionResult Index()
    {
        var serverTime = _dateTime.Now;
        if (serverTime.Hour < 12)
        {
            ViewData["Message"] = "Chào buổi sáng!";
        }
        else if (serverTime.Hour < 17)
        {
            ViewData["Message"] = "Chào buổi chiều!";
        }
        else
        {
            ViewData["Message"] = "Chào buổi tối!";
        }
        return View();
    }
}
Lưu ý: Services thường được đăng ký dùng interfaces để:
  • Giảm coupling giữa controller và implementation
  • Dễ thay thế implementation trong unit tests (mock)
  • Tăng tính modular của ứng dụng

Action Injection với [FromServices]

Khi nào dùng

Action injection hữu ích khi service chỉ cần cho một action duy nhất, không cần dùng ở nhiều action.
public class HomeController : Controller
{
    [FromServices]
    public IDateTime DateTime { get; set; }

    public IActionResult About()
    {
        return Content($"Thời gian hiện tại: {DateTime.Now}");
    }
}

So sánh Constructor vs Action Injection

Tiêu chíConstructor InjectionAction Injection
Phạm viTất cả actions trong controllerChỉ action được chỉ định
Tái sử dụng✅ Cao❌ Thấp
Đăng kýMột lầnMỗi action
Dùng khiNhiều actions cần serviceChỉ một action cần service

Keyed Services với [FromKeyedServices]

Giới thiệu

ASP.NET Core hỗ trợ keyed services — cho phép đăng ký nhiều implementations của cùng một interface với keys khác nhau.

Đăng ký Keyed Services

var builder = WebApplication.CreateBuilder(args);

// Đăng ký nhiều implementations với keys khác nhau
builder.Services.AddKeyedSingleton<ICache, BigCache>("big");
builder.Services.AddKeyedSingleton<ICache, SmallCache>("small");

builder.Services.AddControllers();

Định nghĩa Interface và Implementations

public interface ICache
{
    object Get(string key);
}

public class BigCache : ICache
{
    public object Get(string key) =>
        $"Resolving {key} from big cache.";
}

public class SmallCache : ICache
{
    public object Get(string key) =>
        $"Resolving {key} from small cache.";
}

Sử dụng trong Controller

[ApiController]
[Route("cache")]
public class CustomServicesApiController : Controller
{
    [HttpGet("big")]
    public ActionResult<object> GetBigCache(
        [FromKeyedServices("big")] ICache cache)
    {
        return cache.Get("data-mvc");
    }

    [HttpGet("small")]
    public ActionResult<object> GetSmallCache(
        [FromKeyedServices("small")] ICache cache)
    {
        return cache.Get("data-mvc");
    }
}

Truy cập Settings với Options Pattern

Pattern đề xuất

Truy cập app settings từ controller là pattern phổ biến. Options Pattern là cách tiếp cận được ưu tiên thay vì trực tiếp inject IConfiguration.

Bước 1: Tạo Settings Class

public class SampleWebSettings
{
    public string Title { get; set; }
    public int Updates { get; set; }
}

Bước 2: Đăng ký Configuration

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IDateTime, SystemDateTime>();

    // Đăng ký settings với Options Pattern
    services.Configure<SampleWebSettings>(Configuration);

    services.AddControllersWithViews();
}

Bước 3: Cấu hình đọc từ JSON file

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                config.AddJsonFile("samplewebsettings.json",
                    optional: false,
                    reloadOnChange: true);
            })
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

Bước 4: Sử dụng trong Controller

public class SettingsController : Controller
{
    private readonly SampleWebSettings _settings;

    public SettingsController(
        IOptions<SampleWebSettings> settingsOptions)
    {
        _settings = settingsOptions.Value;
    }

    public IActionResult Index()
    {
        ViewData["Title"] = _settings.Title;
        ViewData["Updates"] = _settings.Updates;
        return View();
    }
}

Controllers as Services

Mặc định

Theo mặc định, ASP.NET Core không đăng ký controllers như services trong DI container. Runtime dùng DefaultControllerActivator để tạo controller instances và resolve services từ DI container cho constructor parameters.

Kích hoạt Controllers as Services

builder.Services.AddControllersWithViews()
    .AddControllersAsServices();

Lợi ích

Lợi íchMô tả
Custom IControllerActivatorIntercept việc tạo controller
Lifetime ManagementDùng bất kỳ DI lifetime nào cho controllers
Multi-constructorDI container chọn constructor phù hợp
Lưu ý: Cấu hình ApplicationPartManager trước khi gọi AddControllersAsServices. Xem thêm: Share controllers, views, Razor Pages với Application Parts.

So sánh các cách Injection


Tài liệu tham khảo