Partilhar via


Injeção de dependência em visualizações no ASP.NET Core

O ASP.NET Core suporta de injeção de dependência em visualizações. Isso pode ser útil para serviços específicos de exibição, como localização ou dados necessários apenas para preencher elementos de exibição. A maioria das visualizações de dados exibidas deve ser passada do controlador.

Ver ou transferir código de exemplo (como baixar)

Injeção de configuração

Os valores em arquivos de configurações, como appsettings.json e appsettings.Development.json, podem ser injetados em uma exibição. Considere o appsettings.Development.json do código de exemplo :

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

A seguinte marcação exibe o valor de configuração na vista Razor Páginas.

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

A marcação a seguir exibe o valor de configuração em uma exibição 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>

Para obter mais informações, consulte Configuração no ASP.NET Core

Injeção de serviço

Um serviço pode ser injetado em uma exibição usando a diretiva @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>

Esta vista exibe uma lista de ocorrências de ToDoItem, juntamente com um resumo que apresenta estatísticas gerais. O resumo é preenchido a partir do StatisticsServiceinjetado. Este serviço está registado para injeção de dependência em ConfigureServices em 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();

O StatisticsService realiza alguns cálculos no conjunto de instâncias ToDoItem, que acessa através de um repositório:

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

O repositório de exemplo usa uma coleção na memória. Uma implementação na memória não deve ser usada para grandes conjuntos de dados acessados remotamente.

O exemplo exibe dados do modelo vinculado ao modo de exibição e o serviço injetado no modo de exibição:

Vista de

Preenchendo dados de referência

A injeção de exibição pode ser útil para preencher opções em elementos da interface do usuário, como listas suspensas. Considere um formulário de perfil de usuário que inclua opções para especificar gênero, estado e outras preferências. A renderização desse formulário usando uma abordagem padrão pode exigir que o controlador ou a Página Razor:

  • Solicite serviços de acesso a dados para cada um dos conjuntos de opções.
  • Preencha um modelo ou ViewBag com cada conjunto de opções a serem vinculadas.

Uma abordagem alternativa injeta serviços diretamente na visualização para obter as opções. Isso minimiza a quantidade de código exigida pelo controlador ou pela Razor Page, movendo essa lógica de construção de elementos para a vista em si. A ação do controlador ou Razor Página para exibir um formulário de edição de perfil só precisa passar o formulário da instância do perfil:

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

O formulário HTML usado para atualizar as preferências inclui listas suspensas para três das propriedades:

Atualizar visualização de perfil com um formulário que permite a entrada de nome, sexo, estado e cor favorita.

Essas listas são preenchidas por um serviço que foi injetado na exibição:

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

O ProfileOptionsService é um serviço de nível de interface do usuário projetado para fornecer apenas os dados necessários para este formulário:

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

Observação: Um tipo não registado lança uma exceção em tempo de execução por causa de o provedor de serviços ser consultado internamente via GetRequiredService.

Serviços de substituição

Além de injetar novos serviços, essa técnica pode ser usada para substituir serviços injetados anteriormente em uma página. A figura abaixo mostra todos os campos disponíveis na página usada no primeiro exemplo:

menu contextual do Intellisense ao digitar o símbolo @, listando os atributos Html, Componente, StatsService e Url

Os campos padrão incluem Html, Componente Url. Para substituir os Auxiliares de HTML padrão por uma versão personalizada, use @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>

Ver também

ASP.NET Core suporta a injeção de dependência em visualizações. Isso pode ser útil para serviços específicos de exibição, como localização ou dados necessários apenas para preencher elementos de exibição. Você deve tentar manter separação de preocupações entre seus controladores e visualizações. A maioria dos dados exibidos pelas visualizações deve ser passada do controlador.

Exibir ou baixar código de exemplo (como baixar)

Injeção de configuração

Os valores appsettings.json podem ser injetados diretamente numa vista.

Exemplo de um ficheiro appsettings.json:

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

A sintaxe para @inject: @inject <type> <name>

Um exemplo usando @inject:

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

Injeção de serviço

Um serviço pode ser injetado em uma exibição usando a diretiva @inject. Você pode pensar em @inject como adicionar uma propriedade à exibição e preencher a propriedade usando 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 exibição apresenta uma lista de ToDoItem ocorrências, juntamente com um resumo que mostra estatísticas gerais. O resumo é preenchido a partir do StatisticsServiceinjetado. Este serviço está registado para injeção de dependência em ConfigureServices em 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();

O StatisticsService realiza alguns cálculos no conjunto de instâncias ToDoItem, que acessa através de um repositório:

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

O repositório de exemplo usa uma coleção na memória. A implementação mostrada acima (que opera em todos os dados na memória) não é recomendada para grandes conjuntos de dados acessados remotamente.

O exemplo exibe dados do modelo vinculado ao modo de exibição e o serviço injetado no modo de exibição:

Visualização do To Do mostrando o total de itens, itens concluídos, prioridade média e uma lista de tarefas com seus níveis de prioridade e indicadores booleanos de conclusão.

Preenchendo dados de referência

A injeção de visualização pode ser útil para preencher opções em elementos da interface, como listas suspensas. Considere um formulário de perfil de usuário que inclua opções para especificar gênero, estado e outras preferências. Renderizar tal formulário usando uma abordagem MVC padrão exigiria que o controlador solicitasse serviços de acesso a dados para cada um desses conjuntos de opções e, em seguida, preenchesse um modelo ou ViewBag com cada conjunto de opções a serem vinculadas.

Uma abordagem alternativa injeta serviços diretamente na visualização para obter as opções. Isso minimiza a quantidade de código exigida pelo controlador, movendo essa lógica de construção do elemento de exibição para a própria exibição. A ação do controlador para exibir um formulário de edição de perfil só precisa passar o formulário a instância do perfil:

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

O formulário HTML usado para atualizar essas preferências inclui listas suspensas para três das propriedades:

Atualizar visualização de perfil com um formulário que permite a entrada de nome, sexo, estado e cor favorita.

Essas listas são preenchidas por um serviço que foi injetado na exibição:

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

O ProfileOptionsService é um serviço de nível de interface do usuário projetado para fornecer apenas os dados necessários para este formulário:

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

Não se esqueça de registrar os tipos solicitados por meio de injeção de dependência no Startup.ConfigureServices. Um tipo não registado lança uma exceção durante o tempo de execução porque o fornecedor de serviços é consultado internamente via GetRequiredService.

Serviços de substituição

Além de injetar novos serviços, essa técnica também pode ser usada para substituir serviços injetados anteriormente em uma página. A figura abaixo mostra todos os campos disponíveis na página usada no primeiro exemplo:

menu contextual do Intellisense em um símbolo @ digitado listando campos Html, Componente, StatsService e Url

Como você pode ver, os campos padrão incluem Html, Componente Url (bem como o StatsService que injetamos). Se, por exemplo, você quisesse substituir os HTML Helpers padrão pelo seu próprio, você poderia facilmente fazê-lo usando @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>

Se você quiser estender os serviços existentes, você pode simplesmente usar essa técnica enquanto herda ou encapsula a implementação existente com a sua própria.

Ver também