Je voudrais dire que vous réutilisez le terme ViewModel pour les deux sens de l'interaction client. Si vous avez lu suffisamment de code ASP.NET MVC dans la nature, vous avez probablement vu la distinction entre un ViewModel et un EditModel. Je pense que c'est important.
Un ViewModel représente toutes les informations requises pour rendre une vue. Cela peut inclure des données qui sont rendues dans des endroits statiques non interactifs et également des données purement pour effectuer une vérification pour décider de ce qu'il faut exactement rendre. Une action Controller GET est généralement responsable de l'empaquetage du ViewModel pour sa vue.
Un EditModel (ou peut-être un ActionModel) représente les données requises pour effectuer l'action que l'utilisateur voulait faire pour ce POST. Donc, un EditModel essaie vraiment de décrire une action. Cela exclura probablement certaines données du ViewModel et bien que liées, je pense qu'il est important de réaliser qu'elles sont en effet différentes.
Une idée
Cela dit, vous pourriez très facilement avoir une configuration AutoMapper pour aller de Model -> ViewModel et une autre pour aller de EditModel -> Model. Ensuite, les différentes actions du contrôleur doivent simplement utiliser AutoMapper. Enfer, l'EditModel pourrait avoir une fonction pour valider ses propriétés par rapport au modèle et appliquer ces valeurs au modèle lui-même. Cela ne fait rien d'autre et vous avez de toute façon ModelBinders dans MVC pour mapper la demande à EditModel.
Une autre idée
Au-delà de cela, quelque chose auquel j'ai pensé récemment et qui fonctionne en quelque sorte sur l'idée d'un ActionModel est que ce que le client vous renvoie est en fait la description de plusieurs actions que l'utilisateur a effectuées et pas seulement un gros glob de données. Cela nécessiterait certainement du Javascript côté client pour gérer mais l'idée est intrigante je pense.
Essentiellement au fur et à mesure que l'utilisateur effectue des actions sur l'écran que vous avez présenté, Javascript commence à créer une liste d'objets d'action. Un exemple est peut-être que l'utilisateur se trouve sur un écran d'informations sur les employés. Ils mettent à jour le nom de famille et ajoutent une nouvelle adresse parce que l'employé s'est récemment marié. Sous les couvertures cela produit unChangeEmployeeName
et un AddEmployeeMailingAddress
objets dans une liste. L'utilisateur clique sur «Enregistrer» pour valider les modifications et vous soumettez la liste de deux objets, chacun contenant uniquement les informations nécessaires pour effectuer chaque action.
Vous auriez besoin d'un ModelBinder plus intelligent que celui par défaut, mais un bon sérialiseur JSON devrait être en mesure de prendre en charge le mappage des objets d'action côté client vers ceux côté serveur. Ceux du côté serveur (si vous êtes dans un environnement à 2 niveaux) pourraient facilement avoir des méthodes qui ont terminé l'action sur le modèle avec lequel ils travaillent. Ainsi, l'action Controller finit par obtenir simplement un identifiant pour l'instance de modèle à extraire et une liste d'actions à effectuer dessus. Ou les actions ont l'identifiant en elles pour les garder très séparées.
Alors peut-être que quelque chose comme ça se réalise du côté serveur:
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);
}
}
Cela rend vraiment l'action de publication assez générique puisque vous comptez sur votre ModelBinder pour vous obtenir la bonne instance IUserAction et votre instance IUserAction pour exécuter la logique correcte elle-même ou (plus probablement) appeler le modèle avec les informations.
Si vous étiez dans un environnement à 3 niveaux, l'IUserAction pourrait simplement être de simples DTO à tirer à travers la frontière et exécutés de manière similaire sur la couche d'application. Selon la façon dont vous faites cette couche, elle pourrait être divisée très facilement et rester dans une transaction (ce qui vient à l'esprit est la demande / réponse d'Agatha et tirer parti de la carte d'identité de DI et NHibernate).
Quoi qu'il en soit, je suis sûr que ce n'est pas une idée parfaite, cela nécessiterait un peu de JS côté client pour le gérer, et je n'ai pas encore été en mesure de faire un projet pour voir comment il se déroule, mais l'article essayait de réfléchir à la façon de le faire. aller et revenir encore alors j'ai pensé que je donnerais mes pensées. J'espère que cela aide et j'aimerais entendre d'autres façons de gérer les interactions.