Внедрение зависимостей в представления в ASP.NET Core
ASP.NET Core поддерживает внедрение зависимостей в представления. Это может быть полезно для связанных с представлениями служб (например, локализации) или данных, необходимых только для заполнения элементов представления. Большинство представлений данных должно передаваться из контроллера.
Просмотреть или скачать образец кода (описание загрузки)
Внедрение конфигурации
Значения в файлах параметров, таких как appsettings.json
и appsettings.Development.json
, можно внедрить в представление. Рассмотрим appsettings.Development.json
из пример кода:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"MyRoot": {
"MyParent": {
"MyChildName": "Joe"
}
}
}
Следующая разметка отображает значение конфигурации в представлении Razor Pages:
@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
. Эта служба зарегистрирована для внедрения зависимостей в ConfigureServices
Program.cs
:
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);
}
}
}
Пример репозитория использует коллекцию в оперативной памяти. Реализация в памяти не должна использоваться для больших удаленных наборов данных.
Пример показывает данные из модели, привязанной к представлению, и из внедренной в представление службы:
Заполнение данных для поиска
Внедрение представления можно использовать для заполнения параметров в элементах пользовательского интерфейса, например, в раскрывающихся списках. Рассмотрим форму профиля пользователя, содержащую параметры для указания пола, штата и другие свойства. Для отрисовки такой формы с помощью стандартного подхода может потребоваться контроллер или 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-форма, используемая для обновления параметров, включает раскрывающийся список для трех свойств:
Эти списки заполняются службой, которая интегрирована в представление.
@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
— это работающая на уровне пользовательского интерфейса служба, которая предоставляет именно те данные, которые нужны в этой форме:
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 осуществляется внутренний запрос поставщика услуг.
Переопределение служб
Помимо внедрения новых служб, этот метод можно использовать для переопределения ранее внедренных служб на странице. На следующем рисунке показаны все доступные поля на странице, используемой в первом примере:
Поля по умолчанию включают Html
, Component
и Url
. Чтобы заменить вспомогательный элемент 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>
См. также
- Блог Саймона Тиммса (Simon Timms): Getting Lookup Data Into Your View (Внедрение данных подстановки в ваше представление)
ASP.NET Core поддерживает внедрение зависимостей в представления. Это может быть полезно для связанных с представлениями служб (например, локализации) или данных, необходимых только для заполнения элементов представления. Старайтесь сохранять разделение областей ответственности между контроллерами и представлениями. Большая часть данных, отображаемых представлениями, должна передаваться от контроллера.
Просмотреть или скачать образец кода (описание загрузки)
Внедрение конфигурации
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
. Эта служба зарегистрирована для внедрения зависимостей в ConfigureServices
и Startup.cs
.
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);
}
}
}
Пример репозитория использует коллекцию, хранящуюся в оперативной памяти. Представленная выше реализация (работающая со всеми данными в памяти) не рекомендуется для больших наборов данных с удаленным доступом.
Пример показывает данные из модели, привязанной к представлению, и из внедренной в представление службы:
Заполнение справочных данных
Инъекция в представление может быть полезна для заполнения опций в элементах пользовательского интерфейса, таких как раскрывающиеся списки. Рассмотрим форму профиля пользователя, содержащую параметры для указания пола, штата и другие свойства. Для отрисовки такой формы при использовании стандартного подхода 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
— это работающая на уровне пользовательского интерфейса служба, которая предоставляет именно те данные, которые нужны в этой форме:
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.
Переопределение служб
Помимо добавления новых служб, этот метод также позволяет переопределять ранее добавленные службы на странице. На следующем рисунке показаны все доступные поля на странице, используемой в первом примере:
Как видно, полями по умолчанию являются Html
, Component
и Url
(а также внедренное нами поле 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>
При необходимости расширить существующие сервисы, вы можете просто применять этот метод, наследуя от существующей реализации или обёртывая её в вашу собственную.
См. также
- Блог Саймона Тиммса: Внедрение данных просмотра в ваше представление
ASP.NET Core