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. 您應該嘗試維護控制器與檢視之間的 Separation of Concerns (關注點分離)。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.csConfigureServices 中,註冊此服務以進行相依性插入: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 方法轉譯這類表單時,需要控制器要求所有這些選項集的資料存取服務,然後將已繫結的每個選項集填入模型或 ViewBag 中。Rendering 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 listing Html、Component、StatsService 和 URL 欄位上的 Intellisense 操作功能表

如您所見,預設欄位包括 HtmlComponentUrl (以及我們插入的 StatsService)。As you can see, the default fields include Html, Component, and Url (as well as the StatsService that we injected). 例如,如果您要將預設 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