Was ist ViewModel in MVC?


429

Ich bin neu in ASP.NET MVC. Ich habe ein Problem mit dem Verständnis des Zwecks eines ViewModel.

Was ist ein ViewModel und warum benötigen wir ein ViewModel für eine ASP.NET MVC-Anwendung?

Wenn ich ein gutes Beispiel für seine Funktionsweise und Erklärung bekomme, wäre das besser.


4
Nach diesem Beitrag suchen Sie - "Was ist ein ASP.NET MVC ViewModel?"
Yusubov

6
Dieser Artikel sieht gut aus: rachelappel.com/…
Andrew

Antworten:


607

A view modelstellt die Daten dar, die Sie in Ihrer Ansicht / Seite anzeigen möchten, unabhängig davon, ob sie für statischen Text oder für Eingabewerte (wie Textfelder und Dropdown-Listen) verwendet werden, die der Datenbank hinzugefügt (oder bearbeitet) werden können. Es ist etwas anderes als dein domain model. Es ist ein Modell für die Ansicht.

Angenommen, Sie haben eine EmployeeKlasse, die Ihr Mitarbeiterdomänenmodell darstellt und die folgenden Eigenschaften enthält (eindeutige Kennung, Vorname, Nachname und Erstellungsdatum):

public class Employee : IEntity
{
     public int Id { get; set; }

     public string FirstName { get; set; }

     public string LastName { get; set; }

     public DateTime DateCreated { get; set; }
}

Ansichtsmodelle unterscheiden sich von Domänenmodellen darin, dass Ansichtsmodelle nur die Daten (dargestellt durch Eigenschaften) enthalten, die Sie für Ihre Ansicht verwenden möchten. Angenommen, Sie möchten einen neuen Mitarbeiterdatensatz hinzufügen, sieht Ihr Ansichtsmodell möglicherweise folgendermaßen aus:

public class CreateEmployeeViewModel
{
     public string FirstName { get; set; }

     public string LastName { get; set; }
}

Wie Sie sehen, enthält es nur zwei der Eigenschaften. Diese beiden Eigenschaften befinden sich auch im Mitarbeiterdomänenmodell. Warum fragst du das vielleicht? Idwird möglicherweise nicht in der Ansicht festgelegt, sondern möglicherweise automatisch von der Employee-Tabelle generiert. Und DateCreatedkann auch in der gespeicherten Prozedur oder in der Service-Schicht Ihrer Anwendung festgelegt werden. Also Idund DateCreatedwerden im Ansichtsmodell nicht benötigt. Möglicherweise möchten Sie diese beiden Eigenschaften anzeigen, wenn Sie die Details eines Mitarbeiters (eines bereits erfassten Mitarbeiters) als statischen Text anzeigen.

Beim Laden der Ansicht / Seite erstellt die Erstellungsaktionsmethode in Ihrem Mitarbeiter-Controller eine Instanz dieses Ansichtsmodells, füllt bei Bedarf alle Felder aus und übergibt dieses Ansichtsmodell an die Ansicht / Seite:

public class EmployeeController : Controller
{
     private readonly IEmployeeService employeeService;

     public EmployeeController(IEmployeeService employeeService)
     {
          this.employeeService = employeeService;
     }

     public ActionResult Create()
     {
          CreateEmployeeViewModel model = new CreateEmployeeViewModel();

          return View(model);
     }

     public ActionResult Create(CreateEmployeeViewModel model)
     {
          // Do what ever needs to be done before adding the employee to the database
     }
}

Ihre Ansicht / Seite könnte folgendermaßen aussehen (vorausgesetzt, Sie verwenden ASP.NET MVCund die RazorAnsichts-Engine):

@model MyProject.Web.ViewModels.CreateEmployeeViewModel

<table>
     <tr>
          <td><b>First Name:</b></td>
          <td>@Html.TextBoxFor(m => m.FirstName, new { maxlength = "50", size = "50" })
              @Html.ValidationMessageFor(m => m.FirstName)
          </td>
     </tr>
     <tr>
          <td><b>Last Name:</b></td>
          <td>@Html.TextBoxFor(m => m.LastName, new { maxlength = "50", size = "50" })
              @Html.ValidationMessageFor(m => m.LastName)
          </td>
     </tr>
</table>

Die Validierung würde also nur am FirstNameund erfolgen LastName. Mit FluentValidation haben Sie möglicherweise eine Validierung wie folgt :

public class CreateEmployeeViewModelValidator : AbstractValidator<CreateEmployeeViewModel>
{
     public CreateEmployeeViewModelValidator()
     {
          RuleFor(m => m.FirstName)
               .NotEmpty()
               .WithMessage("First name required")
               .Length(1, 50)
               .WithMessage("First name must not be greater than 50 characters");

          RuleFor(m => m.LastName)
               .NotEmpty()
               .WithMessage("Last name required")
               .Length(1, 50)
               .WithMessage("Last name must not be greater than 50 characters");
     }
}

Und mit Datenanmerkungen könnte es so aussehen:

public class CreateEmployeeViewModel : ViewModelBase
{
    [Display(Name = "First Name")]
    [Required(ErrorMessage = "First name required")]
    public string FirstName { get; set; }

    [Display(Name = "Last Name")]
    [Required(ErrorMessage = "Last name required")]
    public string LastName { get; set; }
}

Das Wichtigste ist, dass das Ansichtsmodell nur die Daten darstellt, die Sie verwenden möchten , sonst nichts. Sie können sich den unnötigen Code und die Validierung vorstellen, wenn Sie ein Domänenmodell mit 30 Eigenschaften haben und nur einen einzigen Wert aktualisieren möchten. In diesem Szenario hätten Sie nur diesen einen Wert / diese eine Eigenschaft im Ansichtsmodell und nicht alle Eigenschaften, die sich im Domänenobjekt befinden.

Ein Ansichtsmodell enthält möglicherweise nicht nur Daten aus einer Datenbanktabelle. Es kann Daten aus einer anderen Tabelle kombinieren. Nehmen Sie mein Beispiel oben zum Hinzufügen eines neuen Mitarbeiterdatensatzes. Neben dem Hinzufügen von Vor- und Nachnamen möchten Sie möglicherweise auch die Abteilung des Mitarbeiters hinzufügen. Diese Liste der Abteilungen stammt aus Ihrer DepartmentsTabelle. Jetzt haben Sie Daten aus den Tabellen Employeesund Departmentsin einem Ansichtsmodell. In diesem Fall müssen Sie Ihrem Ansichtsmodell die folgenden zwei Eigenschaften hinzufügen und es mit Daten füllen:

public int DepartmentId { get; set; }

public IEnumerable<Department> Departments { get; set; }

Beim Bearbeiten von Mitarbeiterdaten (ein Mitarbeiter, der bereits zur Datenbank hinzugefügt wurde) würde sich dies nicht wesentlich von meinem obigen Beispiel unterscheiden. Erstellen Sie ein Ansichtsmodell, nennen Sie es beispielsweise EditEmployeeViewModel. Verwenden Sie in diesem Ansichtsmodell nur die Daten, die Sie bearbeiten möchten, z. B. Vor- und Nachname. Bearbeiten Sie die Daten und klicken Sie auf die Schaltfläche Senden. Ich würde mir keine Sorgen um das IdFeld machen, da der IdWert wahrscheinlich in der URL enthalten sein wird, zum Beispiel:

http://www.yourwebsite.com/Employee/Edit/3

Nehmen Sie dies Idund geben Sie es zusammen mit Ihrem Vor- und Nachnamen an Ihre Repository-Ebene weiter.

Beim Löschen eines Datensatzes folge ich normalerweise dem gleichen Pfad wie beim Modell der Bearbeitungsansicht. Ich hätte auch eine URL, zum Beispiel:

http://www.yourwebsite.com/Employee/Delete/3

Wenn die Ansicht zum ersten Mal geladen wird, werden die Daten des Mitarbeiters mithilfe Idvon 3 aus der Datenbank abgerufen. Anschließend wird auf meiner Ansicht / Seite nur statischer Text angezeigt, damit der Benutzer sehen kann, welcher Mitarbeiter gelöscht wird. Wenn der Benutzer auf die Schaltfläche Löschen klickt, verwende ich einfach den IdWert 3 und übergebe ihn an meine Repository-Ebene. Sie müssen nur Ideinen Datensatz aus der Tabelle löschen.

Ein weiterer Punkt: Sie benötigen nicht für jede Aktion ein Ansichtsmodell. Wenn es sich um einfache Daten handelt, ist es in Ordnung, sie nur zu verwenden EmployeeViewModel. Wenn es sich um komplexe Ansichten / Seiten handelt und diese sich voneinander unterscheiden, würde ich vorschlagen, dass Sie für jede Ansicht separate Ansichtsmodelle verwenden.

Ich hoffe, dies beseitigt die Verwirrung, die Sie über Ansichtsmodelle und Domänenmodelle hatten.


5
@Kenny: Dann zeig es :) Was ich sagen wollte, ist, dass du ein Domain-Modell mit 50 Eigenschaften hast und deine Ansicht nur 5 anzeigen muss, dann ist es sinnlos, alle 50 Eigenschaften nur zur Anzeige von 5 zu senden.
Brendan Vogt

5
@BrendanVogt - Sie haben das gut erklärt, aber ich verstehe nicht, wie hoch die Kosten für das "Senden aller 50 Immobilien" sind. Anderer Code hat bereits ein Modell - Objekt erstellt, mit allen 50 Eigenschaften, und es scheint nicht lohnt eine andere Klasse zu halten , nur um nicht 45 Objekte senden - vor allem , wenn Sie vielleicht eine dieser 45 Objekten in Zukunft gesendet werden sollen.
Kenny Evitt

5
@BrendanVogt - Ich denke, die Antwort von LukLed hilft mir zu verstehen, warum diese nützlich sein könnten, insbesondere, dass ein ViewModel (kann) "... Werte aus verschiedenen Datenbankentitäten kombinieren" [wobei ich davon ausgehe, dass der Ausdruck genauso wahr ist wie " Datenbankentitäten ", die durch" Modellobjekte "ersetzt werden sollen]. Welche spezifischen Probleme sollten mit ViewModels behoben werden? Hast du irgendwelche Links? Ich konnte selbst nichts finden. [Und ich entschuldige mich, wenn ich Sie anscheinend
auswähle

1
Ich habe gerade jemanden sagen hören, dass ViewModels eine gute Möglichkeit ist, mehrere Sammlungen (oder modellübergreifende Eigenschaften) in eine einzige Ansicht zu senden, ohne sie in viewBag einfügen zu müssen. Für mich ergibt das Sinn.
Ayyash

3
Es tut mir leid, dass ich kritisch bin, aber diese Antwort ist leider unvollständig. Wenn Sie ein Ansichtsmodell so definieren, dass nur das angezeigt wird, was Sie auf Ihrer Seite anzeigen müssen, fragen Sie "Was ist ein Auto?". und eine Antwort erhalten "Es ist kein Flugzeug". Nun, das stimmt, ist aber nicht sehr hilfreich. Die korrektere Definition einer VM lautet "Alles, was Sie zum Rendern Ihrer Seite benötigen." Wenn Sie ganz nach unten lesen, habe ich die Komponenten identifiziert, die Sie zum korrekten und einfachen Erstellen Ihrer VMs benötigen. In vielen Fällen nutzen Sie Ihre vorhandenen Domänenmodelle und Präsentationsmodelle.
Sam

133

Das Ansichtsmodell ist eine Klasse, die das in einer bestimmten Ansicht verwendete Datenmodell darstellt. Wir könnten diese Klasse als Modell für eine Anmeldeseite verwenden:

public class LoginPageVM
{
    [Required(ErrorMessage = "Are you really trying to login without entering username?")]
    [DisplayName("Username/e-mail")]
    public string UserName { get; set; }
    [Required(ErrorMessage = "Please enter password:)")]
    [DisplayName("Password")]
    public string Password { get; set; }
    [DisplayName("Stay logged in when browser is closed")]
    public bool RememberMe { get; set; }
}

Mit diesem Ansichtsmodell können Sie die Ansicht definieren (Razor View Engine):

@model CamelTrap.Models.ViewModels.LoginPageVM

@using (Html.BeginForm()) {
    @Html.EditorFor(m => m);
    <input type="submit" value="Save" class="submit" />
}

Und Aktionen:

[HttpGet]
public ActionResult LoginPage()
{
    return View();
}

[HttpPost]
public ActionResult LoginPage(LoginPageVM model)
{
    ...code to login user to application...
    return View(model);
}

Was zu diesem Ergebnis führt (Bildschirm wird nach dem Absenden des Formulars mit Validierungsnachrichten angezeigt):

Wie Sie sehen können, hat ein Ansichtsmodell viele Rollen:

  • Ansichtsmodelle dokumentieren eine Ansicht, indem sie nur aus Feldern bestehen, die in der Ansicht dargestellt werden.
  • Ansichtsmodelle können bestimmte Validierungsregeln enthalten, die Datenanmerkungen oder IDataErrorInfo verwenden.
  • View - Modell definiert , wie ein Blick aussehen soll (für LabelFor, EditorFor, DisplayForHelfer).
  • Ansichtsmodelle können Werte aus verschiedenen Datenbankentitäten kombinieren.
  • Sie können einfach Anzeigevorlagen für Ansichtsmodelle angeben und diese mithilfe von DisplayFor- oder EditorFor-Hilfsprogrammen an vielen Stellen wiederverwenden.

Ein weiteres Beispiel für ein Ansichtsmodell und dessen Abruf: Wir möchten grundlegende Benutzerdaten, seine Berechtigungen und den Benutzernamen anzeigen. Wir erstellen ein spezielles Ansichtsmodell, das nur die erforderlichen Felder enthält. Wir rufen Daten von verschiedenen Entitäten aus der Datenbank ab, aber die Ansicht kennt nur die Ansichtsmodellklasse:

public class UserVM {
    public int ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public bool IsAdministrator { get; set; }
    public string MothersName { get; set; }
}

Abruf:

var user = db.userRepository.GetUser(id);

var model = new UserVM() {
   ID = user.ID,
   FirstName = user.FirstName,
   LastName = user.LastName,
   IsAdministrator = user.Proviledges.IsAdministrator,
   MothersName = user.Mother.FirstName + " " + user.Mother.LastName
} 

Ich dünne user.Mother.FirstName + "" + user.Mother.LastName sollte in View Model End erfolgen. Die gesamte Logik sollte am Ende des Ansichtsmodells ausgeführt werden.
Kurkula

3
@Chandana: Ich glaube, dass eine einfache Verkettung im Ansichtsmodell durchgeführt werden kann. Es gibt keinen Grund, zwei Felder freizulegen, wenn sie zusammen dargestellt werden sollen.
LukLed

82

Bearbeiten: Ich habe diese Antwort in meinem Blog aktualisiert:

http://www.samwheat.com/post/The-function-of-ViewModels-in-MVC-web-development

Meine Antwort ist etwas langwierig, aber ich denke, es ist wichtig, Ansichtsmodelle mit anderen Arten häufig verwendeter Modelle zu vergleichen, um zu verstehen, warum sie unterschiedlich sind und warum sie notwendig sind.

Um die gestellte Frage zusammenzufassen und direkt zu beantworten:

Im Allgemeinen ist ein Ansichtsmodell ein Objekt, das alle Eigenschaften und Methoden enthält, die zum Rendern einer Ansicht erforderlich sind. Ansichtsmodelleigenschaften beziehen sich häufig auf Datenobjekte wie Kunden und Bestellungen und enthalten darüber hinaus Eigenschaften, die sich auf die Seite oder Anwendung selbst beziehen, wie z. B. Benutzername, Anwendungsname usw. Ansichtsmodelle bieten ein praktisches Objekt, an das sie an eine Rendering-Engine übergeben werden können Erstellen Sie eine HTML-Seite. Einer der vielen Gründe für die Verwendung eines Ansichtsmodells besteht darin, dass Ansichtsmodelle eine Möglichkeit bieten, bestimmte Präsentationsaufgaben wie das Verarbeiten von Benutzereingaben, das Überprüfen von Daten, das Abrufen von Daten zur Anzeige usw. zu testen.

Hier finden Sie einen Vergleich von Entitätsmodellen (a.ka. DTOs a.ka.-Modellen), Präsentationsmodellen und Ansichtsmodellen.

Datenübertragungsobjekte, auch bekannt als "Modell"

Ein Datenübertragungsobjekt (DTO) ist eine Klasse mit Eigenschaften, die mit einem Tabellenschema in einer Datenbank übereinstimmen. DTOs werden nach ihrer allgemeinen Verwendung für den Transport von Daten zu und von einem Datenspeicher benannt.
Eigenschaften von DTOs:

• Sind Geschäftsobjekte - ihre Definition hängt von den Anwendungsdaten ab.

• Enthält normalerweise nur Eigenschaften - kein Code.

• Wird hauptsächlich zum Transportieren von Daten zu und von einer Datenbank verwendet.

• Eigenschaften stimmen genau oder genau mit Feldern in einer bestimmten Tabelle in einem Datenspeicher überein.

Datenbanktabellen werden normalerweise normalisiert, daher werden DTOs normalerweise auch normalisiert. Dies macht sie für die Darstellung von Daten von begrenztem Nutzen. Für bestimmte einfache Datenstrukturen sind sie jedoch oft recht gut geeignet.

Hier sind zwei Beispiele, wie DTOs aussehen könnten:

public class Customer
{
    public int ID { get; set; }
    public string CustomerName { get; set; }
}


public class Order
{
    public int ID { get; set; }
    public int CustomerID { get; set; }
    public DateTime OrderDate { get; set; }
    public Decimal OrderAmount { get; set; }
}

Präsentationsmodelle

Ein Präsentationsmodell ist eine Dienstprogrammklasse , mit der Daten auf einem Bildschirm oder Bericht gerendert werden. Präsentationsmodelle werden normalerweise verwendet, um komplexe Datenstrukturen zu modellieren, die aus Daten mehrerer DTOs bestehen. Präsentationsmodelle repräsentieren häufig eine denormalisierte Ansicht von Daten.

Eigenschaften von Präsentationsmodellen:

• Sind Geschäftsobjekte - ihre Definition hängt von den Anwendungsdaten ab.

• Enthalten hauptsächlich Eigenschaften. Code beschränkt sich normalerweise auf das Formatieren von Daten oder das Konvertieren in oder von einem DTO. Präsentationsmodelle sollten keine Geschäftslogik enthalten.

• Präsentieren Sie häufig eine denormalisierte Ansicht von Daten. Das heißt, sie kombinieren häufig Eigenschaften aus mehreren DTOs.

• Enthält häufig Eigenschaften eines anderen Basistyps als ein DTO. Beispielsweise können Dollarbeträge als Zeichenfolgen dargestellt werden, sodass sie Kommas und ein Währungssymbol enthalten können.

• Oft definiert durch ihre Verwendung sowie ihre Objektmerkmale. Mit anderen Worten, ein einfaches DTO, das als Hintergrundmodell für das Rendern eines Rasters verwendet wird, ist tatsächlich auch ein Präsentationsmodell im Kontext dieses Rasters.

Präsentationsmodelle werden "nach Bedarf" und "wo erforderlich" verwendet (während DTOs normalerweise an das Datenbankschema gebunden sind). Ein Präsentationsmodell kann verwendet werden, um Daten für eine ganze Seite, ein Raster auf einer Seite oder ein Dropdown-Menü für ein Raster auf einer Seite zu modellieren. Präsentationsmodelle enthalten häufig Eigenschaften, die andere Präsentationsmodelle sind. Präsentationsmodelle werden häufig für einen einmaligen Zweck erstellt, z. B. um ein bestimmtes Raster auf einer einzelnen Seite zu rendern.

Ein Beispiel für ein Präsentationsmodell:

public class PresentationOrder
{
    public int OrderID { get; set; }
    public DateTime OrderDate { get; set; }
    public string PrettyDate { get { return OrderDate.ToShortDateString(); } }
    public string CustomerName { get; set; }
    public Decimal OrderAmount { get; set; }
    public string PrettyAmount { get { return string.Format("{0:C}", OrderAmount); } }
}

Modelle anzeigen

Ein Ansichtsmodell ähnelt einem Präsentationsmodell, das eine Hintergrundklasse zum Rendern einer Ansicht darstellt. Es unterscheidet sich jedoch stark von einem Präsentationsmodell oder einem DTO in seiner Konstruktion. Ansichtsmodelle enthalten häufig dieselben Eigenschaften wie Präsentationsmodelle und DTOs. Aus diesem Grund werden sie häufig miteinander verwechselt.

Eigenschaften von Ansichtsmodellen:

• Sind die einzige Datenquelle, die zum Rendern einer Seite oder eines Bildschirms verwendet wird. Normalerweise bedeutet dies, dass ein Ansichtsmodell jede Eigenschaft verfügbar macht, die ein Steuerelement auf der Seite benötigt, um sich selbst korrekt zu rendern. Wenn Sie das Ansichtsmodell zur einzigen Datenquelle für die Ansicht machen, werden die Funktionen und der Wert für das Testen von Einheiten erheblich verbessert.

• Sind zusammengesetzte Objekte , die Eigenschaften enthalten, die aus Anwendungsdaten bestehen, sowie Eigenschaften, die vom Anwendungscode verwendet werden. Diese Eigenschaft ist beim Entwerfen des Ansichtsmodells für die Wiederverwendbarkeit von entscheidender Bedeutung und wird in den folgenden Beispielen erläutert.

• Anwendungscode enthalten. Ansichtsmodelle enthalten normalerweise Methoden, die beim Rendern und bei der Interaktion des Benutzers mit der Seite aufgerufen werden. Dieser Code bezieht sich normalerweise auf Ereignisbehandlung, Animation, Sichtbarkeit von Steuerelementen, Stil usw.

• Enthalten Code, der Geschäftsdienste aufruft, um Daten abzurufen oder an einen Datenbankserver zu senden. Dieser Code wird häufig fälschlicherweise in eine Steuerung eingefügt. Das Aufrufen von Geschäftsdiensten von einem Controller schränkt normalerweise die Nützlichkeit des Ansichtsmodells für Komponententests ein. Um es klar auszudrücken, sollten Ansichtsmodelle selbst keine Geschäftslogik enthalten, sondern Dienste aufrufen, die Geschäftslogik enthalten.

• Enthalten häufig Eigenschaften, die andere Ansichtsmodelle für andere Seiten oder Bildschirme sind.

• Sind "pro Seite" oder "pro Bildschirm" geschrieben. Ein eindeutiges Ansichtsmodell wird normalerweise für jede Seite oder jeden Bildschirm in einer Anwendung geschrieben.

• Normalerweise von einer Basisklasse abgeleitet, da die meisten Seiten und Bildschirme gemeinsame Eigenschaften haben.

Modellzusammensetzung anzeigen

Wie bereits erwähnt, sind Ansichtsmodelle zusammengesetzte Objekte, da sie Anwendungseigenschaften und Geschäftsdateneigenschaften für ein einzelnes Objekt kombinieren. Beispiele für häufig verwendete Anwendungseigenschaften, die in Ansichtsmodellen verwendet werden, sind:

• Eigenschaften, mit denen der Anwendungsstatus angezeigt wird, z. B. Fehlermeldungen, Benutzername, Status usw.

• Eigenschaften zum Formatieren, Anzeigen, Stilisieren oder Animieren von Steuerelementen.

• Eigenschaften, die für die Datenbindung verwendet werden, z. B. Listenobjekte und Eigenschaften, die vom Benutzer eingegebene Zwischendaten enthalten.

Die folgenden Beispiele zeigen, warum die zusammengesetzte Natur von Ansichtsmodellen wichtig ist und wie wir am besten ein Ansichtsmodell erstellen können, das effizient und wiederverwendbar ist.

Angenommen, wir schreiben eine Webanwendung. Eine der Anforderungen des Anwendungsdesigns besteht darin, dass der Seitentitel, der Benutzername und der Anwendungsname auf jeder Seite angezeigt werden müssen. Wenn Sie eine Seite zum Anzeigen eines Präsentationsreihenfolgeobjekts erstellen möchten, können Sie das Präsentationsmodell wie folgt ändern:

public class PresentationOrder
{
    public string PageTitle { get; set; }
    public string UserName { get; set; }
    public string ApplicationName { get; set; }
    public int OrderID { get; set; }
    public DateTime OrderDate { get; set; }
    public string PrettyDate { get { return OrderDate.ToShortDateString(); } }
    public string CustomerName { get; set; }
    public Decimal OrderAmount { get; set; }
    public string PrettyAmount { get { return string.Format("{0:C}", OrderAmount); } }
}

Dieses Design könnte funktionieren ... aber was ist, wenn wir eine Seite erstellen möchten, auf der eine Liste der Bestellungen angezeigt wird? Die Eigenschaften PageTitle, UserName und ApplicationName werden wiederholt und sind unhandlich. Was ist auch, wenn wir im Konstruktor der Klasse eine Logik auf Seitenebene definieren möchten? Dies ist nicht mehr möglich, wenn wir für jede angezeigte Bestellung eine Instanz erstellen.

Zusammensetzung über Vererbung

Hier ist eine Möglichkeit, das Bestellpräsentationsmodell neu zu faktorisieren, sodass es zu einem echten Ansichtsmodell wird und für die Anzeige eines einzelnen PresentationOrder-Objekts oder einer Sammlung von PresentationOrder-Objekten nützlich ist:

public class PresentationOrderVM
{
    // Application properties
    public string PageTitle { get; set; }
    public string UserName { get; set; }
    public string ApplicationName { get; set; }

    // Business properties
    public PresentationOrder Order { get; set; }
}


public class PresentationOrderVM
{
    // Application properties
    public string PageTitle { get; set; }
    public string UserName { get; set; }
    public string ApplicationName { get; set; }

    // Business properties
    public List<PresentationOrder> Orders { get; set; }
}

Wenn wir uns die beiden oben genannten Klassen ansehen, können wir sehen, dass eine Möglichkeit, über ein Ansichtsmodell nachzudenken, darin besteht, dass es ein Präsentationsmodell ist, das ein anderes Präsentationsmodell als Eigenschaft enthält. Das Präsentationsmodell der obersten Ebene (dh das Ansichtsmodell) enthält Eigenschaften, die für die Seite oder Anwendung relevant sind, während das Präsentationsmodell (Eigenschaft) Eigenschaften enthält, die für Anwendungsdaten relevant sind.

Wir können unser Design noch einen Schritt weiter gehen und eine Modellklasse für die Basisansicht erstellen, die nicht nur für PresentationOrders, sondern auch für jede andere Klasse verwendet werden kann:

public class BaseViewModel
{
    // Application properties
    public string PageTitle { get; set; }
    public string UserName { get; set; }
    public string ApplicationName { get; set; }
}

Jetzt können wir unsere PresentationOrderVM folgendermaßen vereinfachen:

public class PresentationOrderVM : BaseViewModel
{
    // Business properties
    public PresentationOrder Order { get; set; }
}

public class PresentationOrderVM : BaseViewModel
{
    // Business properties
    public List<PresentationOrder> Orders { get; set; }
}

Wir können unser BaseViewModel noch wiederverwendbarer machen, indem wir es generisch machen:

public class BaseViewModel<T>
{
    // Application properties
    public string PageTitle { get; set; }
    public string UserName { get; set; }
    public string ApplicationName { get; set; }

    // Business property
    public T BusinessObject { get; set; }
}

Jetzt sind unsere Implementierungen mühelos:

public class PresentationOrderVM : BaseViewModel<PresentationOrder>
{
    // done!
}

public class PresentationOrderVM : BaseViewModel<List<PresentationOrder>>
{
    // done!
}

2
Sam Danke !! Dies hat mir geholfen, die facettenreiche Entität, die ein: View-Modell ist, vollständig zu erfassen. Ich bin ein College-Student, der gerade die MVC-Architektur lernt, und dies verdeutlichte eine Reihe von Funktionen, die dem Entwickler zur Verfügung stehen. Wenn ich könnte, würde ich einen Stern neben Ihre Antwort setzen.
Chef_Code

1
@Sam ' Ansichtsmodelle enthalten häufig dieselben Eigenschaften wie Präsentationsmodelle und DTOs. Aus diesem Grund werden sie häufig miteinander verwechselt.' Bedeutet das, dass sie häufig anstelle von Präsentationsmodellen verwendet werden, oder sollen sie die Präsentationsmodelle / dtos enthalten?
Alexander Derck

2
@AlexanderDerck Sie werden für verschiedene Zwecke verwendet. Sie werden untereinander verwechselt (irrtümlich). Nein, Sie verwenden normalerweise kein Pres-Modell anstelle eines Ansichtsmodells. Viel häufiger ist, dass die VM das Präsentationsmodell "enthält", dh MyViewModel<MyPresModel>
Sam

2
@Sam Angenommen, Modellobjekte sind Live-Objekte, z. B. nhibernate-Modelle. Wenn Sie also BusinessObject verwenden, setzen wir Modell- / Live-Objekte nicht direkt der Ansicht aus? dh das Geschäftsobjekt kann verwendet werden, um den Datenbankstatus direkt zu ändern? Was ist mit verschachtelten Ansichtsmodellen? Das würde mehrere Geschäftsobjekteigenschaften erfordern, oder?
Muhammad Ali

22

Wenn Sie Eigenschaften haben, die für die Ansicht spezifisch sind und nicht mit dem DB / Service / Datenspeicher zusammenhängen, empfiehlt es sich, ViewModels zu verwenden. Angenommen, Sie möchten ein Kontrollkästchen basierend auf einem DB-Feld (oder zwei) aktiviert lassen, aber das DB-Feld selbst ist kein Boolescher Wert. Während es möglich ist, diese Eigenschaften im Modell selbst zu erstellen und sie vor der Bindung an Daten zu verbergen, möchten Sie das Modell möglicherweise nicht überladen, abhängig von der Anzahl solcher Felder und Transaktionen.

Wenn zu wenige ansichtsspezifische Daten und / oder Transformationen vorhanden sind, können Sie das Modell selbst verwenden


19

Ich habe nicht alle Beiträge gelesen, aber jeder Antwort scheint ein Konzept zu fehlen, das mir wirklich geholfen hat, es zu verstehen ...

Wenn ein Modell in eine Datenbank verwendet ist Tabelle , dann ist ein Ansichtsmodell in eine Datenbank verwandte Ansicht - Eine Ansicht , die typischerweise entweder gibt kleine Mengen von Daten aus einer Tabelle oder komplexe Sätze von Daten aus mehreren Tabellen (Joins).

Ich verwende ViewModels, um Informationen an eine Ansicht / ein Formular zu übergeben und diese Daten dann in ein gültiges Modell zu übertragen, wenn das Formular an den Controller zurückgesendet wird - auch sehr praktisch zum Speichern von Listen (IEnumerable).


11

MVC hat kein Ansichtsmodell: Es verfügt über ein Modell, eine Ansicht und einen Controller. Ein Ansichtsmodell ist Teil von MVVM (Model-View-Viewmodel). MVVM ist vom Präsentationsmodell abgeleitet und wird in WPF populär gemacht. Es sollte auch ein Modell in MVVM geben, aber die meisten Leute übersehen den Punkt dieses Musters vollständig und haben nur eine Ansicht und ein Ansichtsmodell. Das Modell in MVC ähnelt dem Modell in MVVM.

In MVC ist der Prozess in drei verschiedene Verantwortlichkeiten unterteilt:

  • View ist dafür verantwortlich, die Daten dem Benutzer zu präsentieren
  • Ein Controller ist für den Seitenfluss verantwortlich
  • Ein Modell ist für die Geschäftslogik verantwortlich

MVC ist für Webanwendungen nicht sehr geeignet. Es ist ein von Smalltalk eingeführtes Muster zum Erstellen von Desktop-Anwendungen. Eine Webumgebung verhält sich völlig anders. Es macht wenig Sinn, ein 40 Jahre altes Konzept aus der Desktop-Entwicklung zu kopieren und in eine Web-Umgebung einzufügen. Viele Leute denken jedoch, dass dies in Ordnung ist, da ihre Anwendung die richtigen Werte kompiliert und zurückgibt. Das reicht meiner Meinung nach nicht aus, um eine bestimmte Designwahl als ok zu erklären.

Ein Beispiel für ein Modell in einer Webanwendung könnte sein:

public class LoginModel
{
    private readonly AuthenticationService authentication;

    public LoginModel(AuthenticationService authentication)
    {
        this.authentication = authentication;
    }

    public bool Login()
    {
        return authentication.Login(Username, Password);
    }

    public string Username { get; set; }
    public string Password { get; set; }
}

Der Controller kann es folgendermaßen verwenden:

public class LoginController
{
    [HttpPost]
    public ActionResult Login(LoginModel model)
    {
        bool success = model.Login();

        if (success)
        {
            return new RedirectResult("/dashboard");
        }
        else
        {
            TempData["message"] = "Invalid username and/or password";
            return new RedirectResult("/login");
        }
    }
}

Ihre Controller-Methoden und Ihre Modelle sind klein, leicht zu testen und auf den Punkt zu bringen.


Vielen Dank für den Einblick in die MVVM-Architektur, aber warum ist MVC nicht in Ordnung? Ihre Argumentation ist fragwürdig und verdächtig zu favorisieren. Zugegeben, ich weiß nichts über MVVM, aber wenn eine Architektur wie MVC das Verhalten nachahmen kann, ohne 50.000 Codezeilen schreiben zu müssen, was ist dann die große Sache?
Chef_Code

@Chef_Code: Es ist nicht fraglich oder favorisiert: Lesen Sie einfach das Originalpapier über MVC. Zurück zur Quelle zu gehen ist viel besser, als der Herde ohne Frage blind zu folgen (auch bekannt als "Best Practices"). MVC ist für viel kleinere Einheiten gedacht: Eine Schaltfläche auf einem Bildschirm besteht beispielsweise aus einem Modell, einer Ansicht und einer Steuerung. In Web-MVC verfügt die gesamte Seite über einen Controller, ein Modell und eine Ansicht. Das Modell und die Ansicht sollen miteinander verbunden sein, damit Änderungen im Modell sofort in der Ansicht widergespiegelt werden und umgekehrt. Nachahmung ist eine sehr große Sache. Eine Architektur sollte ihre Entwickler nicht anlügen.
Jeroen

1
@jeroen Das Akronym MVC wurde gestohlen und verstümmelt. Ja, MVC verfügt nicht über eine VM, aber auch nicht über ein Repository oder eine Serviceschicht. Diese Objekte werden häufig auf Websites verwendet. Ich glaube, das OP fragt: "Wie führe ich eine VM in MVC ein und verwende sie?" In der neuen Bedeutung von MVC ist ein Modell nicht dort, wo Geschäftslogik hingehört. Geschäftslogik gehört zu einer Service-Schicht für ein Web oder eine Desktop-App, die MVC oder MVVM verwendet. Der Begriff Modell beschreibt die Geschäftsobjekte, die an die / von der Serviceschicht übergeben werden. Diese Definitionen unterscheiden sich erheblich von der ursprünglichen Beschreibung von MVC.
Sam

1
@Sam Nicht alles, was Teil einer Website ist, kann als Teil von MVC bezeichnet werden. Es gibt keine neue Bedeutung von MVC. Es gibt die richtige Bedeutung und das "etwas völlig Unabhängiges, das Menschen mit MVC verwechseln". Zu sagen, dass das Modell für die Geschäftslogik verantwortlich ist, ist nicht dasselbe wie die Geschäftslogik, die im Modell codiert ist. Meistens fungiert das Modell als Fassade für die Anwendung.
Jeroen

Der Hauptfehler, den ich in Microsoft MVC sehe, ist das Sperren eines Modells mit einer Ansicht. Das selbst macht den ganzen Zweck all dieser Trennung zunichte, die in den letzten 20 Jahren in N-Tier-Designs stattgefunden hat. Sie haben unsere Zeit verschwendet und uns 2002 gezwungen, "WebForms" zu verwenden, ein weiteres vom Desktop inspiriertes Modell, das in die Web-Welt aufgenommen wurde. Jetzt haben sie das weggeworfen, aber noch einmal ein weiteres Desktop-Modell für dieses neue Paradigma für Webentwickler gehisst. In der Zwischenzeit bauen Google und andere riesige clientseitige Modelle, die alles voneinander trennen. Ich denke, altes ASP VBScript von 1998 war ihr wahrstes Webentwicklungssystem.
Stokely

11

Das Ansichtsmodell a ist eine einfache Klasse, die mehr als eine Klasseneigenschaft enthalten kann. Wir verwenden es, um alle erforderlichen Eigenschaften zu erben, z. B. habe ich zwei Klassen Student und Subject

Public class Student
{
public int Id {get; set;}
public string Name {get; set;}
}  
Public class Subject
{
public int SubjectID {get; set;}
public string SubjectName {get; set;}
}

Jetzt möchten wir den Namen des Schülers und den Namen des Betreffs in der Ansicht (in MVC) anzeigen, aber es ist nicht möglich, mehr als eine Klasse hinzuzufügen, z.

 @model ProjectName.Model.Student  
 @model ProjectName.Model.Subject

Der obige Code wird einen Fehler auslösen ...

Jetzt erstellen wir eine Klasse und können ihr einen beliebigen Namen geben, aber dieses Format "XyzViewModel" erleichtert das Verständnis. Es ist ein Vererbungskonzept. Jetzt erstellen wir eine dritte Klasse mit folgendem Namen:

public class StudentViewModel:Subject
{
public int ID {get; set;}
public string Name {get; set;}
}

Jetzt verwenden wir dieses ViewModel in View

@model ProjectName.Model.StudentViewModel

Jetzt können wir auf alle Eigenschaften von StudentViewModel und der geerbten Klasse in View zugreifen.


10

Viele große Beispiele, lassen Sie mich klar und knackig erklären.

ViewModel = Modell, das für die Ansicht erstellt wurde.

Die ASP.NET MVC-Ansicht kann nicht mehr als ein Modell haben. Wenn also Eigenschaften von mehr als einem Modell in der Ansicht angezeigt werden müssen, ist dies nicht möglich. ViewModel dient diesem Zweck.

Ansichtsmodell ist eine Modellklasse, die nur die Eigenschaften enthalten kann, die für eine Ansicht erforderlich sind. Es kann auch Eigenschaften von mehr als einer Entität (Tabelle) der Datenbank enthalten. Wie der Name schon sagt, wird dieses Modell speziell für die View-Anforderungen erstellt.

Im Folgenden finden Sie einige Beispiele für Ansichtsmodelle

  • Um Daten von mehr als Entitäten auf einer Ansichtsseite aufzulisten, können Sie ein Ansichtsmodell erstellen und Eigenschaften aller Entitäten haben, für die wir Daten auflisten möchten. Verbinden Sie diese Datenbankentitäten, legen Sie die Eigenschaften des Ansichtsmodells fest und kehren Sie zur Ansicht zurück, um Daten verschiedener Entitäten in einer tabellarischen Form anzuzeigen
  • Das Ansichtsmodell definiert möglicherweise nur bestimmte Felder einer einzelnen Entität, die für die Ansicht erforderlich sind.

ViewModel kann auch zum Einfügen und Aktualisieren von Datensätzen in mehr als eine Entität verwendet werden. Die Hauptverwendung von ViewModel besteht jedoch darin, Spalten aus mehreren Entitäten (Modell) in einer einzigen Ansicht anzuzeigen.

Das Erstellen von ViewModel entspricht dem Erstellen eines Modells. Das Erstellen einer Ansicht für das Viewmodel entspricht dem Erstellen einer Ansicht für das Modell.

Hier ist ein kleines Beispiel für Listendaten mit ViewModel .

Hoffe das wird nützlich sein.


6

ViewModel ist eine Problemumgehung, die die konzeptionelle Ungeschicklichkeit des MVC-Frameworks behebt. Es stellt die 4. Schicht in der 3-Schicht-Model-View-Controller-Architektur dar. Wenn das Modell (Domänenmodell) nicht geeignet ist, zu groß (größer als 2-3 Felder) für die Ansicht, erstellen wir ein kleineres ViewModel, um es an die Ansicht zu übergeben.


1

Ein Ansichtsmodell ist ein konzeptionelles Datenmodell. Es wird beispielsweise verwendet, um entweder eine Teilmenge abzurufen oder Daten aus verschiedenen Tabellen zu kombinieren.

Möglicherweise möchten Sie nur bestimmte Eigenschaften, sodass Sie nur diese und keine zusätzlichen unnötigen Eigenschaften laden können


1
  • ViewModel enthält Felder, die in der Ansicht dargestellt werden (für LabelFor-, EditorFor- und DisplayFor-Helfer).
  • ViewModel kann mithilfe von Datenanmerkungen oder IDataErrorInfo über bestimmte Validierungsregeln verfügen.
  • ViewModel kann mehrere Entitäten oder Objekte aus verschiedenen Datenmodellen oder Datenquellen enthalten.

ViewModel entwerfen

public class UserLoginViewModel 
{ 
[Required(ErrorMessage = "Please enter your username")] 
[Display(Name = "User Name")]
[MaxLength(50)]
public string UserName { get; set; }
 [Required(ErrorMessage = "Please enter your password")]
 [Display(Name = "Password")]
 [MaxLength(50)]
 public string Password { get; set; } 
} 

Darstellung des Ansichtsmodells in der Ansicht

@model MyModels.UserLoginViewModel 
@{
 ViewBag.Title = "User Login";
 Layout = "~/Views/Shared/_Layout.cshtml";
}
@using (Html.BeginForm())
{
<div class="editor-label">
 @Html.LabelFor(m => m.UserName)
</div>
<div class="editor-field">
 @Html.TextBoxFor(m => m.UserName)
 @Html.ValidationMessageFor(m => m.UserName)
</div>
<div class="editor-label">
 @Html.LabelFor(m => m.Password)
</div>
<div class="editor-field">
 @Html.PasswordFor(m => m.Password)
 @Html.ValidationMessageFor(m => m.Password)
</div>
<p>
 <input type="submit" value="Log In" />
</p>
</div>
}

Mit Aktion arbeiten

public ActionResult Login()
{ 
return View();
}
[HttpPost]
public ActionResult Login(UserLoginViewModel user)
{
// To acces data using LINQ
DataClassesDataContext mobjentity = new DataClassesDataContext();
 if (ModelState.IsValid) 
{ 
try
 {
 var q = mobjentity.tblUsers.Where(m => m.UserName == user.UserName && m.Password == user.Password).ToList(); 
 if (q.Count > 0) 
 { 
 return RedirectToAction("MyAccount");
 }
 else
 {
 ModelState.AddModelError("", "The user name or password provided is incorrect.");
 }
 }
 catch (Exception ex)
 {
 } 
 } 
 return View(user);
} 
  1. Fügen Sie in ViewModel nur die Felder / Daten ein, die Sie in der Ansicht / Seite anzeigen möchten.
  2. Da die Ansicht die Eigenschaften des ViewModel darstellt, ist das Rendern und Warten einfach.
  3. Verwenden Sie einen Mapper, wenn ViewModel komplexer wird.

1

View Model ist eine Klasse, mit der wir Daten in View rendern können. Angenommen, Sie haben zwei Entitäten Place und PlaceCategory und möchten mit einem einzigen Modell auf Daten von beiden Entitäten zugreifen. Dann verwenden wir ViewModel.

  public class Place
    {
       public int PlaceId { get; set; }
        public string PlaceName { get; set; }
        public string Latitude { get; set; }
        public string Longitude { get; set; }
        public string BestTime { get; set; }
    }
    public class Category
    {
        public int ID { get; set; }
        public int? PlaceId { get; set; }
        public string PlaceCategoryName { get; set; }
        public string PlaceCategoryType { get; set; }
    }
    public class PlaceCategoryviewModel
    {
        public string PlaceName { get; set; }
        public string BestTime { get; set; }
        public string PlaceCategoryName { get; set; }
        public string PlaceCategoryType { get; set; }
    }

Im obigen Beispiel sind Place und Category die beiden unterschiedlichen Entitäten, und das Ansichtsmodell von PlaceCategory ist ViewModel, das wir in View verwenden können.


Ihre Beispiele sind nicht so klar. Wie oben angegeben, verbindet ein ViewModel Daten mit seiner Ansicht. Wenn Sie sich die ViewModels in BlipAjax ansehen, sehen Sie Klassen, die perfekt dazu passen.
Herman Van Der Blom

0

Wenn Sie Code zum Einrichten einer "Baseline" -Webanwendung mit ViewModels studieren möchten, kann ich empfehlen, diesen Code auf GitHub herunterzuladen: https://github.com/ajsaulsberry/BlipAjax . Ich habe große Unternehmensanwendungen entwickelt. Wenn Sie dies tun, ist es problematisch, eine gute Architektur einzurichten, die all diese "ViewModel" -Funktionen verarbeitet. Ich denke, mit BlipAjax haben Sie zunächst eine sehr gute "Grundlinie". Es ist nur eine einfache Website, aber großartig in ihrer Einfachheit. Ich mag die Art und Weise, wie sie die englische Sprache verwendeten, um darauf hinzuweisen, was in der Anwendung wirklich benötigt wird.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.