Trong các ứng dụng hiện đại trên nền tảng .NET, việc áp dụng nguyên lý Inversion of Control (IoC) thông qua cơ chế Dependency Injection (DI) giúp tăng tính linh hoạt, khả năng kiểm thử và khả năng bảo trì mã nguồn. Từ phiên bản .NET 5 trở đi, Microsoft đã tích hợp sẵn một container DI nhẹ và hiệu quả trong gói Microsoft.Extensions.DependencyInjection, thay thế phần lớn nhu cầu sử dụng thư viện bên thứ ba như Autofac hay Ninject — đặc biệt khi phát triển trên .NET 8 hoặc mới hơn.
Thiết lập dự án cơ bản
Tạo một dự án Console App (.NET 8) với cấu hình sau:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
</ItemGroup>
</Project>Định nghĩa hợp đồng và triển khai
Tạo giao diện IAlertService để mô phỏng một dịch vụ thông báo:
public interface IAlertService
{
void Notify(string content);
}Triển khai lớp ConsoleAlertService thực hiện giao diện trên:
public class ConsoleAlertService : IAlertService
{
public void Notify(string content)
{
Console.WriteLine($"[ALERT] {DateTime.Now:HH:mm:ss} — {content}");
}
}Dịch vụ phụ thuộc và cách tiêm vào
Tạo lớp NotificationRunner yêu cầu IAlertService thông qua constructor:
public class NotificationRunner
{
private readonly IAlertService _alert;
public NotificationRunner(IAlertService alert)
{
_alert = alert ?? throw new ArgumentNullException(nameof(alert));
Console.WriteLine("NotificationRunner được khởi tạo với dịch vụ cảnh báo.");
}
public void Trigger()
{
_alert.Notify("Chạy tác vụ định kỳ thành công!");
}
}Cấu hình và sử dụng DI trong Host
Trong Program.cs, sử dụng HostApplicationBuilder để đăng ký dịch vụ và truy xuất chúng trong phạm vi scope:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
var builder = Host.CreateApplicationBuilder(args);
// Đăng ký các dịch vụ theo lifetime phù hợp
builder.Services.AddTransient<NotificationRunner>();
builder.Services.AddSingleton();
var host = builder.Build();
// Thực thi logic trong scope riêng
using var scope = host.Services.CreateScope();
var runner = scope.ServiceProvider.GetRequiredService<NotificationRunner>();
runner.Trigger();
host.WaitForShutdown(); Giải thích chu kỳ sống dịch vụ
- Transient: Mỗi lần gọi
GetRequiredService<T>()sẽ trả về một thể hiện mới. - Scoped: Một thể hiện duy nhất trong mỗi scope (ví dụ: một request HTTP hoặc một block
using IServiceScope). - Singleton: Chỉ một thể hiện tồn tại suốt vòng đời ứng dụng — được chia sẻ giữa tất cả các scope.
Lưu ý rằng việc đăng ký không chỉ giới hạn ở kiểu cụ thể — bạn có thể dùng ServiceDescriptor để cấu hình nâng cao, ví dụ như đăng ký factory method hoặc xử lý điều kiện.