Vorrei dire che riutilizzi il termine ViewModel per entrambe le direzioni dell'interazione con il cliente. Se hai letto abbastanza codice ASP.NET MVC in natura, probabilmente avrai notato la distinzione tra ViewModel e EditModel. Penso che sia importante.
Un ViewModel rappresenta tutte le informazioni richieste per eseguire il rendering di una vista. Ciò potrebbe includere dati resi in luoghi statici non interattivi e anche dati puramente per eseguire un controllo per decidere cosa esattamente rendere. Un'azione GET del controller è generalmente responsabile della creazione del pacchetto di ViewModel per la sua visualizzazione.
Un EditModel (o forse un ActionModel) rappresenta i dati richiesti per eseguire l'azione che l'utente desiderava eseguire per quel POST. Quindi un EditModel sta davvero cercando di descrivere un'azione. Questo probabilmente escluderà alcuni dati dal ViewModel e sebbene siano correlati penso sia importante rendersi conto che sono effettivamente diversi.
Un'idea
Detto questo, potresti facilmente avere una configurazione AutoMapper per andare da Model -> ViewModel e un'altra per andare da EditModel -> Model. Quindi le diverse azioni del controller devono solo utilizzare AutoMapper. Hell the EditModel potrebbe avere una funzione su di esso per convalidare le sue proprietà rispetto al modello e per applicare quei valori al modello stesso. Non sta facendo nient'altro e hai ModelBinders in MVC per mappare comunque la richiesta a EditModel.
Un'altra idea
Oltre a ciò a cui ho pensato di recente, questo tipo di funzionamento dell'idea di un ActionModel è che ciò che il client ti sta inviando è in realtà la descrizione di diverse azioni eseguite dall'utente e non solo un grande gruppo di dati. Ciò richiederebbe certamente un po 'di Javascript sul lato client per essere gestito, ma penso che l'idea sia intrigante.
Essenzialmente quando l'utente esegue le azioni sullo schermo che hai presentato, Javascript inizierà a creare un elenco di oggetti azione. Un esempio è possibile che l'utente si trovi in una schermata di informazioni sui dipendenti. Aggiornano il cognome e aggiungono un nuovo indirizzo perché il dipendente è stato recentemente sposato. Sotto le coperte questo produce un ChangeEmployeeName
e un AddEmployeeMailingAddress
oggetti a una lista. L'utente fa clic su "Salva" per confermare le modifiche e si invia l'elenco di due oggetti, ciascuno contenente solo le informazioni necessarie per eseguire ciascuna azione.
Avresti bisogno di un ModelBinder più intelligente di quello predefinito ma un buon serializzatore JSON dovrebbe essere in grado di occuparsi della mappatura degli oggetti azione lato client su quelli lato server. Quelli lato server (se ti trovi in un ambiente a 2 livelli) potrebbero facilmente avere metodi che completano l'azione sul Modello con cui lavorano. Quindi l'azione Controller finisce per ottenere solo un ID per l'istanza del modello da estrarre e un elenco di azioni da eseguire su di essa. Oppure le azioni contengono l'id per tenerle molto separate.
Quindi forse qualcosa del genere viene realizzato sul lato server:
public interface IUserAction<TModel>
{
long ModelId { get; set; }
IEnumerable<string> Validate(TModel model);
void Complete(TModel model);
}
[Transaction]
public ActionResult Save(IEnumerable<IUserAction<Employee>> actions)
{
var errors = new List<string>();
foreach( var action in actions )
{
var employee = _employeeRepository.Get(action.ModelId);
errors.AddRange(action.Validate(employee));
}
foreach( var action in editModel.UserActions )
{
var employee = _employeeRepository.Get(action.ModelId);
action.Complete(employee);
_employeeRepository.Update(employee);
}
}
Ciò rende davvero l'azione di post-back abbastanza generica poiché ti affidi al tuo ModelBinder per ottenere l'istanza IUserAction corretta e l'istanza IUserAction per eseguire la logica corretta o (più probabilmente) chiamare il modello con le informazioni.
Se ti trovi in un ambiente a 3 livelli, IUserAction potrebbe essere semplicemente reso semplice DTO da sparare attraverso il confine ed eseguito in un metodo simile sul livello dell'app. A seconda di come si esegue quel livello, potrebbe essere suddiviso molto facilmente e rimanere comunque in una transazione (ciò che viene in mente è la richiesta / risposta di Agatha e sfruttare la mappa dell'identità di DI e NHibernate).
Ad ogni modo sono sicuro che non sia un'idea perfetta, richiederebbe un po 'di JS lato client per essere gestito, e non sono ancora stato in grado di fare un progetto per vedere come si svolge, ma il post stava cercando di pensare a come arrivare e tornare di nuovo così ho pensato di dare i miei pensieri. Spero che aiuti e mi piacerebbe conoscere altri modi per gestire le interazioni.