Dependency Injection vào Controllers trong ASP.NET Core
Bởi Rick Anderson
Các controllers trong ASP.NET Core MVC yêu cầu dependencies (phụ thuộc) một cách tường minh qua constructors. ASP.NET Core có hỗ trợ tích hợp cho dependency injection (DI - tiêm phụ thuộc). DI giúp ứng dụng dễ kiểm thử và bảo trì hơn.
Constructor Injection (Tiêm qua Constructor)
Services (dịch vụ) được thêm vào như tham số constructor, và runtime (môi trường chạy) resolves (giải quyết) service từ service container (vùng chứa dịch vụ). Services thường được định nghĩa qua interfaces (giao diện). Ví dụ, xét ứng dụng cần biết thời gian hiện tại. Interface sau hiển thị service IDateTime:
public interface IDateTime
{
DateTime Now { get; }
}Code sau implement (triển khai) interface IDateTime:
public class SystemDateTime : IDateTime
{
public DateTime Now
{
get { return DateTime.Now; }
}
}Thêm service vào service container:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IDateTime, SystemDateTime>();
services.AddControllersWithViews();
}Xem thêm về AddSingleton tại DI service lifetimes (vòng đời service DI).
Code sau hiển thị lời chào tới người dùng dựa trên thời điểm trong ngày:
public class HomeController : Controller
{
private readonly IDateTime _dateTime;
public HomeController(IDateTime dateTime)
{
_dateTime = dateTime;
}
public IActionResult Index()
{
var serverTime = _dateTime.Now;
if (serverTime.Hour < 12)
{
ViewData["Message"] = "It's morning here - Good Morning!";
}
else if (serverTime.Hour < 17)
{
ViewData["Message"] = "It's afternoon here - Good Afternoon!";
}
else
{
ViewData["Message"] = "It's evening here - Good Evening!";
}
return View();
}Chạy ứng dụng sẽ hiển thị thông điệp dựa trên thời gian.
Action Injection với FromServices
Thuộc tính FromServicesAttribute cho phép tiêm service trực tiếp vào action method mà không cần constructor injection:
public IActionResult About([FromServices] IDateTime dateTime)
{
return Content( $"Current server time: {dateTime.Now}");
}Action Injection với FromKeyedServices
Code sau cho thấy cách truy cập keyed services (dịch vụ có khóa) từ DI container bằng thuộc tính [FromKeyedServices]:
using Microsoft.AspNetCore.Mvc;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddKeyedSingleton<ICache, BigCache>("big");
builder.Services.AddKeyedSingleton<ICache, SmallCache>("small");
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run();
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.";
}
[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 từ Controller
Truy cập settings (cài đặt) của ứng dụng hoặc cấu hình từ trong controller là mẫu phổ biến. Options pattern (mẫu tùy chọn) được mô tả trong Options pattern in ASP.NET Core là cách tiếp cận ưu tiên để quản lý settings. Nhìn chung, không nên tiêm trực tiếp IConfiguration vào controller.
Tạo class đại diện cho các options. Ví dụ:
public class SampleWebSettings
{
public string Title { get; set; }
public int Updates { get; set; }
}Thêm configuration class vào services collection:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IDateTime, SystemDateTime>();
services.Configure<SampleWebSettings>(Configuration);
services.AddControllersWithViews();
}Cấu hình ứng dụng đọc settings từ file JSON:
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>();
});
}Code sau yêu cầu settings IOptions<SampleWebSettings> từ service container và dùng chúng trong phương thức Index:
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 (Controllers như là Services)
Theo mặc định, ASP.NET Core không đăng ký controllers là services trong DI container. Runtime dùng DefaultControllerActivator để tạo controller instances và resolve services từ DI container cho các tham số constructor, nhưng bản thân controller không được resolve từ container.
Gọi AddControllersAsServices đăng ký tất cả controllers là services trong DI container:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllersWithViews().AddControllersAsServices();
Đăng ký controllers là services cho phép:
- Chặn quá trình tạo controller bằng
IControllerActivatortùy chỉnh. - Dùng bất kỳ DI lifetime management (quản lý vòng đời DI) nào cho controllers.
- Tiêm services vào controllers dùng bất kỳ constructor đã đăng ký nào, vì DI container chọn constructor.
Lưu ý: Cấu hình ApplicationPartManager trước khi gọi AddControllersAsServices. Xem Share controllers, views, Razor Pages and more with Application Parts in ASP.NET Core để biết chi tiết.
Tài nguyên bổ sung
- Xem Test controller logic in ASP.NET Core để học cách làm code dễ kiểm thử hơn bằng cách yêu cầu dependencies tường minh trong controllers.
- Thay thế default dependency injection container bằng third party implementation.