23 Stimmen

Elegantes Verwenden von ViewModels für POST-Aktionen in MVC

Derzeit übergebe ich meine Domänenobjekte an meine Ansichten, und Bindung direkt an sie von POSTs. Jeder sagt, das ist schlecht, so dass ich versuche, in das ViewModel-Konzept hinzufügen.

Ich kann jedoch keinen Weg finden, dies sehr elegant zu tun, und ich würde gerne wissen, welche Lösungen andere Leute haben, um nicht mit einer sehr unordentlichen Controller-Aktion zu enden.

Der typische Prozess für die Funktion "Person hinzufügen" sieht folgendermaßen aus:

  1. eine GET-Anfrage für eine Ansicht stellen, die ein leeres Person-Ansichtsmodell darstellt
  2. (un)gültige Daten zurückschicken
  3. Controller bindet gebuchte Daten an ein Personen-Viewmodel
  4. Wenn die Bindung fehlschlägt, muss ich die gleiche Aktion wie in (1), aber mit einigen Daten, nicht ein leeres Objekt und Fehler zu tun
  5. Wenn die Bindung erfolgreich war, muss ich die Eigenschaften aus der VM auf ein reales Modell abbilden
  6. das Modell zu validieren
  7. bei bestandener Validierung: Speichern der Person, Übertragen, Zuordnen der Benutzerdetails zu einer Anzeige-VM und Rückgabe in einer Ansicht
  8. wenn die Validierung fehlgeschlagen ist, dieselben Aktionen wie in (1), aber mit einigen Daten und Fehlern

All dies in einer Controller-Aktion (ohne GET) zu tun, ist sicherlich nicht SRP oder DRY.

Ich versuche, einen Weg zu finden, diesen Prozess so aufzubrechen, dass er sich an SRP hält, sauber, modular und vor allem testbar ist.

Was sind die Lösungen für die Menschen?

Ich habe mit benutzerdefinierten Controller-Action-Invokateure experimentiert, um die Anliegen in einzelne Methoden, intelligente modelbinders und einfach nur rohe Gewalt zu trennen, aber ich habe noch über eine Lösung in glücklich mit kommen.

P.S. da es so viel Komplexität hinzufügt, überzeugen Sie mich, warum ich mich überhaupt bemühen muss

6voto

spot Punkte 2413

Ich habe das gleiche Unbehagen empfunden. Meine einzige Möglichkeit, das zu umgehen, war, Folgendes zu tun:

  1. Erstellen eines Binders zur Bindung und Validierung des Ansichtsmodells
  2. Erstellen Sie einen Binder, um die Entität aus der Datenbank zu holen (oder tun Sie dies einfach im Controller)
  3. Aufruf einer geerbten Speichermethode in der Oberklasse. Diese Methode nimmt das Viewmodel und die zu aktualisierende Entität auf und führt alle Arbeiten aus, die Sie in Ihren Schritten aufgeführt haben.

Die Aktionsmethode sieht wie folgt aus:

public ActionResult Whatever(TViewModel viewModel, TEntity entity)
{
    return Save(viewModel, entity);
}

Der Basis-Controller hat eine generische Definition, etwa so:

public abstract BaseController<TEntity, TViewModel>
    where TEntity : Entity
    where TViewModel : ViewModel

Der Konstruktor hat zwei Abhängigkeiten, eine für das Entity Repository und eine für den Model Mapper, wie folgt:

protected BaseController(IRepository<TEntity> repository, IMapper<TEntity, TViewModel> mapper)

Damit können Sie eine geschützte Speichermethode schreiben, die von den Controller-Aktionen in der Unterklasse aufgerufen werden kann, etwa so:

protected ActionResult Save(TViewModel viewModel, TEntity entity)
{
    if (!ModelState.IsValid)
        return View(viewModel);

    _mapper.Map(viewModel, entity);
    if (!entity.IsValid)
    {
        // add errors to model state
        return View(viewModel);
    }

    try
    {
        _repository.Save(entity);
        // either redirect with static url or add virtual method for defining redirect in subclass.
    }
    catch (Exception)
    {
        // do something here with the exception
        return View(viewModel);
    }
}

Was die Testbarkeit anbelangt, so können Sie die Speichermethode testen, indem Sie gültige/ungültige Ansichtsmodelle und Entitäten eingeben. Sie können die Implementierung des Modell-Mappers, den gültigen Zustand des View-Modells und den gültigen Zustand der Entität separat testen.

Indem Sie den Basis-Controller generisch machen, können Sie dieses Muster für jede Entity/Viewmodel-Kombination in Ihrer Domäne wiederholen, wenn Sie viele Controller erstellen, um dasselbe zu tun.

Ich bin sehr daran interessiert zu hören, was andere dazu zu sagen haben. Gute Frage.

2voto

Kieron Punkte 25804

Die MVVM (ViewModel) Muster ist definitiv die eine für zu gehen, ich hatte eine ähnliche Frage über POSTing zurück zu einer Aktion ein paar Tage zurück - hier ist der Link: MVVM und ModelBinders im ASP.NET MVC Framework

Das Ergebnis war, dass Sie das Bind-Attribut verwenden können, um den gewünschten komplexen Typ zurückzuschicken.

0voto

Omu Punkte 67085

Ich habe viele gute Lösungen in der asp.net mvc beispielanwendung die sich in der Download von valueinjecter (Mapper, den ich verwende, um ViewModels auf/von Entities abzubilden, Sie können auch FormCollection/Request auf Entities abbilden)

Hier ist eine:

    public class TinyController :Controller
        {
            private readonly IModelBuilder<Person, PersonViewModel> modelBuilder;

            public TinyController()
            {
                modelBuilder = new PersonModelBuilder();
            }

            public ActionResult Index()
            {
                return View(modelBuilder.BuildModel(new PersonRepository().Get()));
            }

            [HttpPost]
            public ActionResult Index(PersonViewModel model)
            {
                if (!ModelState.IsValid)
                    return View(modelBuilder.RebuildModel(model));

                   var entity = modelBuilder.BuildEntity(model);
...
//save it or whatever
            }
        }

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X