MVC generisches ViewModel

8

Kurz gesagt, ich würde gerne ein generisches ViewModel in meine Ansichten einfügen können

Hier ist ein vereinfachter Code des Kerns dessen, was ich erreichen möchte

%Vor%

Wenn ich meine Ansicht wie folgt erstelle, funktioniert alles wie erwartet

%Vor%

Ich möchte jedoch nicht direkt von FakePerson in der Ansicht abhängig sein, wenn ich eine andere IPerson-Implementierung weitergeben möchte, also habe ich versucht, die Page-Direktive in

zu ändern %Vor%

Aber natürlich funktioniert das nicht, also habe ich nach einem ganzen Tag, in dem ich herumgemacht habe, mehr graues Haar und keine Ahnung, was ich als nächstes tun soll.

Kann jemand bitte helfen.

[AKTUALISIEREN]

Einige Ratschläge haben ergeben, dass ich eine kovariante Schnittstelle verwenden sollte; Definieren Sie eine nicht-generische Schnittstelle und verwenden Sie sie in der Ansicht. Leider habe ich das versucht, aber es gibt eine zusätzliche Implikation. Ich möchte, dass die HtmlHelper-Funktion in der Lage ist, auf alle Datenanmerkungsattribute zuzugreifen, die in der von IPerson abgeleiteten Klasse

definiert sein können %Vor%

Bei der Verwendung einer kovarianten Schnittstelle funktioniert diese Methode teilweise, um auf den abgeleiteten Typ über das ViewModel zuzugreifen; Da die Ansicht in die Schnittstelle eingegeben wurde, scheinen die Attribute nicht zugänglich zu sein.

Gibt es vielleicht einen Weg, auf diese Attribute zuzugreifen, vielleicht mit Reflektion? Oder könnte es einen anderen Weg geben, um die Ansicht zum Generischen zu schreiben.

    
ricardo 17.01.2011, 18:27
quelle

4 Antworten

2

Ich habe erfolgreich die Kovariante zur Arbeit gebracht, dh die View an eine abstrakte Basisklasse gebunden. In der Tat, was ich habe, ist eine Bindung an eine List & lt; MyBaseClass & gt ;. Dann erstelle ich eine spezifische View, die für jede Unterklasse stark typisiert ist. Das sorgt für die Bindung.

Aber das erneute Binden schlägt fehl, weil der DefaultModelBinder nur die abstrakte Basisklasse kennt und Sie eine Ausnahme wie 'Abstrakte Klasse nicht erstellen' erhalten können. Die Lösung besteht darin, eine Eigenschaft auf Ihrer Basisklasse zu haben:

%Vor%

Binden Sie das an eine versteckte Eingabe in Ihrer Ansicht. Dann ersetzen Sie Ihren Standard ModelBinder durch einen benutzerdefinierten in Global.asax:

%Vor%

Und in Ihrem benutzerdefinierten Modellbinder fangen Sie die Bindung ab. Wenn es sich um einen Ihrer bekannten abstrakten Typen handelt, analysieren Sie die BindingType-Eigenschaft und ersetzen den Modelltyp, sodass Sie eine Instanz der Unterklasse erhalten:

%Vor%

Hier ist meine abstrakte Klasse BaseParameter und ich ersetze die StringValue-Eigenschaft durch einen anderen Wert als die Unterklasse (nicht gezeigt).

Beachten Sie, dass die Formularwerte, die nur mit der Unterklasse verknüpft sind, nicht automatisch abgerundet werden, obwohl Sie den richtigen Typ erneut binden können, da der Modellbinder nur die Eigenschaften der Basisklasse anzeigt. In meinem Fall musste ich nur einen Wert in GetValue ersetzen und stattdessen aus der Unterklasse holen, also war es einfach. Wenn Sie viele Unterklasseneigenschaften binden müssen, müssen Sie etwas mehr Arbeit erledigen und sie aus dem Formular holen (ValueProvider [0]) und die Instanz selbst füllen.

Beachten Sie, dass Sie einen neuen Modellordner für einen bestimmten Typ hinzufügen können, damit Sie die generische Typüberprüfung vermeiden können.

    
Rob Kent 27.01.2011 16:36
quelle
0

Lassen Sie Ihre ViewModel-Klasse eine nicht-generische (oder generische kovariante) Schnittstelle implementieren und ändern Sie dann die Ansicht, um diese Schnittstelle anstelle der konkreten Klasse zu verwenden.

    
SLaks 17.01.2011 19:03
quelle
0

Ihr Code ist fast korrekt. Sie müssen nur eine HomeViewModel<IPerson> (nicht FakePerson ) im Controller übergeben.

Sie könnten auch eine kovariante Schnittstelle für das Modell in der Ansicht verwenden, aber das ist wahrscheinlich übertrieben.

    
SLaks 17.01.2011 18:29
quelle
0

Ich habe eine Schnittstelle erstellt

%Vor%

Klasse erstellen und von dieser Schnittstelle übernehmen

%Vor%

erstellte Instanz der Klasse im Controller

%Vor%

So erstellte Ansicht

%Vor%

Jetzt kann ich jedes Objekt einer ähnlichen Schnittstelle übergeben

Ich hoffe, es wird helfen:)

    
Nathanael 24.11.2011 16:13
quelle