ASP.NET Core 檢視中的相依性插入

ASP.NET Core 支援檢視中的相依性插入。 這可用於檢視特定服務,例如僅適用於填入檢視項目所需的當地語系化或資料。 檢視所顯示的大部分資料都應該從控制器傳入。

檢視或下載範例程式碼 \(英文\) (如何下載)

插入組態

設定檔中的值,例如 appsettings.jsonappsettings.Development.json,可以插入檢視中。 考慮來自範例程式碼appsettings.Development.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "MyRoot": {
    "MyParent": {
      "MyChildName": "Joe"
    }
  }
}

下列標記會在 Razor 網頁檢視中顯示設定值:

@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>

下列標記會在 MVC 檢視中顯示設定值:

@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>

如需詳細資訊,請參閱 ASP.NET Core 中的設定

插入服務

使用 @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 中填入。 此服務已在 Program.csConfigureServices 中註冊相依性插入:

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 會對一組 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);
        }
    }
}

範例存放庫會使用記憶體內部集合。 記憶體內部實作不應該用於大型、遠端存取的資料集。

此範例所顯示的資料來自繫結至檢視的模型,以及插入至檢視的服務:

To Do view listing total items, completed items, average priority, and a list of tasks with their priority levels and boolean values indicating completion.

填入查閱資料

檢視插入可以用來填入 UI 項目中的選項,例如下拉式清單。 請考慮使用者設定檔表單,其中包含指定性別、狀態和其他喜好設定的選項。 使用標準方法轉譯這類表單可能需要控制器或 Razor 頁面,才能:

  • 針對每組選項要求資料存取服務。
  • 使用要繫結的每組選項填入模型或 ViewBag

另一種方法會將服務直接插入至檢視,以取得選項。 這會將控制器或 Razor 頁面所需的程式碼數量降到最低,並將此檢視元素建構邏輯移至檢視本身。 要顯示設定檔編輯表單的控制器動作或 Razor 頁面,只需要將設定檔執行個體傳遞給表單:

using Microsoft.AspNetCore.Mvc;
using ViewInjectSample.Model;

namespace ViewInjectSample.Controllers;

public class ProfileController : Controller
{
    public IActionResult Index()
    {
        // A real app would up profile based on the user.
        var profile = new Profile()
        {
            Name = "Rick",
            FavColor = "Blue",
            Gender = "Male",
            State = new State("Ohio","OH")
        };
        return View(profile);
    }
}

用來更新偏好設定的 HTML 表單包含三個屬性的下拉式清單:

Update Profile view with a form allowing the entry of name, gender, state, and favorite Color.

這些清單會填入已插入至檢視的服務:

@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 層級服務:

namespace ViewInjectSample.Model.Services;

public class ProfileOptionsService
{
    public List<string> ListGenders()
    {
        // Basic sample
        return new List<string>() {"Female", "Male"};
    }

    public List<State> ListStates()
    {
        // Add a few states
        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" };
    }
}

請注意,未註冊的類型會在執行階段擲回例外狀況,因為服務提供者是透過 GetRequiredService 進行內部查詢。

覆寫服務

除了插入新的服務之外,這項技術可以用來覆寫頁面上先前插入的服務。 下圖顯示第一個範例中所使用頁面上的所有可用欄位:

Intellisense contextual menu on a typed @ symbol listing Html, Component, StatsService, and Url fields

預設欄位包括 HtmlComponentUrl。 若要以自訂版本取代預設 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>

另請參閱

ASP.NET Core 支援檢視中的相依性插入。 這可用於檢視特定服務,例如僅適用於填入檢視項目所需的當地語系化或資料。 您應該嘗試維護控制器與檢視之間的 Separation of Concerns (關注點分離)。 您檢視所顯示的大部分資料應該都是從控制器傳入。

檢視或下載範例程式碼 \(英文\) (如何下載)

插入組態

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"];
   ...
}

插入服務

使用 @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.csConfigureServices 中註冊相依性插入:

public class Startup
{
    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();

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

範例存放庫會使用記憶體內部集合。 上面顯示的實作 (作用於記憶體中的所有資料) 不建議用於大型可從遠端存取的資料集。

此範例所顯示的資料來自繫結至檢視的模型,以及插入至檢視的服務:

To Do view listing total items, completed items, average priority, and a list of tasks with their priority levels and boolean values indicating completion.

填入查閱資料

檢視插入可以用來填入 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 表單包括三個屬性的下拉式清單:

Update Profile view with a form allowing the entry of name, gender, state, and favorite Color.

這些清單會填入已插入至檢視的服務:

@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 進行內部查詢。

覆寫服務

除了插入新的服務之外,這項技術也可以用來覆寫頁面上先前插入的服務。 下圖顯示第一個範例中所使用頁面上的所有可用欄位:

Intellisense contextual menu on a typed @ symbol listing Html, Component, StatsService, and Url fields

如您所見,預設欄位包括 HtmlComponentUrl (以及我們插入的 StatsService)。 例如,如果您要將預設 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>

如果您想要擴充現有服務,則只需要在繼承自現有實作或自行包裝現有實作時使用這項技術。

另請參閱