在 ASP.NET Core 中将依赖项注入到视图Dependency injection into views in ASP.NET Core

作者:Steve SmithBy Steve Smith

ASP.NET Core 支持将依赖关系注入到视图。ASP.NET Core supports dependency injection into views. 这对于视图特定服务很有用,例如仅为填充视图元素所需的本地化或数据。This can be useful for view-specific services, such as localization or data required only for populating view elements. 应尽量在控制器和视图之间保持问题分离You should try to maintain separation of concerns between your controllers and views. 视图显示的大部分数据应该从控制器传入。Most of the data your views display should be passed in from the controller.

查看或下载示例代码如何下载View or download sample code (how to download)

配置注入Configuration injection

appsettings.json** 值可以直接注入到视图。appsettings.json values can be injected directly into a view.

appsettings.json** 文件示例:Example of an appsettings.json file:

{
   "root": {
      "parent": {
         "child": "myvalue"
      }
   }
}

@inject 的语法:@inject <type> <name>The syntax for @inject: @inject <type> <name>

使用 @inject 的示例:An example using @inject:

@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration
@{
   string myValue = Configuration["root:parent:child"];
   ...
}

服务注入Service injection

可以使用 @inject 指令将服务注入到视图。A service can be injected into a view using the @inject directive. 可以将 @inject 视为向视图添加属性,然后使用 DI 填充属性。You can think of @inject as adding a property to the view, and populating the property using 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 实例的列表,以及显示总体统计信息的摘要。This view displays a list of ToDoItem instances, along with a summary showing overall statistics. 摘要从已注入的 StatisticsService 中填充。The summary is populated from the injected StatisticsService. 在 Startup.cs 的 ConfigureServices 中为依赖关系注入注册此服务**:This service is registered for dependency injection in ConfigureServices in Startup.cs:

// 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 实例集并执行某些计算:The StatisticsService performs some calculations on the set of ToDoItem instances, which it accesses via a repository:

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);
        }
    }
}

示例存储库使用内存中集合。The sample repository uses an in-memory collection. 建议不将上示实现(对内存中的所有数据进行操作)用于远程访问的大型数据集。The implementation shown above (which operates on all of the data in memory) isn't recommended for large, remotely accessed data sets.

该示例显示绑定到视图的模型数据以及注入到视图中的服务:The sample displays data from the model bound to the view and the service injected into the view:

列出项总数、已完成项、平均优先级以及任务列表(具有其优先级别和表示完成的布尔值)的“To Do”视图。

填充查找数据Populating Lookup Data

视图注入可用于填充 UI 元素(如下拉列表)中的选项。View injection can be useful to populate options in UI elements, such as dropdown lists. 请考虑这样的用户个人资料窗体,其中包含用于指定性别、状态和其他首选项的选项。Consider a user profile form that includes options for specifying gender, state, and other preferences. 使用标准 MVC 方法呈现这样的窗体,需让控制器为每组选项请求数据访问服务,然后用要绑定的每组选项填充模型或 ViewBagRendering such a form using a standard MVC approach would require the controller to request data access services for each of these sets of options, and then populate a model or ViewBag with each set of options to be bound.

另一种方法是将服务直接注入视图以获取选项。An alternative approach injects services directly into the view to obtain the options. 这最大限度地减少了控制器所需的代码量,将此视图元素构造逻辑移入视图本身。This minimizes the amount of code required by the controller, moving this view element construction logic into the view itself. 显示个人资料编辑窗体的控制器操作只需要传递个人资料实例的窗体:The controller action to display a profile editing form only needs to pass the form the profile instance:

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 窗体包括三个属性的下拉列表:The HTML form used to update these preferences includes dropdown lists for three of the properties:

用窗体更新个人资料视图,可输入姓名、性别、状态和喜爱的颜色。

这些列表由已注入视图的服务填充:These lists are populated by a service that has been injected into the 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 是 UI 级别的服务,旨在准确提供此窗体所需的数据:The ProfileOptionsService is a UI-level service designed to provide just the data needed for this form:

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 中注册通过依赖项注入请求的类型。Don't forget to register types you request through dependency injection in Startup.ConfigureServices. 注销的类型将在运行时引发异常,因为服务提供程序通过 GetRequiredService 接受内部查询。An unregistered type throws an exception at runtime because the service provider is internally queried via GetRequiredService.

替代服务Overriding Services

除了注入新的服务之外,此方法也可用于替代以前在页面上注入的服务。In addition to injecting new services, this technique can also be used to override previously injected services on a page. 下图显示了第一个示例中使用的页面上的所有可用字段:The figure below shows all of the fields available on the page used in the first example:

类型化 @ symbol 列表 Html 上的 IntelliSense 上下文菜单, 组件, StatsService, 以及 Url 字段

如你所见,包括默认字段 HtmlComponentUrl(以及我们注入的 StatsService)。As you can see, the default fields include Html, Component, and Url (as well as the StatsService that we injected). 如果想用自己的 HTML 帮助程序替换默认的 HTML 帮助程序,可使用 @inject 轻松完成:If for instance you wanted to replace the default HTML Helpers with your own, you could easily do so using @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>

如果想扩展现有的服务,用自己的方法继承或包装现有的实现时,只需使用此方法。If you want to extend existing services, you can simply use this technique while inheriting from or wrapping the existing implementation with your own.

另请参阅See Also