在 ASP.NET Core 中将依赖项注入到视图Dependency injection into views in ASP.NET Core
本文内容
作者:Steve Smith
ASP.NET Core 支持将依赖关系注入到视图。这对于视图特定服务很有用,例如仅为填充视图元素所需的本地化或数据。应尽量在控制器和视图之间保持问题分离。视图显示的大部分数据应该从控制器传入。
配置注入Configuration injection
appsettings.json 值可以直接注入到视图。
appsettings.json 文件示例:
{
"root": {
"parent": {
"child": "myvalue"
}
}
}
@inject
的语法:@inject <type> <name>
使用 @inject
的示例:
@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration
@{
string myValue = Configuration["root:parent:child"];
...
}
服务注入Service injection
可以使用 @inject
指令将服务注入到视图。可以将 @inject
视为向视图添加属性,然后使用 DI 填充属性。
@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);
}
}
}
示例存储库使用内存中集合。建议不将上示实现(对内存中的所有数据进行操作)用于远程访问的大型数据集。
该示例显示绑定到视图的模型数据以及注入到视图中的服务:
填充查找数据Populating Lookup Data
视图注入可用于填充 UI 元素(如下拉列表)中的选项。请考虑这样的用户个人资料窗体,其中包含用于指定性别、状态和其他首选项的选项。使用标准 MVC 方法呈现这样的窗体,需让控制器为每组选项请求数据访问服务,然后用要绑定的每组选项填充模型或 ViewBag
。
另一种方法是将服务直接注入视图以获取选项。这最大限度地减少了控制器所需的代码量,将此视图元素构造逻辑移入视图本身。显示个人资料编辑窗体的控制器操作只需要传递个人资料实例的窗体:
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.ConfigureServices
中注册通过依赖项注入请求的类型。注销的类型将在运行时引发异常,因为服务提供程序通过 GetRequiredService 接受内部查询。
替代服务Overriding Services
除了注入新的服务之外,此方法也可用于替代以前在页面上注入的服务。下图显示了第一个示例中使用的页面上的所有可用字段:
如你所见,包括默认字段 Html
、Component
和 Url
(以及我们注入的 StatsService
)。如果想用自己的 HTML 帮助程序替换默认的 HTML 帮助程序,可使用 @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>
如果想扩展现有的服务,用自己的方法继承或包装现有的实现时,只需使用此方法。
另请参阅See Also
- Simon Timms 的博客:在视图中查找数据