Dependency Injection in Ansichten in ASP.NET Core
ASP.NET Core unterstützt Dependency Injection in Ansichten. Dies kann besonders für ansichtsspezifische Dienste nützlich sein – z.B. für die Lokalisierung oder für Daten, die nur zum Auffüllen von Ansichtselementen erforderlich sind. Die meisten Daten, die in Ansichten angezeigt werden, sollten vom Controller übergeben worden sein.
Anzeigen oder Herunterladen von Beispielcode (Vorgehensweise zum Herunterladen)
Konfigurationsinjektion
Die Werte in Einstellungsdateien wie appsettings.json
und appsettings.Development.json
können in eine Ansicht eingefügt werden. Betrachten Sie die Datei appsettings.Development.json
aus dem Beispielcode:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"MyRoot": {
"MyParent": {
"MyChildName": "Joe"
}
}
}
Das folgende Markup zeigt den Konfigurationswert in einer Razor Pages-Ansicht an:
@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>
Das folgende Markup zeigt den Konfigurationswert in einer MVC-Ansicht an:
@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>
Weitere Informationen finden Sie unter Konfiguration in ASP.NET Core.
Dienstinjektion
Ein Dienst kann mithilfe der @inject
-Anweisung in eine Ansicht eingefügt werden.
@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>
In dieser Ansicht wird neben einer Zusammenfassung von allgemeinen Statistiken eine Liste von ToDoItem
-Instanzen angezeigt. Die Zusammenfassung wird über den eingefügten StatisticsService
-Dienst aufgefüllt. Dieser Dienst ist für Abhängigkeitsinjektion in ConfigureServices
in Program.cs
registriert:
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();
Die StatisticsService
führt Berechnungen für die ToDoItem
-Instanzen durch, auf die er über ein Repository zugreift:
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);
}
}
}
Das Beispielrepository verwendet eine speicherinterne Auflistung. Eine In-Memory-Implementierung sollte nicht für große Datasets verwendet werden, auf die remote zugegriffen wird.
Das Beispiel stellt Daten aus dem Modell, die an die Ansicht gebunden sind, und den in die Ansicht eingefügten Dienst dar:
Auffüllen von Nachschlagedaten
Die Ansichtsinjektion kann nützlich sein, wenn Sie Optionen in Benutzeroberflächenelemente wie Dropdownlisten einfügen möchten. Verwenden Sie ggf. ein Benutzerprofilformular, das Optionen zum Festlegen des Geschlechts, des Staats und anderer Vorlieben beinhaltet. Das Rendern eines solchen Formulars mithilfe eines Standardansatzes erfordert möglicherweise, dass der Controller oder die Razor Page Folgendes ausführt:
- Anfordern von Datenzugriffsdiensten für die einzelnen Optionssätze.
- Auffüllen eines Modells oder
ViewBag
-Elements mit jedem Satz von Optionen, die gebunden werden sollen.
Über einen alternativen Ansatz werden Dienste direkt in die Ansicht eingefügt, um die Optionen abzurufen. Dadurch ist weniger Code für den Controller oder razor Page erforderlich, denn die Konstruktionslogik dieses Ansichtselements wird in die Ansicht selbst verschoben. Die Controlleraktion oder Razor Page, die das Formular zur Profilbearbeitung anzeigen soll, muss das Formular nur an die Profilinstanz übergeben:
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);
}
}
Das HTML-Formular, das verwendet wird, um die Einstellungen zu aktualisieren, umfasst Dropdownlisten für drei Eigenschaften:
Die Listen werden über einen Dienst aufgefüllt, der in die Ansicht eingefügt wurde:
@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
ist ein Dienst auf Benutzeroberflächenebene, der nur die für das Formular benötigten Daten einfügt:
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" };
}
}
Beachten Sie, dass ein nicht registrierter Typ eine Ausnahme zur Laufzeit auslöst, weil der Dienstanbieter intern über GetRequiredService abgefragt wird.
Überschreiben von Diensten
Sie können über diese Technik nicht nur neue Dienste einfügen, sondern auch zuvor auf einer Seite eingefügte Dienste überschreiben. In der nachfolgenden Abbildung werden alle Felder angezeigt, die auf der Seite verfügbar sind, die im ersten Beispiel verwendet wird:
Die Standardfelder sind Html
, Component
und Url
. Um die standardmäßigen HTML-Hilfsprogramme durch eine benutzerdefinierte Version zu ersetzen, verwenden Sie @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>
Weitere Informationen
- Blog von Simon Timms: Getting Lookup Data Into Your View (Übertragen von Nachschlagedaten in Ihre Ansicht)
ASP.NET Core unterstützt Dependency Injection in Ansichten. Dies kann besonders für ansichtsspezifische Dienste nützlich sein – z.B. für die Lokalisierung oder für Daten, die nur zum Auffüllen von Ansichtselementen erforderlich sind. Sie sollten versuchen, das Prinzip Separation of Concerns zwischen Controllern und Ansichten beizubehalten. Die meisten Daten, die in Ihren Ansichten angezeigt werden, sollten vom Controller übergeben worden sein.
Anzeigen oder Herunterladen von Beispielcode (Vorgehensweise zum Herunterladen)
Konfigurationsinjektion
appsettings.json
-Werte können direkt in eine Ansicht eingefügt werden.
Beispiel für eine Datei appsettings.json
:
{
"root": {
"parent": {
"child": "myvalue"
}
}
}
Die Syntax für @inject
: @inject <type> <name>
Ein Beispiel, das @inject
verwendet:
@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration
@{
string myValue = Configuration["root:parent:child"];
...
}
Dienstinjektion
Ein Dienst kann mithilfe der @inject
-Anweisung in eine Ansicht eingefügt werden. @inject
fügt der Ansicht eine Eigenschaft hinzu und füllt diese Eigenschaft mittels Dependency Injection auf.
@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>
In dieser Ansicht wird neben einer Zusammenfassung von allgemeinen Statistiken eine Liste von ToDoItem
-Instanzen angezeigt. Die Zusammenfassung wird über den eingefügten StatisticsService
-Dienst aufgefüllt. Dieser Dienst ist für Abhängigkeitsinjektion in ConfigureServices
in Startup.cs
registriert:
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();
Die StatisticsService
führt Berechnungen für die ToDoItem
-Instanzen durch, auf die er über ein Repository zugreift:
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);
}
}
}
Das Beispielrepository verwendet eine speicherinterne Auflistung. Die obenstehend dargestellte Implementierung (die auf allen Daten im Arbeitsspeicher ausgeführt wird) wird nicht für große Datasets empfohlen, auf die über eine Remoteverbindung zugegriffen wird.
Das Beispiel stellt Daten aus dem Modell, die an die Ansicht gebunden sind, und den in die Ansicht eingefügten Dienst dar:
Auffüllen von Nachschlagedaten
Die Ansichtsinjektion kann nützlich sein, wenn Sie Optionen in Benutzeroberflächenelemente wie Dropdownlisten einfügen möchten. Verwenden Sie ggf. ein Benutzerprofilformular, das Optionen zum Festlegen des Geschlechts, des Staats und anderer Vorlieben beinhaltet. Wenn Sie ein solches Formular über einen Standard-MVC-Ansatz rendern, muss der Controller Datenzugriffsdienste für all diese Optionen anfordern, und dann ein Modell oder ViewBag
mit den Optionen auffüllen, die gebunden werden sollen.
Über einen alternativen Ansatz werden Dienste direkt in die Ansicht eingefügt, um die Optionen abzurufen. Dadurch ist weniger Code für den Controller erforderlich, denn die Konstruktionsebene dieses Ansichtselements wird in die Ansicht verschoben. Die Controlleraktion, die das Formular zur Profilbearbeitung anzeigen soll, muss das Formular nur an die Profilinstanz übergeben:
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);
}
}
}
Das HTML-Formular, das verwendet wird, um diese Vorlieben zu aktualisieren, umfasst Dropdownlisten für drei Eigenschaften:
Die Listen werden über einen Dienst aufgefüllt, der in die Ansicht eingefügt wurde:
@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
ist ein Dienst auf Benutzeroberflächenebene, der nur die für das Formular benötigten Daten einfügt:
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" };
}
}
}
Wichtig
Denken Sie daran, Typen zu registrieren, die Sie über Dependency Injection in Startup.ConfigureServices
anfordern. Ein nicht registrierter Typ löst eine Ausnahme zur Laufzeit aus, weil der Dienstanbieter intern über GetRequiredService abgefragt wird.
Überschreiben von Diensten
Sie können über diese Technik nicht nur neue Dienste einfügen, sondern auch zuvor auf einer Seite eingefügte Dienste überschreiben. In der nachfolgenden Abbildung werden alle Felder angezeigt, die auf der Seite verfügbar sind, die im ersten Beispiel verwendet wird:
Die Standardfelder umfassen also Html
, Component
und Url
sowie den StatsService
-Dienst, den Sie eingefügt haben. Wenn Sie beispielsweise die Standard-HTML-Hilfsprogramme durch ihre eigenen ersetzen möchten, können Sie dafür ganz einfach @inject
verwenden:
@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>
Wenn Sie vorhandene Dienste erweitern möchten, können Sie diese Technik verwenden und aus der vorhanden Implementierung erben bzw. diese mit Ihrer eigenen umschließen.
Weitere Informationen
- Blog von Simon Timms: Getting Lookup Data Into Your View (Übertragen von Nachschlagedaten in Ihre Ansicht)