Freigeben über


Verwenden von ViewData und Implementieren von ViewModel-Klassen

von Microsoft

PDF herunterladen

Dies ist Schritt 6 eines kostenlosen "NerdDinner"-Anwendungstutorial , in dem Sie schrittweise eine kleine, aber vollständige Webanwendung mit ASP.NET MVC 1 erstellen.

Schritt 6 zeigt, wie die Unterstützung für umfassendere Formularbearbeitungsszenarien aktiviert wird, und erläutert auch zwei Ansätze, die verwendet werden können, um Daten von Controllern an Ansichten zu übergeben: ViewData und ViewModel.

Wenn Sie ASP.NET MVC 3 verwenden, empfehlen wir Ihnen, die Tutorials Erste Schritte Mit MVC 3 oder MVC Music Store zu befolgen.

NerdDinner Schritt 6: ViewData und ViewModel

Wir haben eine Reihe von Formularbeitragsszenarien behandelt und erläutert, wie CruD-Unterstützung (Create, Update and Delete) implementiert wird. Wir werden nun unsere DinnersController-Implementierung weiter weiterentwickeln und die Unterstützung für umfassendere Formularbearbeitungsszenarien aktivieren. Dabei werden zwei Ansätze erläutert, die verwendet werden können, um Daten von Controllern an Ansichten zu übergeben: ViewData und ViewModel.

Übergeben von Daten von Controllern an View-Templates

Eines der definierenden Merkmale des MVC-Musters ist die strikte "Trennung von Bedenken", die es zwischen den verschiedenen Komponenten einer Anwendung erzwingt. Modelle, Controller und Ansichten verfügen jeweils über klar definierte Rollen und Zuständigkeiten und kommunizieren untereinander auf klar definierte Weise. Dies trägt zur Verbesserung der Testbarkeit und der Wiederverwendung von Code bei.

Wenn eine Controller-Klasse beschließt, eine HTML-Antwort an einen Client zurückzu rendern, ist sie dafür verantwortlich, alle Daten, die zum Rendern der Antwort erforderlich sind, explizit an die Ansichtsvorlage zu übergeben. Ansichtsvorlagen sollten niemals Datenabrufe oder Anwendungslogik ausführen und sich stattdessen darauf beschränken, dass nur Renderingcode vorhanden ist, der vom Controller an das Modell/die Daten übergeben wird.

Im Moment sind die Modelldaten, die von unserer DinnersController-Klasse an unsere Ansichtsvorlagen übergeben werden, einfach und einfach – eine Liste von Dinner-Objekten im Fall von Index() und ein einzelnes Dinner-Objekt im Fall von Details(), Edit(), Create() und Delete(). Da wir unserer Anwendung weitere Benutzeroberflächenfunktionen hinzufügen, müssen wir häufig mehr als nur diese Daten übergeben, um HTML-Antworten in unseren Ansichtsvorlagen zu rendern. Beispielsweise können wir das Feld "Land" in unseren Ansichten Bearbeiten und Erstellen von einem HTML-Textfeld in eine Dropdownliste ändern. Anstatt die Dropdownliste der Länder- und Regionsnamen in der Ansichtsvorlage festzucodieren, möchten wir sie möglicherweise aus einer Liste der unterstützten Länder und Regionen generieren, die wir dynamisch auffüllen. Wir benötigen eine Möglichkeit, sowohl das Dinner-Objekt als auch die Liste der unterstützten Länder und Regionen von unserem Controller an unsere Ansichtsvorlagen zu übergeben.

Sehen wir uns zwei Möglichkeiten an, wie wir dies erreichen können.

Verwenden des ViewData-Wörterbuchs

Die Controller-Basisklasse macht eine ViewData-Wörterbucheigenschaft verfügbar, die verwendet werden kann, um zusätzliche Datenelemente von Controllern an Ansichten zu übergeben.

Um beispielsweise das Szenario zu unterstützen, in dem wir das Textfeld "Country" in der Bearbeitungsansicht von einem HTML-Textfeld in eine Dropdownliste ändern möchten, können wir unsere Edit()-Aktionsmethode so aktualisieren, dass (zusätzlich zu einem Dinner-Objekt) ein SelectList-Objekt übergeben wird, das als Modell einer Dropdownliste "Länder" verwendet werden kann.

//
// GET: /Dinners/Edit/5

[Authorize]
public ActionResult Edit(int id) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    ViewData["Countries"] = new SelectList(PhoneValidator.AllCountries, dinner.Country);

    return View(dinner);
}

Der Konstruktor der obigen SelectList akzeptiert eine Liste von Ländern und Regionen, mit denen die Dropdownliste aufgefüllt werden soll, sowie den aktuell ausgewählten Wert.

Anschließend können wir die Ansichtsvorlage Edit.aspx aktualisieren, um die Html.DropDownList()-Hilfsmethode anstelle der html.TextBox()-Hilfsmethode zu verwenden, die wir zuvor verwendet haben:

<%= Html.DropDownList("Country", ViewData["Countries"] as SelectList) %>

Die obige Html.DropDownList()-Hilfsmethode verwendet zwei Parameter. Die erste ist der Name des html-Formularelements, das ausgegeben werden soll. Das zweite ist das "SelectList"-Modell, das wir über das ViewData-Wörterbuch übergeben haben. Wir verwenden das C#-Schlüsselwort (keyword) "as", um den Typ innerhalb des Wörterbuchs in selectList umzuwandeln.

Wenn wir jetzt unsere Anwendung ausführen und in unserem Browser auf die URL /Dinners/Edit/1 zugreifen, sehen wir, dass die Bearbeitungsbenutzeroberfläche aktualisiert wurde, sodass anstelle eines Textfelds eine Dropdownliste mit Ländern und Regionen angezeigt wird:

Screenshot der Benutzeroberfläche zum Bearbeiten mit hervorgehobener Dropdownliste der Länder und Regionen mit einem roten Pfeil.

Da wir auch die Bearbeitungsansichtsvorlage aus der HTTP-POST Edit-Methode rendern (in Szenarien, in denen Fehler auftreten), möchten wir sicherstellen, dass wir auch diese Methode aktualisieren, um selectList zu ViewData hinzuzufügen, wenn die Ansichtsvorlage in Fehlerszenarien gerendert wird:

//
// POST: /Dinners/Edit/5

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection collection) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    try {
    
        UpdateModel(dinner);

        dinnerRepository.Save();

        return RedirectToAction("Details", new { id=dinner.DinnerID });
    }
    catch {
    
        ModelState.AddModelErrors(dinner.GetRuleViolations());

        ViewData["countries"] = new SelectList(PhoneValidator.AllCountries, dinner.Country);

        return View(dinner);
    }
}

Und jetzt unterstützt unser DinnersController-Bearbeitungsszenario eine DropDownList.

Verwenden eines ViewModel-Musters

Der ViewData-Wörterbuchansatz hat den Vorteil, dass es relativ schnell und einfach zu implementieren ist. Einige Entwickler mögen die Verwendung zeichenfolgenbasierter Wörterbücher jedoch nicht, da Tippfehler zu Fehlern führen können, die zur Kompilierzeit nicht abgefangen werden. Das nicht typisierte ViewData-Wörterbuch erfordert auch die Verwendung des "as"-Operators oder der Umwandlung, wenn eine stark typisierte Sprache wie C# in einer Ansichtsvorlage verwendet wird.

Ein alternativer Ansatz, den wir verwenden könnten, ist einer, der häufig als "ViewModel"-Muster bezeichnet wird. Bei Verwendung dieses Musters erstellen wir stark typisierte Klassen, die für unsere spezifischen Ansichtsszenarien optimiert sind und Eigenschaften für die dynamischen Werte/Inhalte verfügbar machen, die von unseren Ansichtsvorlagen benötigt werden. Unsere Controllerklassen können diese ansichtsoptimierten Klassen dann auffüllen und zur Verwendung an unsere Ansichtsvorlage übergeben. Dies ermöglicht Typsicherheit, Kompilierzeitüberprüfung und Editor intellisense in Ansichtsvorlagen.

Um beispielsweise Szenarien zur Bearbeitung von Dinnerformularen zu aktivieren, können wir eine "DinnerFormViewModel"-Klasse wie unten erstellen, die zwei stark typisierte Eigenschaften verfügbar macht: ein Dinner-Objekt und das SelectList-Modell, das zum Auffüllen der Dropdownliste "Countries" erforderlich ist:

public class DinnerFormViewModel {

    // Properties
    public Dinner     Dinner    { get; private set; }
    public SelectList Countries { get; private set; }

    // Constructor
    public DinnerFormViewModel(Dinner dinner) {
        Dinner = dinner;
        Countries = new SelectList(PhoneValidator.AllCountries, dinner.Country);
    }
}

Anschließend können wir unsere Edit()-Aktionsmethode aktualisieren, um das DinnerFormViewModel mithilfe des Dinner-Objekts zu erstellen, das wir aus unserem Repository abrufen, und es dann an unsere Ansichtsvorlage übergeben:

//
// GET: /Dinners/Edit/5

[Authorize]
public ActionResult Edit(int id) {

    Dinner dinner = dinnerRepository.GetDinner(id);
    
    return View(new DinnerFormViewModel(dinner));
}

Anschließend aktualisieren wir unsere Ansichtsvorlage so, dass sie ein "DinnerFormViewModel" anstelle eines "Dinner"-Objekts erwartet, indem Sie das Attribut "inherits" oben auf der Seite edit.aspx wie folgt ändern:

Inherits="System.Web.Mvc.ViewPage<NerdDinner.Controllers.DinnerFormViewModel>

Sobald wir dies tun, wird der IntelliSense der Eigenschaft "Model" in unserer Ansichtsvorlage aktualisiert, um das Objektmodell des DinnerFormViewModel-Typs widerzuspiegeln, den wir übergeben:

Screenshot des Code-Editor-Fensters mit einer Dropdownliste und hervorgehobenem Listenelement

Screenshot des Code-Editor-Fensters mit einer Dropdownliste und hervorgehobenem Adresslistenelement mit einem grau gepunkteten Rechteck.

Wir können dann unseren Ansichtscode aktualisieren, um ihn zu verwenden. Beachten Sie, dass wir die Namen der eingaben Elemente, die wir erstellen, nicht ändern (die Formularelemente haben weiterhin den Namen "Title", "Country") – aber wir aktualisieren die HTML-Hilfsmethoden, um die Werte mithilfe der DinnerFormViewModel-Klasse abzurufen:

<p>
    <label for="Title">Dinner Title:</label>
    <%= Html.TextBox("Title", Model.Dinner.Title) %>
    <%=Html.ValidationMessage("Title", "*") %>
</p>

<p>
    <label for="Country">Country:</label>
    <%= Html.DropDownList("Country", Model.Countries) %>                
    <%=Html.ValidationMessage("Country", "*") %>
</p>

Wir aktualisieren auch unsere Edit post-Methode, um die DinnerFormViewModel-Klasse beim Rendern von Fehlern zu verwenden:

//
// POST: /Dinners/Edit/5

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection collection) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    try {
        UpdateModel(dinner);

        dinnerRepository.Save();

        return RedirectToAction("Details", new { id=dinner.DinnerID });
    }
    catch {
        ModelState.AddModelErrors(dinner.GetRuleViolations());

        return View(new DinnerFormViewModel(dinner));
    }
}

Wir können auch unsere Create()-Aktionsmethoden aktualisieren, um die gleiche DinnerFormViewModel-Klasse wiederzuverwenden, um auch darin die DropDownList "Countries" zu aktivieren. Im Folgenden finden Sie die HTTP-GET-Implementierung:

//
// GET: /Dinners/Create

public ActionResult Create() {

    Dinner dinner = new Dinner() {
        EventDate = DateTime.Now.AddDays(7)
    };

    return View(new DinnerFormViewModel(dinner));
}

Im Folgenden finden Sie die Implementierung der HTTP-POST Create-Methode:

//
// POST: /Dinners/Create

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Dinner dinner) {

    if (ModelState.IsValid) {

        try {
            dinner.HostedBy = "SomeUser";

            dinnerRepository.Add(dinner);
            dinnerRepository.Save();

            return RedirectToAction("Details", new { id=dinner.DinnerID });
        }
        catch {
            ModelState.AddModelErrors(dinner.GetRuleViolations());
        }
    }

    return View(new DinnerFormViewModel(dinner));
}

Und jetzt unterstützen sowohl die Bildschirme Bearbeiten als auch Erstellen Dropdownlisten für die Auswahl des Landes oder der Region.

Benutzerdefinierte ViewModel-Klassen

Im obigen Szenario macht unsere DinnerFormViewModel-Klasse das Dinner-Modellobjekt direkt als Eigenschaft zusammen mit einer unterstützenden SelectList-Modelleigenschaft verfügbar. Dieser Ansatz funktioniert gut für Szenarien, in denen die HTML-Benutzeroberfläche, die wir in unserer Ansichtsvorlage erstellen möchten, relativ eng mit unseren Domänenmodellobjekten übereinstimmt.

In Szenarien, in denen dies nicht der Fall ist, können Sie eine benutzerdefinierte ViewModel-Klasse erstellen, deren Objektmodell für die Nutzung durch die Ansicht optimiert ist und sich möglicherweise völlig von dem zugrunde liegenden Domänenmodellobjekt unterscheidet. Es kann z. B. verschiedene Eigenschaftennamen und/oder Aggregateigenschaften verfügbar machen, die aus mehreren Modellobjekten gesammelt wurden.

Benutzerdefinierte ViewModel-Klassen können sowohl zum Übergeben von Daten von Controllern an Ansichten zum Rendern als auch zum Verarbeiten von Formulardaten verwendet werden, die an die Aktionsmethode eines Controllers zurückgesendet werden. In diesem späteren Szenario kann die Aktionsmethode ein ViewModel-Objekt mit den vom Formular bereitgestellten Daten aktualisieren und dann die ViewModel-instance verwenden, um ein tatsächliches Domänenmodellobjekt zuzuordnen oder abzurufen.

Benutzerdefinierte ViewModel-Klassen können ein hohes Maß an Flexibilität bieten und sollten jedes Mal untersucht werden, wenn Sie den Renderingcode in Ihren Ansichtsvorlagen oder den Code zum Bereitstellen von Formularen in Ihren Aktionsmethoden finden, die zu kompliziert werden. Dies ist häufig ein Zeichen dafür, dass Ihre Domänenmodelle nicht sauber der benutzerdefinierten Benutzeroberfläche entsprechen, die Sie generieren, und dass eine benutzerdefinierte ViewModel-Zwischenklasse hilfreich sein kann.

Nächster Schritt

Sehen wir uns nun an, wie wir Teil- und master-Seiten verwenden können, um die Benutzeroberfläche in unserer Anwendung wiederzuverwenden und zu teilen.