Frage

Wenn ich mit gestartet xVal für die clientseitige Validierung, war ich nur Aktionsmethoden Umsetzung Modell, das verwendet Domain Objekte als Viewmodel oder eingebettete Instanzen dieser Objekte im Ansichtsmodell.

Dieser Ansatz funktioniert gut, die meisten Zeit, aber es gibt Fälle, in denen die Ansicht Bedarf anzuzeigen und nach zurück, nur eine Teilmenge der Eigenschaften des Modells (zum Beispiel, wenn der Benutzer sein Passwort aktualisieren will, aber nicht für den Rest seines Profildaten).

Eine (hässlich) Abhilfe ist ein verstecktes Eingabefeld für jede Eigenschaft in dem Formular zu haben, die nicht anderweitig auf der Form ist.

Offensichtlich ist die beste Praxis hier ist ein benutzerdefiniertes Ansichtsmodell zu schaffen, das nur Eigenschaften, die für die Ansicht enthält, und füllen Sie das Ansichtsmodell über AutoMapper . Es ist viel sauberer, da ich nur die Daten, die für die Ansicht zu übertragen, aber es ist bei weitem nicht perfekt, da ich die gleichen Validierungs Attribute wiederholen muß, die auf dem Domänenmodell Objekt bereits vorhanden sind.

Im Idealfall würde Ich mag die Domain Model-Objekt als Meta-Klasse über eine Metadatenattribut angeben (dies wird auch oft als „Buddy-Klasse“ bezeichnet wird), aber das nicht funktioniert, da xVal wirft, wenn die Metadatenklasse Eigenschaften, die nicht auf dem Viewmodel sind.

Gibt es eine elegante Abhilfe für dieses? Ich habe unter Berücksichtigung worden, um die xVal Quellcode Hacking, aber vielleicht gibt es eine andere Möglichkeit, die ich bisher übersehen haben.

Danke,

Adrian

Edit: Mit der Ankunft von ASP.NET MVC 2, ist dies nicht nur ein Problem der Validierung im Zusammenhang Attribut mehr, aber es gilt auch für Editor und Anzeigeattribute.

War es hilfreich?

Lösung

Dies ist die Quintessenz der Grund, warum Sie Ihre Eingabemasken sollten nicht fest an Ihrem Modell gekoppelt werden. Diese Frage erscheint tatsächlich hier auf dem MVC-Tag etwa 3-4 mal pro Monat auf. Ich würde übertölpeln, wenn ich die vorherige Frage finden konnte, und einige der Kommentar Diskussion hier ist interessant. ;)

Das Problem Ihr aufweist, ist Sie versuchen, zwei Kontexten unterschiedliche Validierung zwingen eines Modells in ein einziges Modell, das unter einer großen Menge von Szenarien ausfällt. Das beste Beispiel dafür ist die Unterzeichnung eines neuen Benutzers und dann ein Benutzerfeld später ein Admin bearbeiten zu müssen. Sie benötigen ein Passwort für ein Benutzerobjekt bei der Registrierung zu überprüfen, aber Sie werden das Passwort-Feld an den Administrator der Bearbeitung der Benutzerdaten nicht an.

Die Auswahlmöglichkeiten für um diese immer sind suboptimal. Ich habe jetzt drei Projekte an diesem Problem gearbeitet und Umsetzung der folgenden Lösungen wird nie sauber und in der Regel frustrierend. Ich werde versuchen, und sein praktische und vergessen all die DDD / db / Modell / hotnessofthemonth Diskussionen alle anderen ist zu müssen.

1) Mehr Ansicht Modelle  Mit Viewmodels, die fast die gleiche gegen das DRY Haupt sind, aber ich fühle die Kosten dieses Ansatzes sind wirklich gering. Normalerweise verletzen DRY Ampere Wartungskosten, aber IMHO die Kosten hierfür sind die niedrigsten und nicht Menge zu viel. Hypothetisch gesprochen Sie nicht ändern, wie maximale Anzahl der Zeichen in diesem Feld Nachnamen sehr oft haben kann.

2) Dynamische Metadaten Es gibt Haken in MVC 2 für Ihre eigenen Metadaten für ein Modell bereitstellt. Mit diesem Ansatz könnten Sie haben was auch immer Ihre Verwendung von Metadaten bestimmte Felder auf dem aktuellen Httprequest und daher Aktion und Controller-basierte ausschließen zu liefern. Ich habe diese Technik verwendet, um eine Datenbank gefahrene Berechtigungssystem zu bauen, die an die DB geht und erzählt die eine Unterklasse der DataAnnotationsMetadataProvider Eigenschaften auszuschließen, in der Datenbank gespeichert sind, basierend Werte.

Diese Technik funktioniert großartig atm aber das einzige Problem mit UpdateModel() ist zu validieren. Zur Lösung dieses Problems haben wir eine SmartUpdateModel() Methode, die in der Datenbank geht auch und erzeugt automatisch die Zeichenfolge [] Array ausschließen, so dass alle nicht-permissisable Felder werden nicht validiert. Wir natürlich dies so ist es nicht schlecht aus Leistungsgründen zwischengespeichert.

Ich will nur einmal betonen, dass wir [ValidationAttributes] verwenden auf unseren Modellen und dann mit neuen Regeln zur Laufzeit wird ersetzt sie. Das Endergebnis war, dass das [Required] User.LastName Feld nicht validiert wurde, wenn der Benutzer keine Berechtigung zum Zugang hat es.

3) Verrückte Schnittstelle Dynamische Proxy Thing Die letzte Technik, die ich versuchte, war zu Schnittstellen für Viewmodel zu verwenden. Das Endergebnis war ich ein User-Objekt hatte, dass von Schnittstellen wie IAdminEdit und IUserRegistration geerbt. IAdminEdit und IUserRegistration würde enthalten beide DataAnnotation Attribute, die alle die kontextspezifische Validierung wie ein Password-Eigenschaft mit den Schnittstellen ausgeführt.

Dies erforderte einige Hacks und war mehr eine akademische Übung als alles andere. Das Problem mit dem 2 und 3 ist, dass Updatemodel und der DataAnnotationsAttribute Provider benötigt angepasst werden, um diese Technik bewusst gemacht werden.

Meine größte Stein des Anstoßes war, habe ich nicht immer das gesamte Benutzerobjekt zur Ansicht senden möchten, damit ich dynamische Proxies am Ende mit Laufzeitinstanzen von IAdminEdit

erstellen

Jetzt verstehe ich, ist dies eine sehr xVal spezifische Frage, aber alle Straßen zu dynamischer Validierung wie diese führt zu Anpassung des internen MVC Metadaten-Anbieters. Da alle Metadaten Sachen nichts Neues ist, dass sauber oder einfach an dieser Stelle zu tun. Die Arbeit, die Sie haben würde, zu tun MVC Validierung Verhalten anzupassen ist nicht schwer, erfordert aber einige fundierte Kenntnisse in der, wie alle die Interna der Arbeit.

Andere Tipps

Wir zogen unsere Validierungs Attribute auf die Ansichtsmodell Schicht. In unserem Fall, sofern dieser eine sauberere Trennung von Bedenken wie auch immer, wie wir dann in der Lage waren, unser Domain-Modell so zu gestalten, dass sie nicht in einen ungültigen Zustand in dem ersten Platz zu bekommen. Zum Beispiel Datum könnte auf einem BillingTransaction Objekt erforderlich. Also wir wollen es nicht Nullable machen. Aber auf unserem Ansichtsmodell, müssen wir Nullable so belichten, dass wir die Situation fangen, wo der Benutzer hat keinen Wert eingeben.

In anderen Fällen könnten Sie die Validierung, die spezifisch pro Seite / Form ist, und Sie werden auf der Grundlage des Befehls überprüfen möchten die der Benutzer ausführen versucht, anstatt ein paar Sachen gesetzt und das Domänenmodell fragen, „Sie sind gültig für den Versuch XYZ zu tun“, wo dabei „ABC“ die Werte gültig sind.

Wenn Viewmodels sind auf Sie hypothetisch gezwungen zu werden, dann empfehle ich, dass sie nur domänenunabhängige Anforderungen erzwingen. Dazu gehören Dinge wie „Benutzername ist erforderlich“ und „E-Mail ist so formatiert, richtig“.

Wenn Sie die Validierung von den Domain-Modelle in der Ansicht Modelle duplizieren, dann haben Sie fest, der Domäne der Benutzeroberfläche gekoppelt. Wenn die Domäne Validierung Änderungen ( „nur 2 Coupon pro Woche anwenden können“ „kann nur gelten, 1 Gutschein pro Woche“ wird), muss die Benutzeroberfläche aktualisiert werden. Generell dies wäre schrecklich, und schädlich für Agilität.

Wenn Sie die Validierung von den Domain-Modelle in der Benutzeroberfläche bewegen, haben Sie entkernt im Wesentlichen Ihre Domain und die Verantwortung für die Validierung auf der Benutzeroberfläche gelegt. Eine zweite UI müssten alle die Validierung duplizieren, und Sie haben zwei separate UI miteinander gekoppelt sind. Nun, wenn der Kunde eine spezielle Schnittstelle zur Verwaltung der Bestandsaufnahme von ihrem iPhone will, muss das iPhone-Projekt alle die Validierung replizieren, die auch auf der Website UI gefunden wird. Das wäre noch schlimmer als Validierung Vervielfältigung oben beschrieben.

Wenn Sie nicht die Zukunft vorhersagen können und diese Möglichkeiten ausschließen können, nur Validate Domain-agnostisch Anforderungen.

Ich weiß nicht, wie dies für die clientseitige Validierung spielen wird, aber wenn teilweise Validierung Ihr Problem ist, können Sie ändern die DataAnnotationsValidationRunner hier diskutierten in einer IEnumerable<string> Liste der Eigenschaftsnamen zu nehmen, wie folgt:

public static class DataAnnotationsValidationRunner
{
     public static IEnumerable<ErrorInfo> GetErrors(object instance, IEnumerable<string> fieldsToValidate)
     {
           return from prop in TypeDescriptor.GetProperties(instance).Cast<PropertyDescriptor>().Where(p => fieldsToValidate.Contains(p.Name))
                  from attribute in prop.Attributes.OfType<ValidationAttribute>()
                  where !attribute.IsValid(prop.GetValue(instance))
                  select new ErrorInfo(prop.Name, attribute.FormatErrorMessage(string.Empty), instance);
     }
}

Ich werde die downvotes und Staat Gefahr, dass es zu keinem Nutzen Viewmodel (in ASP.NET MVC) ist, vor allem den Aufwand für die Berücksichtigung zu schaffen und sie zu halten. Wenn die Idee zu entkoppeln von der Domäne ist, ist, dass nicht zu verteidigen. Ein UI entkoppelt von einer Domäne kein UI für diese Domäne ist. Die UI muss ist abhängig von der Domain, so dass Sie entweder Deins Views / Aktionen mit dem Domänenmodell haben oder Ihre Viewmodel-Management-Logik mit dem Domänenmodell gekoppelt. Die Architektur Argument ist somit gegenstandslos.

Wenn die Idee Benutzer zu verhindern, ist bösartige HTTP POSTs von Hacking die die Vorteile von ASP.NET MVC Modell zu mutieren Felder verbindlich sollten sie nicht mehr ändern werden, dann A) die Domäne dieser Anforderung erzwingen sollte, und B) die Maßnahmen sollen weiße Listen von aktualisierbaren Eigenschaften zum Modell Bindemittel liefern.

Wenn Sie nicht gerade Domain ist etwas verrückt wie ein Live-in-Memory-Objektgraphen statt Einheit Kopien aussetzt, sind Viewmodel vergebliche Mühe. So Ihre Frage zu beantworten, halte Domain-Validierung in dem Domänenmodell.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top