Injeção de dependência em exibições no ASP.NET Core
O ASP.NET Core dá suporte à injeção de dependência em exibições. Isso pode ser útil para serviços específicos a uma exibição, como localização ou dados necessários apenas para o preenchimento de elementos de exibição. A maior parte da exibição das visualizações de dados deve ser passada pelo controlador.
Exibir ou baixar código de exemplo (como baixar)
Injeção de configuração
Os valores nos 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 marcação a seguir exibe o valor de configuração em um Razor modo de Exibição de 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 da configuração em uma visualizaçã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>
Essa exibição exibe uma lista de instâncias ToDoItem
, junto com um resumo mostrando estatísticas gerais. O resumo é populado com base no StatisticsService
injetado. Esse serviço é registrado como 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
faz alguns cálculos no conjunto de instâncias ToDoItem
, que ele acessa por meio 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 em memória. Uma implementação na memória não deve ser usada para conjuntos de dados grandes e acessados remotamente.
A amostra exibe dados do modelo associado à exibição e o serviço injetado na exibição:
Populando os dados de pesquisa
A injeção de exibição pode ser útil para popular opções em elementos de interface do usuário, como listas suspensas. Considere um formulário de perfil do usuário que inclui opções para especificar o gênero, estado e outras preferências. Renderizar esse formulário usando uma abordagem padrão pode exigir o controlador ou Razor a Página para:
- Solicite os serviços de acesso aos dados de cada conjunto de opções.
- Preencha um modelo ou
ViewBag
com cada conjunto de opções a serem associadas.
Uma abordagem alternativa injeta os serviços diretamente na exibição para obter as opções. Isso minimiza a quantidade de código exigida pelo controlador ou pela Página do razor, movendo essa lógica de construção do elemento de visualização para a própria visualização. A ação do controlador ou a Razor Página para exibir um formulário de edição de perfil só precisa passar ao formulário a 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 essas preferências inclui listas suspensas de três das propriedades:
Essas listas são populadas 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 no nível da interface do usuário criado para fornecer apenas os dados necessários para esse 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" };
}
}
Observe que um tipo não registrado gera uma exceção em tempo de execução porque o provedor de serviços é consultado internamente pelo GetRequiredService.
Substituindo serviços
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:
Os campos padrão incluem Html
, Component
e Url
. Para substituir os Auxiliares 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>
Consulte Também
- Blog de Simon Timms: Getting Lookup Data Into Your View (Inserindo dados de pesquisa na exibição)
O ASP.NET Core dá suporte à injeção de dependência em exibições. Isso pode ser útil para serviços específicos a uma exibição, como localização ou dados necessários apenas para o preenchimento de elementos de exibição. Você deve tentar manter a separação de interesses entre os controladores e as exibições. A maioria dos dados exibida pelas exibições deve ser passada pelo controlador.
Exibir ou baixar código de exemplo (como baixar)
Injeção de configuração
Os appsettings.json
valores podem ser injetados diretamente em uma exibição.
Exemplo de um appsettings.json
arquivo:
{
"root": {
"parent": {
"child": "myvalue"
}
}
}
A sintaxe de @inject
: @inject <type> <name>
Um exemplo do uso de @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 observar que o @inject
adiciona uma propriedade à exibição e preenche a propriedade usando a 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>
Essa exibição exibe uma lista de instâncias ToDoItem
, junto com um resumo mostrando estatísticas gerais. O resumo é populado com base no StatisticsService
injetado. Esse serviço é registrado como 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
faz alguns cálculos no conjunto de instâncias ToDoItem
, que ele acessa por meio 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 em memória. A implementação mostrada acima (que opera em todos os dados na memória) não é recomendada para conjuntos de dados grandes e acessados remotamente.
A amostra exibe dados do modelo associado à exibição e o serviço injetado na exibição:
Populando os dados de pesquisa
A injeção de exibição pode ser útil para popular opções em elementos de interface do usuário, como listas suspensas. Considere um formulário de perfil do usuário que inclui opções para especificar o gênero, estado e outras preferências. A renderização desse formulário usando uma abordagem MVC padrão exigirá que o controlador solicite serviços de acesso a dados para cada um desses conjuntos de opções e, em seguida, popule um modelo ou ViewBag
com cada conjunto de opções a ser associado.
Uma abordagem alternativa injeta os serviços diretamente na exibição para obter as opções. Isso minimiza a quantidade de código necessária para o 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 precisa apenas passar a instância de perfil para o formulário:
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:
Essas listas são populadas 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 no nível da interface do usuário criado para fornecer apenas os dados necessários para esse 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 tipos que você solicita por meio de injeção de dependência no Startup.ConfigureServices
. Um tipo não registrado gera uma exceção em tempo de execução porque o provedor de serviços é consultado internamente pelo GetRequiredService.
Substituindo serviços
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:
Como você pode ver, os campos padrão incluem Html
, Component
e Url
(bem como o StatsService
que injetamos). Se, para a instância, você desejava substituir os Auxiliares HTML padrão por seus próprios, faça isso com facilidade 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 deseja estender os serviços existentes, basta usar essa técnica herdando da implementação existente ou encapsulando-a com sua própria implementação.
Consulte Também
- Blog de Simon Timms: Getting Lookup Data Into Your View (Inserindo dados de pesquisa na exibição)