Chaque article trouvé sur Internet sur l'utilisation de ViewModels et l'utilisation d'Automapper donne les directives de la cartographie de direction "Controller -> View". Vous prenez un modèle de domaine avec toutes les listes de sélection dans un ViewModel spécialisé et le transmettez à la vue. C'est clair et bien.
La vue a une forme, et finalement nous sommes dans l'action POST. Ici, tous les Model Binders arrivent avec [évidemment] un autre View Model qui est [évidemment] lié au ViewModel original au moins dans la partie des conventions de dénomination pour des raisons de liaison et de validation.
Comment le mappez-vous à votre modèle de domaine?
Que ce soit une action d'insertion, nous pourrions utiliser le même Automapper. Mais que faire s'il s'agissait d'une action de mise à jour? Nous devons récupérer notre entité de domaine à partir du référentiel, mettre à jour ses propriétés en fonction des valeurs du ViewModel et enregistrer dans le référentiel.
ADDENDA 1 (9 février 2010): Parfois, l'attribution des propriétés du modèle ne suffit pas. Des mesures doivent être prises contre le modèle de domaine en fonction des valeurs de View Model. Par exemple, certaines méthodes doivent être appelées sur le modèle de domaine. Probablement, il devrait y avoir une sorte de couche de service d'application qui se situe entre le contrôleur et le domaine afin de traiter les modèles de vue ...
Comment organiser ce code et où le placer pour atteindre les objectifs suivants?
- garder les contrôleurs minces
- honorer la pratique SoC
- suivre les principes de conception pilotée par domaine
- être SEC
- à suivre ...
la source
Des outils comme AutoMapper peuvent être utilisés pour mettre à jour un objet existant avec les données de l'objet source. L'action du contrôleur pour la mise à jour peut ressembler à ceci:
[HttpPost] public ActionResult Update(MyViewModel viewModel) { MyDataModel dataModel = this.DataRepository.GetMyData(viewModel.Id); Mapper<MyViewModel, MyDataModel>(viewModel, dataModel); this.Repostitory.SaveMyData(dataModel); return View(viewModel); }
En dehors de ce qui est visible dans l'extrait ci-dessus:
L'action du contrôleur est assez mince et les préoccupations sont séparées: les problèmes de mappage sont résolus dans la configuration d'AutoMapper, la validation est effectuée par ModelBinder et l'accès aux données par le référentiel.
la source
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 un
ChangeEmployeeName
et unAddEmployeeMailingAddress
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] //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 }
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.
la source
Vous n'avez pas besoin de mapper le modèle de vue sur le domaine, car votre modèle de vue peut être créé plus que le modèle de domaine. Viewmodels optimisés pour l'écran (ui) et différents du modèle de domaine.
http://lostechies.com/jimmybogard/2009/06/30/how-we-do-mvc-view-models/
la source