原文:Dependency injection into views
作者:Steve Smith
翻譯:姚阿勇(Dr.Yao)
校對:孟帥洋(書緣)
ASP.NET Core 支持在視圖中使用 依賴注入 。這將有助於提供視圖專用的服務,比如本地化或者僅用於填充視圖元素的數據。你應該盡量保持控制器和視圖間的關注點分離(separation of concerns)。你的視圖所顯示的大部分數據應該從控制器傳入。
章節:
一個簡單的示例
你可以使用 @inject
指令將服務注入到視圖中。可以把 @inject
看作是給你的視圖增加一個屬性,然后利用依賴注入給這個屬性賦值。
@inject
的語法:
@inject <type> <name>
一個使用 @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>
這個視圖顯示了一個 ToDoItem
實例的列表和統計概覽。概覽信息是由注入的服務 StatisticsService
填入的。這個服務是在 Startup.cs 里的 ConfigureServices
方法中被注冊到依賴注入項的。
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddTransient<IToDoItemRepository, ToDoItemRepository>();
services.AddTransient<StatisticsService>();
services.AddTransient<ProfileOptionsService>();
StatisticsService
對通過倉儲讀取到的 ToDoItem
數據集執行一些計算。
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);
}
}
}
示例中的倉儲使用的是 in-memory 集合。上面的實現方法(所有對內存數據的操作)不推薦用於大型、遠程存儲的數據集。
這個示例通過綁定到視圖的模型以及注入到視圖的服務來顯示數據:
填充查找數據
視圖注入有助於填充 UI 元素中的選項數據,例如下拉列表。設想一個包括性別、州以及其他選項的用戶資料表單。如果通過標准的 MVC 方式渲染這樣一個表單,需要控制器為每一組選項都請求數據訪問服務,然后將每一組待綁定的選項填充到模型或 ViewBag
中。
另一種方法則直接將服務注入到視圖中以獲取這些選項數據。這種方法將控制器需要的代碼量減到了最少,把構造視圖元素的邏輯移到視圖本身中去。用來顯示資料編輯表單的控制器 Action 只需要把用戶資料實例傳給表格就可以了(而不需要傳各種選項數據,譯注):
using Microsoft.AspNetCore.Mvc;
using ViewInjectSample.Model;
namespace ViewInjectSample.Controllers
{
public class ProfileController : Controller
{
[Route("Profile")]
public IActionResult Index()
{
// TODO: look up profile based on logged-in user
var profile = new Profile()
{
Name = "Steve",
FavColor = "Blue",
Gender = "Male",
State = new State("Ohio","OH")
};
return View(profile);
}
}
}
用來編輯這些選項的 HTML 表單包含選項中的三個下拉列表:
這些列表是由注入到視圖的一個服務填充的:
@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
是一個 UI 層的服務,旨在為這個表單提供所需數據 :
using System.Collections.Generic;
namespace ViewInjectSample.Model.Services
{
public class ProfileOptionsService
{
public List<string> ListGenders()
{
// keeping this simple
return new List<string>() {"Female", "Male"};
}
public List<State> ListStates()
{
// a few states from USA
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" };
}
}
}
提示
不要忘記在 Startup.cs 的ConfigureServices
方法中把你想要通過依賴注入請求的類型注冊一下。
重寫服務
除了注入新服務之外,這種技術也可以用來重寫之前已經在頁面上注入的服務。下圖顯示了在第一個例子的頁面上可用的所有字段:
如你所見,默認的字段有 Html
, Component
和 Url
(同樣還有我們注入的 StatsService
)。假如你想要把默認的 HTML Helpers 替換成你自己的,你可以利用 @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>
如果你想要擴展已有服務,你只需要在使用這種替換技術的同時,讓你自己的服務繼承或封裝已有實現。
參考
- Simon Timms 的博文: Getting Lookup Data Into Your View