September 2017

Band 32, Nummer 9

ASP.NET Core: Einfachere ASP.NET MVC-Apps mit Razor-Seiten

Von Steve Smith

Razor-Seiten sind ein neues Feature in ASP.NET Core 2.0. Sie bieten eine einfachere Möglichkeit, Code in ASP.NET Core-Anwendungen zu organisieren, weil die Implementierungslogik und die Anzeigemodelle enger an den Ansichtsimplementierungscode angelehnt sind. Sie stellen außerdem eine einfachere Möglichkeit dar, erste Schritte mit dem Entwickeln von ASP.NET Core-Apps auszuführen. Das bedeutet aber nicht, dass sie auf sie verzichten sollten, wenn Sie ein erfahrener .NET-Entwickler sind. Sie können Razor-Seiten auch zum Optimieren der Organisation größerer und komplexerer ASP.NET Core-Apps verwenden.

Das MVC-Muster (Model-View-Controller) ist ein ausgereiftes Benutzeroberflächenmuster, das Microsoft seit dem Jahr 2009 für die Entwicklung von ASP.NET-Anwendungen unterstützt. Es bietet zahlreiche Vorteile, die Anwendungsentwickler dabei unterstützen können, eine Trennung der Zuständigkeiten mit dem Ergebnis besser zu verwaltender Software zu erreichen. Leider führt das Muster so, wie es in den Standardprojektvorlagen implementiert ist, häufig zu einer Vielzahl von Dateien und Ordnern, die die Entwicklung insbesondere dann behindern können, wenn die Anwendung wächst. Im September 2016 habe ich in einem Artikel beschrieben, wie Feature Slices als ein Ansatz zum Beheben dieses Problems verwendet werden können (msdn.com/magazine/mt763233). Razor-Seiten bieten ein neues und anderes Verfahren zum Lösen dieses Problems. Dies gilt insbesondere für Szenarien, die konzeptmäßig seitenbasiert sind. Dieser Ansatz ist besonders hilfreich, wenn eine nahezu statische Ansicht oder ein einfaches Formular verarbeitet werden muss, das nur POST-Redirect-GET ausführen muss. In diesen Szenarien zeigen Razor-Seiten ihre wahre Stärke, weil sie einen Großteil der Konventionen vermeiden, die für MVC-Apps erforderlich sind.

Erste Schritte mit Razor-Seiten

Als Einstieg in die Verwendung von Razor-Seiten können Sie eine neue ASP.NET Core-Webanwendung in Visual Studio mit ASP.NET Core 2.0 erstellen und die Vorlage „Razor-Seiten“ auswählen. Abbildung 1 zeigt dies.

ASP.NET Core 2.0-Webanwendung mit Vorlage „Razor-Seiten“

Abbildung 1: ASP.NET Core 2.0-Webanwendung mit Vorlage „Razor-Seiten“

Sie erhalten das gleiche Ergebnis über die dotnet-Befehlszeilenschnittstelle (Command-Line Interface, CLI) wenn Sie den folgenden Befehl verwenden:

dotnet new razor

Sie müssen sicherstellen, dass Sie mindestens Version 2.0 des .NET Core SDK ausführen. Sie können die Version mit dem folgenden Befehl überprüfen:

dotnet --version

Wenn Sie das generierte Projekt untersuchen, enthält es in beiden Fällen einen neuen Ordner „Pages“. Abbildung 2 zeigt dies.

Projektvorlagenorganisation von Razor-Seiten

Abbildung 2: Projektvorlagenorganisation von Razor-Seiten

In dieser Vorlage fehlen offensichtlich zwei Ordner, die normalerweise MVC-Projekten zugeordnet sind: „Controllers“ und „Views“. Razor-Seiten verwenden den Ordner „Pages“ zum Speichern aller Seiten für die Anwendung. Es steht Ihnen frei, Ordner innerhalb des Stammordners „Pages“ zum Organisieren von Seiten gemäß den Anforderungen Ihrer Anwendung zu verwenden. Razor-Seiten ermöglichen Entwicklern das Kombinieren der code-quality-Features des MVC-Musters mit den Produktivitätsvorteilen, die sich daraus ergeben, dass Dinge gruppiert werden, die sich meistens zusammen ändern.

Beachten Sie, dass „Pages“ Bestandteil von ASP.NET Core MVC in Version 2 ist. Sie können jeder ASP.NET Core MVC-App Unterstützung für „Pages“ hinzufügen, indem Sie einfach einen Ordner „Pages“ hinzufügen und die Dateien der Razor-Seiten in diesem Ordner speichern.

Razor-Seiten verwenden eine Orderstruktur als Konvention für das Routing von Anforderungen. Die Standardseite in einer typischen MVC-App befindet sich unter „/“, „/Home/“ und „/Home/Index“, die Standardindexseite in einer App mit Razor-Seiten unter „/“ und „/Index“. Wenn Unterordner verwendet werden, können intuitiv verschiedene Bereiche Ihrer App mit den entsprechenden Routen erstellt werden. Jeder Ordner kann eine Datei „Index.cshtml“ aufweisen, die als seine Stammseite fungiert.

Wenn Sie sich eine einzelne Seite ansehen, erkennen Sie, dass eine neue Seitendirektive („@page“) auf Razor-Seiten erforderlich ist. Diese Direktive muss in der ersten Zeile der Seitendatei vorhanden sein, die die Erweiterung .cshtml verwenden sollte. Das Aussehen und Verhalten von Razor-Seiten ähnelt größtenteils auf Razor basierenden Ansichtsdateien, und eine sehr einfache Seite kann ausschließlich aus HTML bestehen:

@page 
<h1>Hello World</h1>

Der große Vorteil von Razor-Seiten besteht darin, dass sie Benutzeroberflächendetails kapseln und gruppieren können. Razor-Seiten unterstützen Inline- oder separate, auf Klassen basierende Seitenmodelle, die Datenelemente darstellen, die die Seite anzeigt oder manipuliert. Sie unterstützen außerdem Handler, durch die separate Controller und Aktionsmethoden überflüssig werden. Diese Features verringern die Anzahl der separaten Ordner und Dateien, die für das Arbeiten mit einer bestimmten Seite einer Web-App erforderlich sind. Abbildung 3 vergleicht die Ordner und Dateien, die für einen typischen, auf MVC basierenden Ansatz erforderlich sind, mit dem Ansatz der Razor-Seiten.

MVC-Ordner und -Dateien im Vergleich zu Razor-Seiten

Abbildung 3: MVC-Ordner und -Dateien im Vergleich zu Razor-Seiten

Ich verwende ein einfaches Beispielprojekt, um Razor-Seiten im Kontext einer ASP.NET Core MVC-App zu erläutern.

Ein Beispielprojekt

Da ich ein etwas komplexeres Projekt mit verschiedenen Featurebereichen simulieren möchte, kehre ich zu dem Beispiel zurück, das ich im Artikel über Feature Slices verwendet habe. Dieses Beispiel beinhaltet das Anzeigen und Verwalten verschiedener Arten von Entitäten, z. B. von Ninjas und Ninjaschwertern, sowie von Piraten, Pflanzen und Zombies. Stellen Sie sich vor, dass die App ein Freizeitspiel begleitet und Sie beim Verwalten von Konstrukten unterstützt, die in diesem Spiel vorkommen. Wenn Sie den typischen MVC-Organisationsansatz verwenden würden, wären wahrscheinlich viele verschiedene Ordner mit Controllern, Ansichten, Ansichtsmodellen usw. für jedes dieser Konstrukte vorhanden. Mit Razor-Seiten können Sie eine einfache Ordnerhierarchie erstellen, die der URL-Struktur Ihrer Anwendung zugeordnet ist.

In diesem Fall verfügt die Anwendung über eine einfache Startseite sowie vier verschiedene Abschnitte, die jeweils einen eigenen Unterordner unter „Pages“ besitzen. Die Ordnerstruktur ist sehr übersichtlich, da sich nur die Startseite („Index.cshtml“) und einige unterstützende Dateien im Stamm des Ordners „Pages“ befinden. Die anderen Bereiche sind in ihren eigenen Ordnern gespeichert, wie Abbildung 4 zeigt.

Ordnerorganisation mit Razor-Seiten

Abbildung 4: Ordnerorganisation mit Razor-Seiten

Einfache Seiten benötigen häufig keine separaten Seitenmodelle. Die Liste der Ninjaschwerter, die unter „/Ninjas/Swords/Index.cshtml“ angezeigt wird, verwendet z. B. einfach Inlinevariablen, wie Abbildung 5 zeigt.

Abbildung 5: Verwenden von Inlinevariablen

@page
@{ 
  var swords = new List<string>()
  {
    "Katana",
    "Ninjago"
  };
}
<h2>Ninja Swords</h2>
<ul>
  @foreach (var item in swords)
  {
    <li>@item</li>
  }
</ul>
<a asp-page="/Ninjas/Index">Ninja List</a>

In Razor-Blöcken deklarierte Variablen befinden sich im Bereich der Seite. Im nächsten Abschnitt erfahren Sie, wie Funktionen und sogar Klassen über @functions-Blöcke deklariert werden. Beachten Sie die Verwendung des neuen asp-page-Taghilfsprogramms unten auf der Seite. Diese Taghilfsprogramme verweisen anhand ihrer Routen auf Seiten und unterstützen absolute und relative Pfade. In diesem Beispiel hätte „/Ninjas/Index“ auch als „../Index“ oder sogar nur „..“ geschrieben werden können. Das Routing würde zur gleichen Razor-Seite „Index.cshtml“ im Ordner „Ninjas“ erfolgen. Sie können das asp-page-Taghilfsprogramm auch für <form>-Elemente zum Angeben des Ziels eines Formulars verwenden. Da die asp-page-Taghilfsprogramme auf der leistungsfähigen Routingunterstützung von ASP.NET Core aufbauen, unterstützen sie zahlreiche URL-Generierungsszenarien, die über einfache relative URLs hinausgehen.

Seitenmodelle

Razor-Seiten können stark typisierte Seitenmodelle unterstützen. Sie geben das Modell für eine Razor-Seite mit der @model-Direktive an (genau wie eine stark typisierte MVC-Ansicht). Sie können das Modell in der Datei der Razor-Seite definieren. Abbildung 6 zeigt dies.

Abbildung 6: Definieren des Modells

@page
@using WithRazorPages.Core.Interfaces;
@using WithRazorPages.Core.Model;
@model IndexModel
@functions
{
  public class IndexModel : PageModel
  {
    private readonly IRepository<Zombie> _zombieRepository;
       
    public IndexModel(IRepository<Zombie> zombieRepository)
    {
      _zombieRepository = zombieRepository;
    }
    // additional code omitted
  }
}

Sie können das Seitenmodell auch in einer separaten CodeBehind-Datei namens „Pagename.cshtml.cs“ definieren. In Visual Studio werden Dateien, die dieser Konvention folgen, mit ihrer entsprechenden Seitendatei verknüpft. Auf diese Weise wird die Navigation zwischen ihnen vereinfacht. Der gleiche Code, der im @functions-Block in Abbildung 6 gezeigt wird, könnte in einer separaten Datei gespeichert werden.

Beide Ansätze zum Speichern von Seitenmodellen haben ihre Vor- und Nachteile. Wenn Seitenmodelllogik auf der Razor-Seite selbst platziert wird, ergeben sich weniger Dateien, und Kompilierung zur Laufzeit wird flexibel ermöglicht. Sie können also Aktualisierungen der Programmlogik der Seite vornehmen, ohne eine vollständige Bereitstellung der App vornehmen zu müssen. Andererseits werden aber auch Kompilierungsfehler in Seitenmodellen, die in Razor-Seiten definiert sind, ggf. erst zur Laufzeit erkannt. Visual Studio zeigt Fehler in geöffneten Razor-Dateien an (ohne sie tatsächlich zu kompilieren). Durch das Ausführen des Befehls „dotnet build“ werden Razor-Seiten nicht kompiliert, und es werden auch keine Informationen zu potenziellen Fehlern in diesen Dateien bereitgestellt.

Separate Seitenmodellklassen bieten eine etwas bessere Trennung der Zuständigkeiten, weil sich die Razor-Seite ausschließlich auf die Vorlage zur Anzeige von Daten konzentrieren kann. Das separate Seitenmodell kann dann die Struktur der Daten der Seite und die entsprechenden Handler verarbeiten. Separate CodeBehind-Seitenmodelle profitieren außerdem von der Fehlerüberprüfung zur Kompilierungszeit und können einfacher als Inlineseitenmodelle Komponententests unterzogen werden. Letztlich haben Sie die Wahl, gar kein Modell, ein Inlinemodell oder separate Seitenmodelle in Ihren Razor-Seiten zu verwenden.

Routing, Modellbindung und Handler

Zwei Schlüsselfeatures von MVC, die normalerweise in Controllerklassen gefunden werden, sind Routing und Modellbindung. Die meisten ASP.NET Core MVC-Apps verwenden Attribute zum Definieren von Routen, HTTP-Verben und Routenparametern. Die verwendete Syntax ähnelt dem folgenden Beispiel:

[HttpGet("{id}")] 
public Task<IActionResult> GetById(int id)

Wie bereits erwähnt, basiert der Routenpfad für Razor-Seiten auf Konventionen, und er stimmt mit dem Speicherort der Seite in der Ordnerhierarchie „/Pages“ überein. Sie können jedoch auch Routenparameter unterstützen, indem Sie diese der @page-Direktive hinzufügen. Anstatt unterstützte HTTP-Verben mithilfe von Attributen anzugeben, verwenden Razor-Seiten Handler, die der Namenskonvention „OnVerb“ folgen. Dabei ist „Verb“ ein HTTP-Verb wie „Get“, „Post“ usw. Razor-Seitenhandler verhalten sich recht ähnlich wie MVC-Controlleraktionen, und sie verwenden Modellbindung, um die von ihnen definierten Parameter mit Daten aufzufüllen. Abbildung 7 zeigt eine Razor-Beispielseite, die Routenparameter, Abhängigkeitsinjektion und einen Handler zum Anzeigen der Details eines Datensatzes verwendet.

Abbildung 7: „Details.cshtml“ – Anzeigen von Details für eine angegebene Datensatz-ID

public async Task OnGetAsync()
{
  Ninjas = _ninjaRepository.List()
    .Select(n => new NinjaViewModel { Id = n.Id, Name = n.Name }).ToList();
}

public async Task<IActionResult> OnPostAddAsync()
{
  var entity = new Ninja()
  {
    Name = "Random Ninja"
  };
_  ninjaRepository.Add(entity);

  return RedirectToPage();
}

public async Task<IActionResult> OnPostDeleteAsync(int id)
{
  var entityToDelete = _ninjaRepository.GetById(id);
_ ninjaRepository.Delete(entityToDelete);

  return RedirectToPage();
}
@page "{id:int}"
@using WithRazorPages.Core.Interfaces;
@using WithRazorPages.Core.Model;
@inject IRepository<Ninja> _repository

@functions {
  public Ninja Ninja { get; set; }

  public IActionResult OnGet(int id)
  {
    Ninja = _repository.GetById(id);

    // A void handler (no return) is equivalent to return Page()
    return Page();
  }
}
<h2>Ninja: @Ninja.Name</h2>
<div>
    Id: @Ninja.Id
</div>
<div>
    <a asp-page="..">Ninja List</a>
</div>

Seiten können mehrere Handler unterstützen. Sie können daher „OnGet“, „OnPost“ usw. definieren. Razor-Seiten verwenden außerdem ein neues Modellbindungsattribut ([BindProperty]), das sich insbesondere für Formulare eignet. Sie können dieses Attribut auf eine Eigenschaft auf einer Razor-Seite (mit oder ohne explizites „PageModel“) anwenden, um Datenbindung für Nicht-GET-Anforderungen der Seite auszuwählen. Dann können Taghilfsprogramme wie „asp-for“ und „asp-validation-for“ mit der von Ihnen angegebenen Eigenschaft arbeiten, und Handler können gebundene Eigenschaften nutzen, ohne diese als Methodenparameter angeben zu müssen. Das Attribut [BindProperty] funktioniert auch für Controller.

Abbildung 8 zeigt eine Razor-Seite, auf der Benutzer der Anwendung neue Datensätze hinzufügen können.

Abbildung 8: „New.cshtml“ – Fügt eine neue Pflanze hinzu

@page
@using WithRazorPages.Core.Interfaces;
@using WithRazorPages.Core.Model;
@inject IRepository<Plant> _repository

@functions {
  [BindProperty]
  public Plant Plant { get; set; }

  public IActionResult OnPost()
  {
    if(!ModelState.IsValid) return Page();

    _repository.Add(Plant);

    return RedirectToPage("./Index");
  }
}
<h1>New Plant</h1>
<form method="post" class="form-horizontal">
  <div asp-validation-summary="All" class="text-danger"></div>
  <div class="form-group">
    <label asp-for="Plant.Name" class="col-md-2 control-label"></label>
    <div class="col-md-10">
      <input asp-for="Plant.Name" class="form-control" />
      <span asp-validation-for="Plant.Name" class="text-danger"></span>
    </div>
  </div>
  <div class="form-group">
    <div class="col-md-offset-2 col-md-10">
      <button type="submit" class="btn btn-primary">Save</button>
    </div>
  </div>
</form>
<div>
  <a asp-page="./Index">Plant List</a>
</div>

Recht häufig wird eine Seite verwendet, die mehrere Vorgänge mit dem gleichen HTTP-Verb unterstützt. Die Hauptseite im Beispiel unterstützt z. B. das Auflisten der Entitäten (als GET-Standardverhalten) sowie das Löschen eines Eintrags oder das Hinzufügen eines neuen Eintrags (beides als POST-Anforderungen). Razor-Seiten unterstützen dieses Szenario mithilfe benannter Handler (siehe Abbildung 9), die den Namen nach dem Verb (aber vor dem Suffix „Async“, wenn vorhanden) einschließen. Der PageModel-Basistyp ähnelt dem Controllerbasistyp insofern, als er eine Reihe von Hilfsmethoden bereitstellt, die Sie beim Zurückgeben von Aktionsergebnissen verwenden können. Wenn Aktualisierungen wie das Hinzufügen eines neuen Datensatzes ausgeführt werden, möchten Sie den Benutzer häufig unmittelbar nach dem Vorgang umleiten, wenn dieser erfolgreich war. Auf diese Weise entfallen Probleme durch Browseraktualisierungen, die doppelte Aufrufe des Server auslösen und zu doppelt vorhandenen Datensätzen (oder größeren Problemen) führen. Sie können „RedirectToPage“ ohne Argumente zum Umleiten an den GET-Standardhandler der aktuellen Razor-Seite verwenden.

Abbildung 9: Benannte Handler

public async Task OnGetAsync()
{
  Ninjas = _ninjaRepository.List()
    .Select(n => new NinjaViewModel { Id = n.Id, Name = n.Name }).ToList();
}

public async Task<IActionResult> OnPostAddAsync()
{
  var entity = new Ninja()
  {
    Name = "Random Ninja"
  };
_  ninjaRepository.Add(entity);

  return RedirectToPage();
}

public async Task<IActionResult> OnPostDeleteAsync(int id)
{
  var entityToDelete = _ninjaRepository.GetById(id);
_ ninjaRepository.Delete(entityToDelete);

  return RedirectToPage();
}

Sie können einen benannten Handler mit dem asp-page-handler-Taghilfsprogramm angeben, das Sie auf ein Formular,einen Link oder eine Schaltfläche anwenden:

<a asp-page-handler="Handler">Link Text</a>
<button type="submit" asp-page-handler="delete" asp-route-id="@id">Delete</button>

Das asp-page-handler-Tag verwendet Routing zum Erstellen der URL. Der Handlername und alle asp-route-parameter-Attribute werden standardmäßig als querystring-Werte angewendet. Die Schaltfläche „Delete“ im Code oben generiert eine URL wie diese:

Ninjas?handler=delete&id=1

Wenn Sie den Handler lieber als Bestandteil der URL verwenden möchten, können Sie dieses Verhalten mit der @page-Direktive angeben:

@page "{handler?}/{id?}"

Wenn diese Route angegeben wird, würde der generierte Link für die Schaltfläche „Delete“ wie folgt aussehen:

Ninjas/Delete/1

Filter

Filter sind ein weiteres leistungsfähiges Feature von ASP.NET Core MVC (dieses habe ich in der Ausgabe aus August 2016 behandelt: msdn.microsoft.com/mt767699). Wenn Sie ein Seitenmodell in einer separaten Datei verwenden, können Sie attributbasierte Filter mit Razor-Seiten verwenden und z. B. Filterattribute für die Seitenmodellklasse nutzen. Andernfalls können Sie noch immer globale Filter angeben, wenn Sie MVC für Ihre App konfigurieren. Filter werden sehr häufig dazu verwendet, Autorsierungsrichtlinien in Ihrer App anzugeben. Sie können ordner- und seitenbasierte Autorsierungsrichtlinien global konfigurieren:

services.AddMvc()
  .AddRazorPagesOptions(options =>
  {
    options.Conventions.AuthorizeFolder("/Account/Manage");
    options.Conventions.AuthorizePage("/Account/Logout");
    options.Conventions.AllowAnonymousToPage("/Account/Login");
  });

Sie können alle vorhandenen Arten von Filtern mit Ausnahme von Aktionsfiltern mit Razor-Seiten verwenden. Diese gelten nur für Aktionsmethoden in Controllern. Razor-Seiten führen jetzt auch den neuen Seitenfilter ein, der durch „IPageFilter“ (oder „IAsyncPageFilter“) dargestellt wird. Mit diesem Filter können Sie Code hinzufügen, dessen Ausführung nach dem Auswählen eines bestimmten Seitenhandlers bzw. vor oder nach der Ausführung einer Handlermethode erfolgt. Die erste Methode kann zum Ändern des Handlers verwendet werden, der die Anforderung verarbeiten soll. Beispiel:

public void OnPageHandlerSelected(PageHandlerSelectedContext context)
{
  context.HandlerMethod = 
    context.ActionDescriptor.HandlerMethods.First(m => m.Name == "Add");
}

Nachdem ein Handler ausgewählt wurde, erfolgt die Modellbindung. Nach der Modellbindung wird die OnPageHandlerExecuting-Methode aller Seitenfilter aufgerufen. Diese Methode kann auf alle modellgebundenen Daten zugreifen, die für den Handler verfügbar sind, und diese manipulieren. Außerdem kann sie den Aufruf des Handlers kurzschließen. Die OnPageHandlerExecuted-Methode wird dann aufgerufen, nachdem der Handler ausgeführt wurde, aber bevor das Aktionsergebnis ausgeführt wird.

Konzeptmäßig ähneln Seitenfilter sehr stark Aktionsfiltern, die vor und nach der Ausführung von Aktionen ausgeführt werden.

Beachten Sie, dass einer der Filter („ValidateAntiforgeryToken“) für Razor-Seiten überhaupt nicht erforderlich ist. Dieser Filter wird als Schutz vor websiteübergreifender Anforderungsfälschung (CSRF- oder XSRF-Angriffe) verwendet. Dieser Schutz ist jedoch automatisch in Razor-Seiten integriert.

Architekturmuster

Razor-Seiten sind im Lieferumfang von ASP.NET Core MVC enthalten und nutzen zahlreiche integrierte ASP.NET Core MVC-Features wie Routing, Modellbindung und Filter. Ihnen ist eine Namensähnlichkeit mit dem Web Pages-Feature gemeinsam, das Microsoft mit Web Matrix im Jahr 2010 bereitgestellt hat. Web Pages richteten sich hauptsächlich an unerfahrene Webentwickler (und waren für die meisten erfahrenen Entwickler uninteressant). Razor-Seiten hingegen kombinieren einen starken Architekturentwurf mit Zugänglichkeit.

Unter architektonischen Gesichtspunkten folgen Razor-Seiten nicht dem MVC-Muster (Model-View-Controller), weil ihnen Controller fehlen. Razor-Seiten folgen eher einem MVVM-Muster (Model-View-ViewModel), das vielen nativen App-Entwicklern vertraut sein sollte. Sie können Razor-Seiten auch als ein Beispiel des Seitencontrollermusters betrachten, das Martin Fowler wie folgt beschreibt: „Ein Objekt, das eine Anforderung für eine bestimmte Seite oder Aktion auf einer Website verarbeitet. Dieses [Objekt] kann die Seite selbst sein, oder es kann sich um ein separates Objekt handeln, das dieser Seite entspricht“. Natürlich sollte das Seitencontrollermuster auch allen Entwicklern vertraut sein, die bereits mit ASP.NET Web Forms gearbeitet haben, weil auch die ursprünglichen ASP.NET-Seiten auf diese Weise funktioniert haben.

Im Gegensatz zu ASP.NET Web Forms bauen Razor-Seiten auf ASP.NET Core auf und unterstützen lose Kopplung, die Trennung von Zuständigkeiten und SOLID-Prinzipien. Für Razor-Seiten können auf einfache Weise Komponententests ausgeführt werden (wenn separate PageModel-Klassen verwendet werden), und sie können eine Grundlage für klare, verwaltbare Unternehmensanwendungen bilden. Tun Sie Razor-Seiten nicht einfach als „Hilfsfeature“ für Hobbyprogrammierer ab. Setzen Sie sich ernsthaft mit Razor-Seiten auseinander, und überlegen Sie, ob Razor-Seiten (allein oder in Kombination mit traditionellen Controller- und Ansichtsseiten) den Entwurf Ihrer ASP.NET Core-Anwendung durch Verringern der Anzahl der Ordner verbessern können, zwischen denen Sie hin- und herspringen müssen, wenn Sie an einem bestimmten Feature arbeiten.

Migration

Auch wenn Razor-Seiten nicht dem MVC-Muster folgen, sind sie so hochgradig mit den vorhandenen ASP.NET Core MVC-Controllern und -Ansichten kompatibel, dass der Wechsel zwischen beiden Mustern normalerweise sehr einfach ist. Führen Sie zum Migrieren vorhandener Seiten, die auf Controller/Ansicht basieren, für die Verwendung von Razor-Seiten die folgenden Schritte aus:

  1. Kopieren Sie die Razor-Ansichtsdatei an den entsprechenden Speicherort im Ordner „/Pages“.
  2. Fügen Sie der Ansicht die @page-Direktive hinzu. Wenn es sich um eine Nur-GET-Ansicht handelt, sind Sie fertig.
  3. Fügen Sie eine PageModel-Datei namens „viewname.cshtml.cs“ hinzu, und speichern Sie diese im Ordner mit der Razor-Seite.
  4. Wenn die Ansicht über ein „ViewModel“ verfügte, kopieren Sie dieses in eine PageModel-Datei.
  5. Kopieren Sie alle Aktionen, die der Ansicht zugeordnet sind, aus ihrem Controller in die PageModel-Klasse.
  6. Benennen Sie die Aktionen so um, dass sie die Syntax des Razor-Seitenhandlers (z. B. „OnGet“) verwenden.
  7. Ersetzen Sie Verweise auf Ansichtshilfsmethoden durch Seitenmethoden.
  8. Kopieren Sie ggf. vorhandenen Konstruktorcode für Abhängigkeitsinjektion aus dem Controller in das „PageModel“.
  9. Ersetzen Sie das Modell, das Code an Ansichten übergibt, durch eine [BindProperty]-Eigenschaft für das „PageModel“.
  10. Ersetzen Sie Aktionsmethodenparameter, die Ansichtsmodellobjekte annehmen, ebenfalls durch eine [BindProperty]-Eigenschaft.

Eine gut strukturierte MVC-App weist häufig separate Dateien für Ansichten, Controller, Ansichtsmodelle und Bindungsmodelle auf, die sich jeweils in separaten Ordnern im Projekt befinden. Razor-Seiten ermöglichen das Zusammenführen dieser Konzepte in einige verknüpfte Dateien in einem einzigen Ordner, wahrend Ihr Code auch weiterhin der logischen Trennung von Zuständigkeiten folgen kann.

In den meisten Fällen sollten Sie in der Lage sein, diese Schritte umzukehren, um aus einer Implementierung mit Razor-Seiten zu einem Ansatz zurückzukehren, der auf Controllern/Ansichten basiert. Die beschriebenen Schritte sollten für die meisten einfachen, auf MVC basierenden Aktionen und Ansichten funktionieren. Für komplexere Anwendungen sind ggf. zusätzliche Schritte sowie Problembehandlung erforderlich.

Nächste Schritte

Das Beispiel enthält alle vier Versionen der NinjaPiratePlantZombie-Organisationsanwendung mit Unterstützung zum Hinzufügen und Anzeigen jedes einzelnen Datentyps. Das Beispiel zeigt die Organisation einer App mit mehreren Funktionsbereichen unter Verwendung von traditionellem MVC, MVC mit Bereichen, MVC mit Feature Slices und Razor-Seiten. Untersuchen Sie diese verschiedenen Ansätze, und ermitteln Sie, welche Ansätze in Ihren eigenen ASP.NET Core-Anwendungen am besten funktionieren. Der aktualisierte Quellcode für dieses Beispiel ist unter bit.ly/2eJ01cS verfügbar.


Steve Smith ist ein unabhängiger Trainer, Mentor und Berater. Er hat bereits 14 Mal den Microsoft MVP Award erhalten und arbeitet eng mit verschiedenen Microsoft-Produktteams zusammen. Nehmen Sie unter ardalis.com oder über Twitter (@ardalis) Kontakt mit ihm auf, wenn Ihr Team über einen Umstieg auf ASP.NET Core nachdenkt oder Sie bessere Codierungspraktiken einsetzen möchten.

Unser Dank gilt dem folgenden technischen Experten bei Microsoft für die Durchsicht dieses Artikels: Ryan Nowak


Diesen Artikel im MSDN Magazine-Forum diskutieren