Inserción de dependencias en vistas de ASP.NET CoreDependency injection into views in ASP.NET Core

Por Steve SmithBy Steve Smith

ASP.NET Core admite la inserción de dependencias en vistas.ASP.NET Core supports dependency injection into views. Esto puede ser útil para servicios específicos de vistas, como la localización o los datos necesarios solamente para rellenar los elementos de vistas.This can be useful for view-specific services, such as localization or data required only for populating view elements. Debe intentar mantener la separación de intereses entre los controladores y las vistas.You should try to maintain separation of concerns between your controllers and views. La mayoría de los datos que muestran las vistas deben pasarse desde el controlador.Most of the data your views display should be passed in from the controller.

Vea o descargue el código de ejemplo (cómo descargarlo)View or download sample code (how to download)

Inserción de configuraciónConfiguration injection

Los valores appSettings.JSON se pueden insertar directamente en una vista.appsettings.json values can be injected directly into a view.

Ejemplo de un archivo appsettings.json:Example of an appsettings.json file:

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

La sintaxis de @inject: @inject <type> <name>The syntax for @inject: @inject <type> <name>

Un ejemplo que usa @inject:An example using @inject:

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

Inserción de un servicioService injection

Un servicio puede insertarse en una vista mediante la directiva @inject.A service can be injected into a view using the @inject directive. Puede pensar que @inject es como si agregara una propiedad a la vista y rellenara la propiedad mediante 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>

Esta vista muestra una lista de instancias ToDoItem, junto con un resumen de estadísticas generales.This view displays a list of ToDoItem instances, along with a summary showing overall statistics. El resumen se rellena a partir de StatisticsService insertado.The summary is populated from the injected StatisticsService. Este servicio está registrado para la inserción de dependencias en ConfigureServices en 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 realiza algunos cálculos en el conjunto de instancias de ToDoItem, al que se accede a través de un repositorio: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);
        }
    }
}

El repositorio de ejemplo usa una colección en memoria.The sample repository uses an in-memory collection. La implementación que se muestra arriba (que funciona en todos los datos en memoria) no se recomienda para conjuntos de datos grandes, con acceso de forma remota.The implementation shown above (which operates on all of the data in memory) isn't recommended for large, remotely accessed data sets.

El ejemplo muestra los datos del modelo enlazado a la vista y del servicio que se inserta en la vista:The sample displays data from the model bound to the view and the service injected into the view:

Vista de tareas pendientes que enumera los elementos totales, los elementos completados, la prioridad media y una lista de tareas con sus niveles de propiedad y valores booleanos que indican si se han completado.

Rellenar datos de búsquedaPopulating Lookup Data

La inserción de vistas puede ser útil para rellenar opciones en elementos de interfaz de usuario, como por ejemplo, listas desplegables.View injection can be useful to populate options in UI elements, such as dropdown lists. Imagine un formulario de perfil de usuario que incluye opciones para especificar el sexo, el estado y otras preferencias.Consider a user profile form that includes options for specifying gender, state, and other preferences. Para representar este tipo de formulario mediante un enfoque MVC estándar, necesitaría que el controlador solicitara servicios de acceso a datos para cada uno de estos conjuntos de opciones y, después, rellenar un modelo o ViewBag con cada conjunto de opciones para enlazar.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.

Un enfoque alternativo consiste en insertar servicios directamente en la vista para obtener las opciones.An alternative approach injects services directly into the view to obtain the options. Esto reduce la cantidad de código necesario para el controlador, ya que mueve esta lógica de construcción del elemento de vista a la propia vista.This minimizes the amount of code required by the controller, moving this view element construction logic into the view itself. La acción del controlador para mostrar un formulario de edición de perfil solamente necesita pasar el formulario de la instancia de perfil: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);
        }
    }
}

El formulario HTML que se utilizó para actualizar estas preferencias incluye listas desplegables para tres de las propiedades:The HTML form used to update these preferences includes dropdown lists for three of the properties:

Vista de actualización de perfil, con un formulario que permite la entrada del nombre, el género, el estado y el color favorito.

Estas listas se rellenan mediante un servicio que se ha insertado en la vista: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 es un servicio de nivel de interfaz de usuario diseñado para proporcionar solo los datos necesarios para este formulario: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" };
        }
    }
}

Importante

No olvide registrar los tipos que solicite a través de la inserción de dependencias en Startup.ConfigureServices.Don't forget to register types you request through dependency injection in Startup.ConfigureServices. Un tipo no registrado produce una excepción en tiempo de ejecución porque el proveedor de servicios se consulta internamente a través de GetRequiredService.An unregistered type throws an exception at runtime because the service provider is internally queried via GetRequiredService.

Reemplazar serviciosOverriding Services

Además de insertar nuevos servicios, esta técnica también puede usarse para reemplazar servicios previamente insertados en una página.In addition to injecting new services, this technique can also be used to override previously injected services on a page. En la imagen de abajo se muestran todos los campos disponibles en la página usada en el primer ejemplo:The figure below shows all of the fields available on the page used in the first example:

Menú contextual de IntelliSense de un símbolo @ de tipo que enumera los campos Html, componente, StatsService y URL

Como puede ver, los campos predeterminados incluyen Html, Component y Url (además de StatsService que hemos insertado).As you can see, the default fields include Html, Component, and Url (as well as the StatsService that we injected). Si, por ejemplo, quisiera reemplazar los asistentes de HTML predeterminadas con las suyas propias, puede hacerlo fácilmente mediante @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>

Si quiere ampliar los servicios existentes, simplemente puede usar esta técnica al heredar o encapsular la implementación existente con la suya propia.If you want to extend existing services, you can simply use this technique while inheriting from or wrapping the existing implementation with your own.

Otras referenciasSee Also