Внедрение зависимостей в представления в ASP.NET CoreDependency injection into views in ASP.NET Core

Автор: Стив Смит (Steve Smith)By 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 добавляет в представление свойство и заполняет это свойство с помощью внедрения зависимостей.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. Эта служба регистрируется для внедрения зависимости в ConfigureServices в файле Startup.cs: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:

Список дел к выполнению, показывающий общее количество пунктов, число выполненных пунктов, средний показатель приоритета, а также список задач с их уровнями приоритета и логическими значениями, характеризующими выполнение.

Заполнение данных подстановкиPopulating Lookup Data

Внедрение в представление можно использовать для заполнения параметров в элементах пользовательского интерфейса, например в раскрывающихся списках.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 — это работающая на уровне пользовательского интерфейса служба, которая предоставляет именно те данные, которые нужны в этой форме: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:

Контекстное меню IntelliSense к введенному символу @ с полями Html, Component, StatsService и Url

Как видно, полями по умолчанию являются Html, Component и Url (а также внедренное нами поле 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