Dependency Injection vào Views trong ASP.NET Core
ASP.NET Core hỗ trợ dependency injection (DI - tiêm phụ thuộc) vào views. Điều này có thể hữu ích cho các view-specific services (dịch vụ riêng của view), chẳng hạn như localization (bản địa hóa) hoặc dữ liệu chỉ cần để điền vào các phần tử view. Hầu hết dữ liệu mà views hiển thị nên được truyền từ controller.
Configuration Injection (Tiêm cấu hình)
Các giá trị trong file settings như appsettings.json và appsettings.Development.json có thể được tiêm vào view. Xét file appsettings.Development.json từ mã mẫu:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"MyRoot": {
"MyParent": {
"MyChildName": "Joe"
}
}
}Markup sau hiển thị giá trị cấu hình trong Razor Pages view:
@page
@model PrivacyModel
@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration
@{
ViewData["Title"] = "Privacy RP";
}
<h1>@ViewData["Title"]</h1>
<p>PR Privacy</p>
<h2>
MyRoot:MyParent:MyChildName: @Configuration["MyRoot:MyParent:MyChildName"]
</h2>Markup sau hiển thị giá trị cấu hình trong MVC view:
@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration
@{
ViewData["Title"] = "Privacy MVC";
}
<h1>@ViewData["Title"]</h1>
<p>MVC Use this page to detail your site's privacy policy.</p>
<h2>
MyRoot:MyParent:MyChildName: @Configuration["MyRoot:MyParent:MyChildName"]
</h2>Xem thêm tại Configuration in ASP.NET Core.
Service Injection (Tiêm dịch vụ)
Một service có thể được tiêm vào view bằng chỉ thị @inject.
@using System.Threading.Tasks
@using ViewInjectSample.Model
@using ViewInjectSample.Model.Services
@model IEnumerable<ToDoItem>
@inject StatisticsService StatsService
<!DOCTYPE html>
<html>
<head>
<title>To Do Items</title>
</head>
<body>
<div>
<h1>To Do Items</h1>
<ul>
<li>Total Items: @StatsService.GetCount()</li>
<li>Completed: @StatsService.GetCompletedCount()</li>
<li>Avg. Priority: @StatsService.GetAveragePriority()</li>
</ul>
<table>
<tr>
<th>Name</th>
<th>Priority</th>
<th>Is Done?</th>
</tr>
@foreach (var item in Model)
{
<tr>
<td>@item.Name</td>
<td>@item.Priority</td>
<td>@item.IsDone</td>
</tr>
}
</table>
</div>
</body>
</html>View này hiển thị danh sách các ToDoItem instances cùng phần tóm tắt thống kê tổng thể. Phần tóm tắt được điền từ StatisticsService được tiêm vào. Service này được đăng ký cho dependency injection trong ConfigureServices trong Program.cs:
using ViewInjectSample.Helpers;
using ViewInjectSample.Infrastructure;
using ViewInjectSample.Interfaces;
using ViewInjectSample.Model.Services;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
builder.Services.AddTransient<IToDoItemRepository, ToDoItemRepository>();
builder.Services.AddTransient<StatisticsService>();
builder.Services.AddTransient<ProfileOptionsService>();
builder.Services.AddTransient<MyHtmlHelper>();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.MapRazorPages();
app.MapDefaultControllerRoute();
app.Run();StatisticsService thực hiện một số tính toán trên tập hợp ToDoItem instances, mà nó truy cập qua repository (kho lưu trữ):
using System.Linq;
using ViewInjectSample.Interfaces;
namespace ViewInjectSample.Model.Services
{
public class StatisticsService
{
private readonly IToDoItemRepository _toDoItemRepository;
public StatisticsService(IToDoItemRepository toDoItemRepository)
{
_toDoItemRepository = toDoItemRepository;
}
public int GetCount()
{
return _toDoItemRepository.List().Count();
}
public int GetCompletedCount()
{
return _toDoItemRepository.List().Count(x => x.IsDone);
}
public double GetAveragePriority()
{
if (_toDoItemRepository.List().Count() == 0)
{
return 0.0;
}
return _toDoItemRepository.List().Average(x => x.Priority);
}
}
}Repository mẫu dùng in-memory collection (bộ sưu tập trong bộ nhớ). Không nên dùng implementation in-memory cho các data sets (tập dữ liệu) lớn, được truy cập từ xa.
Mẫu hiển thị dữ liệu từ model được bind vào view và service được tiêm vào view.
Điền dữ liệu Lookup (Dữ liệu tra cứu)
View injection có thể hữu ích để điền options (tùy chọn) trong các phần tử UI như dropdown lists (danh sách thả xuống). Xét form profile người dùng có options để chỉ định giới tính, tiểu bang và các tùy chọn khác. Render form như vậy theo cách MVC chuẩn sẽ yêu cầu controller hoặc Razor Page:
- Yêu cầu data access services cho mỗi tập options.
- Điền vào model hoặc
ViewBagmỗi tập options để bind.
Một cách tiếp cận khác là tiêm services trực tiếp vào view để lấy options. Điều này giảm thiểu lượng code cần thiết trong controller hoặc Razor Page, chuyển logic xây dựng phần tử view này vào trong view. Controller action hoặc Razor Page hiển thị form chỉnh sửa profile chỉ cần truyền form instance profile:
using Microsoft.AspNetCore.Mvc;
using ViewInjectSample.Model;
namespace ViewInjectSample.Controllers;
public class ProfileController : Controller
{
public IActionResult Index()
{
// Ứng dụng thực tế sẽ lấy profile dựa trên người dùng.
var profile = new Profile()
{
Name = "Rick",
FavColor = "Blue",
Gender = "Male",
State = new State("Ohio","OH")
};
return View(profile);
}
}HTML form dùng để cập nhật các tùy chọn bao gồm dropdown lists cho ba thuộc tính.
Các danh sách này được điền bởi service được tiêm vào view:
@using System.Threading.Tasks
@using ViewInjectSample.Model.Services
@model ViewInjectSample.Model.Profile
@inject ProfileOptionsService Options
<!DOCTYPE html>
<html>
<head>
<title>Update Profile</title>
</head>
<body>
<div>
<h1>Update Profile</h1>
Name: @Html.TextBoxFor(m => m.Name)
<br/>
Gender: @Html.DropDownList("Gender",
Options.ListGenders().Select(g =>
new SelectListItem() { Text = g, Value = g }))
<br/>
State: @Html.DropDownListFor(m => m.State!.Code,
Options.ListStates().Select(s =>
new SelectListItem() { Text = s.Name, Value = s.Code}))
<br />
Fav. Color: @Html.DropDownList("FavColor",
Options.ListColors().Select(c =>
new SelectListItem() { Text = c, Value = c }))
</div>
</body>
</html>ProfileOptionsService là UI-level service được thiết kế để cung cấp chỉ dữ liệu cần thiết cho form này:
namespace ViewInjectSample.Model.Services;
public class ProfileOptionsService
{
public List<string> ListGenders()
{
// Mẫu cơ bản
return new List<string>() {"Female", "Male"};
}
public List<State> ListStates()
{
// Thêm một vài tiểu bang
return new List<State>()
{
new State("Alabama", "AL"),
new State("Alaska", "AK"),
new State("Ohio", "OH")
};
}
public List<string> ListColors()
{
return new List<string>() { "Blue","Green","Red","Yellow" };
}
}Lưu ý rằng type chưa đăng ký sẽ throw exception (ném ngoại lệ) lúc runtime vì service provider được truy vấn nội bộ qua GetRequiredService.
Ghi đè Services (Override Services)
Ngoài việc tiêm services mới, kỹ thuật này có thể dùng để override (ghi đè) các services đã tiêm trước đó trên trang. Hình dưới đây cho thấy tất cả các trường có sẵn trên trang dùng trong ví dụ đầu tiên.
Các trường mặc định bao gồm Html, Component, và Url. Để thay thế các HTML Helpers mặc định bằng phiên bản tùy chỉnh, dùng @inject:
@using System.Threading.Tasks
@using ViewInjectSample.Helpers
@inject MyHtmlHelper Html
<!DOCTYPE html>
<html>
<head>
<title>My Helper</title>
</head>
<body>
<div>
Test: @Html.Value
</div>
</body>
</html>Nếu muốn mở rộng các services hiện có, chỉ cần dùng kỹ thuật này trong khi kế thừa hoặc bọc implementation hiện tại bằng implementation của bạn.
Xem thêm
- Blog Simon Timms: Getting Lookup Data Into Your View