Ich möchte sagen, dass Sie den Begriff ViewModel für beide Richtungen der Client-Interaktion wiederverwenden. Wenn Sie genug ASP.NET MVC-Code in freier Wildbahn gelesen haben, haben Sie wahrscheinlich den Unterschied zwischen einem ViewModel und einem EditModel gesehen. Ich denke das ist wichtig.
Ein ViewModel repräsentiert alle Informationen, die zum Rendern einer Ansicht erforderlich sind. Dies kann Daten umfassen, die an statischen, nicht interaktiven Orten gerendert werden, sowie Daten, die lediglich eine Überprüfung durchführen, um zu entscheiden, was genau gerendert werden soll. Eine Controller-GET-Aktion ist im Allgemeinen dafür verantwortlich, das ViewModel für seine Ansicht zu verpacken.
Ein EditModel (oder möglicherweise ein ActionModel) stellt die Daten dar, die erforderlich sind, um die Aktion auszuführen, die der Benutzer für diesen POST ausführen wollte. Ein EditModel versucht also wirklich, eine Aktion zu beschreiben. Dies wird wahrscheinlich einige Daten aus dem ViewModel ausschließen, und obwohl sie verwandt sind, denke ich, ist es wichtig zu erkennen, dass sie tatsächlich unterschiedlich sind.
Eine Idee
Das heißt, Sie könnten sehr leicht eine AutoMapper-Konfiguration haben, um von Modell -> ViewModel zu wechseln, und eine andere, um von EditModel -> Modell zu wechseln. Dann müssen die verschiedenen Controller-Aktionen nur noch AutoMapper verwenden. Zum Teufel könnte das EditModel eine Funktion haben, um seine Eigenschaften anhand des Modells zu validieren und diese Werte auf das Modell selbst anzuwenden. Es macht nichts anderes und Sie haben ModelBinders in MVC, um die Anforderung trotzdem dem EditModel zuzuordnen.
Eine andere Idee
Darüber hinaus habe ich in letzter Zeit darüber nachgedacht, dass die Idee eines ActionModels darin besteht, dass der Client Ihnen tatsächlich mehrere Aktionen beschreibt, die der Benutzer ausgeführt hat, und nicht nur einen großen Datenglob. Dies würde sicherlich etwas Javascript auf der Clientseite erfordern, um es zu verwalten, aber die Idee ist meiner Meinung nach faszinierend.
Wenn der Benutzer Aktionen auf dem von Ihnen dargestellten Bildschirm ausführt, erstellt Javascript im Wesentlichen eine Liste von Aktionsobjekten. Ein Beispiel ist möglicherweise, dass sich der Benutzer auf einem Mitarbeiterinformationsbildschirm befindet. Sie aktualisieren den Nachnamen und fügen eine neue Adresse hinzu, da der Mitarbeiter kürzlich verheiratet war. Unter der Decke erzeugt dies ein ChangeEmployeeName
und ein AddEmployeeMailingAddress
Objekt zu einer Liste. Der Benutzer klickt auf "Speichern", um die Änderungen zu übernehmen, und Sie senden die Liste von zwei Objekten, die jeweils nur die Informationen enthalten, die zum Ausführen jeder Aktion erforderlich sind.
Sie benötigen einen intelligenteren ModelBinder als den Standard, aber ein guter JSON-Serializer sollte sich um die Zuordnung der clientseitigen Aktionsobjekte zu den serverseitigen Objekten kümmern können. Die serverseitigen (wenn Sie sich in einer zweistufigen Umgebung befinden) verfügen möglicherweise über Methoden, mit denen die Aktion für das Modell ausgeführt wird, mit dem sie arbeiten. Die Controller-Aktion erhält also nur eine ID für die Modellinstanz zum Abrufen und eine Liste der Aktionen, die für sie ausgeführt werden sollen. Oder die Aktionen enthalten die ID, um sie sehr getrennt zu halten.
Vielleicht wird so etwas auf der Serverseite realisiert:
public interface IUserAction<TModel>
{
long ModelId { get; set; }
IEnumerable<string> Validate(TModel model);
void Complete(TModel model);
}
[Transaction] //just assuming some sort of 2-tier with transactions handled by filter
public ActionResult Save(IEnumerable<IUserAction<Employee>> actions)
{
var errors = new List<string>();
foreach( var action in actions )
{
// relying on ORM's identity map to prevent multiple database hits
var employee = _employeeRepository.Get(action.ModelId);
errors.AddRange(action.Validate(employee));
}
// handle error cases possibly rendering view with them
foreach( var action in editModel.UserActions )
{
var employee = _employeeRepository.Get(action.ModelId);
action.Complete(employee);
// against relying on ORMs ability to properly generate SQL and batch changes
_employeeRepository.Update(employee);
}
// render the success view
}
Das macht die Aktion zum Zurücksenden wirklich ziemlich allgemein, da Sie sich auf Ihren ModelBinder verlassen, um die richtige IUserAction-Instanz zu erhalten, und Ihre IUserAction-Instanz, um entweder die richtige Logik selbst auszuführen oder (wahrscheinlicher) das Modell mit den Informationen aufzurufen.
Wenn Sie sich in einer dreistufigen Umgebung befinden, kann die IUserAction einfach zu einfachen DTOs gemacht werden, die über die Grenze geschossen und auf ähnliche Weise auf der App-Ebene ausgeführt werden. Abhängig davon, wie Sie diese Ebene erstellen, kann sie sehr einfach aufgeteilt werden und dennoch in einer Transaktion verbleiben (was in den Sinn kommt, ist die Anfrage / Antwort von Agatha und die Nutzung der Identitätskarte von DI und NHibernate).
Ich bin mir jedenfalls sicher, dass dies keine perfekte Idee ist. Es würde einige JS auf Client-Seite erfordern, um sie zu verwalten, und ich konnte noch kein Projekt durchführen, um zu sehen, wie es sich entwickelt, aber der Beitrag versuchte darüber nachzudenken, wie Komm hin und zurück, also dachte ich mir, ich würde meine Gedanken geben. Ich hoffe es hilft und ich würde gerne von anderen Möglichkeiten hören, die Interaktionen zu verwalten.