2014-05-14 18 views
16

Ich bin ziemlich neu in der Verwendung von ViewModels und ich frage mich, ist es akzeptabel für ein ViewModel enthalten Instanzen von Domänenmodellen als Eigenschaften, oder sollten die Eigenschaften dieser Domänenmodelle Eigenschaften des ViewModel selbst sein? Zum Beispiel, wenn ich eine Klasse Album.csViewModels in MVC/MVVM/Trennung von Schichten - Best Practices?

public class Album 
{ 
    public int AlbumId { get; set; } 
    public string Title { get; set; } 
    public string Price { get; set; } 
    public virtual Genre Genre { get; set; } 
    public virtual Artist Artist { get; set; } 
} 

Würden Sie haben in der Regel haben die Ansichtsmodell eine Instanz der Klasse Album.cs halten, oder würden Sie haben die Ansichtsmodell haben Eigenschaften für jede der Album.cs Klasse Eigenschaften.

public class AlbumViewModel 
{ 
    public Album Album { get; set; } 
    public IEnumerable<SelectListItem> Genres { get; set; } 
    public IEnumerable<SelectListItem> Artists { get; set; } 
    public int Rating { get; set; } 
    // other properties specific to the View 
} 


public class AlbumViewModel 
{ 
    public int AlbumId { get; set; } 
    public string Title { get; set; } 
    public string Price { get; set; } 
    public IEnumerable<SelectListItem> Genres { get; set; } 
    public IEnumerable<SelectListItem> Artists { get; set; } 
    public int Rating { get; set; } 
    // other properties specific to the View 
} 
+0

Es könnte gefährlich sein, vollen Zugriff auf das Modellobjekt zu geben, wie bei Ihrem ersten Ansatz. Die bessere Option wäre, wenn Sie Ihr Modell im privaten Bereich haben und nur Eigenschaften anzeigen, die für die Ansicht benötigt werden. z.B. Öffentliche Zeichenfolge Titel {get {return this.album.Title; } set {this.album.Title = Wert;}} –

Antwort

23

Der spaßige Teil: diese ist nicht auf viewmodels in MVC, es ist eigentlich eine Frage der Trennung von den „guten alten Daten/Unternehmen/ui Schichten“ heißt Trennung von Bedenken. Ich werde das später illustrieren, aber für jetzt; Denken Sie daran, es gilt auch für MVVM oder ein anderes Design-Muster.

Ist es akzeptabel, dass ein ViewModel Instanzen von Domänenmodellen enthält?

Grundsätzlich nicht, obwohl ich sehe es oft passieren. Es hängt ein bisschen von der quick-win Ebene Ihres Projekts ab.

Lassen Sie mich ein Beispiel geben. Stellen Sie sich folgende Ansicht Modell:

public class FooViewModel 
{ 
    public string Name {get; set;} 
    public DomainClass Genre {get;set;} 
} 

und die folgende DomainClass

//also applies to database data/POCO classes 
public class DomainClass 
{ 
    public int Id {get; set;}  
    public string Name {get;set;} 
} 

Also, irgendwo in Ihrem Controller füllen Sie das FooViewModel und übergeben es an Ihrer Ansicht auf. Jetzt

, sollten Sie die folgenden Szenarien:

1) Das Domänenmodell ändert.

In diesem Fall müssen Sie wahrscheinlich auch die Ansicht anpassen, dies ist eine schlechte Praxis im Zusammenhang mit der Trennung von Bedenken.

Wenn Sie das ViewModel vom DomainModel getrennt haben, wäre eine geringfügige Anpassung der Zuordnungen (ViewModel => DomainModel (und zurück)) ausreichend.

2) Die DomainClass hat verschachtelte Eigenschaften und Ihre Ansicht zeigt nur GenreName an.

Ich habe gesehen, dass dies in echten Live-Szenarien falsch läuft.

In diesem Fall ist ein häufiges Problem, dass die Verwendung von @Html.EdittorFor zu Eingaben für das verschachtelte Objekt führt. Dies könnte Id s und andere vertrauliche Informationen enthalten. Nach diesem Kurs erstellen Sie hidden Eingaben. Wenn Sie dies mit einem serverbasierten Modelbinding oder Auto-Mapper kombinieren, ist es sehr schwierig die Manipulation von versteckten Id's mit Tools wie Firebug zu blockieren.

Obwohl es möglich, vielleicht einfach ist, einige dieser Felder zu blockieren, je mehr geschachtelte Domänen-/Datenobjekte Sie haben, desto schwieriger wird es, diesen Teil zu sichern.Denken Sie daran, dass Sie möglicherweise Ihr DomainModel aus einem Grund ändern möchten, der nicht unbedingt auf die Ansicht ausgerichtet ist. Mit jeder Änderung in Ihrem DomainModel sollten Sie sich bewusst sein, dass die Ansicht und die Sicherheitsaspekte des Controllers beeinflussen können.

3) In asp.net-MVC ist es üblich, Validierungsattribute zu verwenden.

Möchten Sie wirklich, dass Ihre Domain Metadaten zu Ihren Ansichten enthält? Oder wenden Sie View-Logik auf Ihre Datenebene an? Ist Ihre View-Validierung immer die gleiche wie die Domain-Validierung? Hat es die gleiche Validierungslogik? Verwenden Sie Ihre domänenübergreifende Anwendung? usw.

Ich denke, es ist klar, das ist nicht der Weg zu nehmen.

4) Mehr

kann ich Sie mehr Szenarien, aber es ist nur eine Frage des Geschmacks, was attraktiver ist. Ich werde nur an dieser Stelle hoffen, werden Sie erhalten den Punkt :) Trotzdem versprach ich eine Illustration:

Scematic

Nun, für wirklich schmutzig und quick-wins es funktionieren wird, aber ich glaube nicht, dass Sie sollte es wollen.

Es ist nur ein wenig mehr Aufwand, ein View-Modell zu erstellen, das normalerweise für 80 +% ähnlich dem Domain-Modell ist. Dies könnte das Gefühl, dass unnötige Zuordnungen zu tun, aber wenn der erste konzeptionelle Unterschied entsteht, werden Sie feststellen, dass es sich lohnt die Mühe hat :)

So als Alternative, schlage ich vor, die folgende Einstellung für einen allgemeinen Fall:

  • ein Ansichtsmodell
  • erstellen domainmodel
  • erstellen Datenmodell
  • verwenden, um eine Bibliothek wie automapper erstellen Mapping von einem zum anderen erstellen (dies Foo.FooProp-zur Karte helfen)

Die Vorteile sind z. Wenn Sie ein zusätzliches Feld in einer Ihrer Datenbanktabellen erstellen, wirkt sich dies nicht auf Ihre Ansicht aus. Es könnte Ihre Business-Ebene oder Zuordnungen treffen, aber dort wird es aufhören. Natürlich, die meiste Zeit möchten Sie auch Ihre Ansicht ändern, aber in diesem Fall brauchen Sie nicht zu. Es hält daher das Problem in einem Teil Ihres Codes isoliert.

web api/Daten-Schicht

Noch ein weiteres konkretes Beispiel dafür, wie diese in einem Web-API/EF Szenario funktioniert:

Web Api Datalayer EF

note

Wie @mrjoltcola sagte: Es gibt auch eine zu starke Komponente, die man im Hinterkopf behalten sollte. Wenn keiner der oben genannten Punkte zutrifft und den Benutzern/Programmierern vertraut werden kann, ist es gut zu gehen. Bedenken Sie jedoch, dass Wartbarkeit und Wiederverwendbarkeit aufgrund der Mischung von DomainModel/ViewModel verringert werden.

13

Meinungen variieren von einer Mischung aus technischen Best Practices und persönlichen Vorlieben.

Es gibt nichts falsch mit Domänenobjekte in Ihrer Ansicht Modelle oder sogar Domänenobjekte als Ihr Modell verwenden, und viele Leute tun. Einige legen großen Wert darauf, für jede einzelne Ansicht Ansichtsmodelle zu erstellen, aber ich persönlich finde, dass viele Apps von Entwicklern überbearbeitet werden, die einen Ansatz erlernen und wiederholen, mit dem sie vertraut sind. Die Wahrheit ist, gibt es mehrere Möglichkeiten, um das Ziel mit neueren Versionen von ASP.NET MVC zu erreichen.

Das größte Risiko besteht bei der Verwendung einer gemeinsamen Domänenklasse für Ihr Ansichtsmodell und Ihre Geschäfts- und Persistenzschicht in der Modellinjektion. Durch das Hinzufügen neuer Eigenschaften zu einer Modellklasse können diese Eigenschaften außerhalb der Servergrenze verfügbar gemacht werden. Ein Angreifer kann möglicherweise Eigenschaften sehen, die er nicht sehen sollte (Serialisierung) und Werte ändern, die er nicht ändern sollte (Modellbinder).

Um sich vor einer Injektion zu schützen, verwenden Sie sichere Verfahren, die für Ihren Gesamtansatz relevant sind. Wenn Sie beabsichtigen, Domänenobjekte zu verwenden, stellen Sie sicher, dass Sie weiße Listen oder schwarze Listen (Einschluss/Ausschluss) im Controller oder über Modellbinderanmerkungen verwenden. Schwarze Listen sind bequemer, aber faule Entwickler, die zukünftige Versionen schreiben, können sie vergessen oder sich ihrer nicht bewusst sein. Weiße Listen ([Bindung (Include = ...)] sind obligatorisch, die Aufmerksamkeit erfordern, wenn neue Felder hinzugefügt werden, so dass sie als Inline-View-Modell handeln

. Beispiel:

[Bind(Exclude="CompanyId,TenantId")] 
public class CustomerModel 
{ 
    public int Id { get; set; } 
    public int CompanyId { get; set; } // user cannot inject 
    public int TenantId { get; set; } // .. 
    public string Name { get; set; } 
    public string Phone { get; set; } 
    // ... 
} 

oder

public ActionResult Edit([Bind(Include = "Id,Name,Phone")] CustomerModel customer) 
{ 
    // ... 
} 

die erste Probe ist ein guter Weg, Multi-Tenant-Sicherheit für die Anwendung zu erzwingen. die zweite Probe kann jede Aktion anpassen.

in Ihrem Ansatz konsistent sein und dokumentiert deutlich die Ansatz in Ihrem Projekt für andere Entwickler verwendet.

Ich empfehle, dass Sie immer Ansichtsmodelle für Anmelde-/profilbezogene Funktionen verwenden, um sich zu zwingen, die Felder zwischen dem Webcontroller und der Datenzugriffsebene als Sicherheitsübung zu "marshallen".