Cada artículo que se encuentra en Internet sobre el uso de ViewModels y el uso de Automapper proporciona las pautas del mapeo de dirección "Controlador -> Vista". Toma un modelo de dominio junto con todas las listas de selección en un ViewModel especializado y lo pasa a la vista. Eso es claro y está bien.
La vista tiene una forma y finalmente estamos en la acción POST. Aquí todos los Model Binders entran en escena junto con [obviamente] otro View Model que está [obviamente] relacionado con el ViewModel original al menos en la parte de las convenciones de nomenclatura por motivos de vinculación y validación.
¿Cómo se asigna a su modelo de dominio?
Que sea una acción de inserción, podríamos usar el mismo Automapper. Pero, ¿y si fuera una acción de actualización? Tenemos que recuperar nuestra entidad de dominio del repositorio, actualizar sus propiedades de acuerdo con los valores en ViewModel y guardar en el repositorio.
ADENDA 1 (9 de febrero de 2010): A veces, asignar las propiedades del Modelo no es suficiente. Debería tomarse alguna acción contra el Modelo de dominio de acuerdo con los valores de Modelo de vista. Es decir, algunos métodos deben llamarse en el modelo de dominio. Probablemente, debería haber una especie de capa de servicio de aplicación que se encuentre entre el controlador y el dominio para procesar los modelos de vista ...
¿Cómo organizar este código y dónde colocarlo para lograr los siguientes objetivos?
- mantener los controladores delgados
- honrar la práctica de SoC
- seguir los principios del diseño basado en dominios
- estar seco
- continuará ...
fuente
Se pueden utilizar herramientas como AutoMapper para actualizar un objeto existente con datos del objeto de origen. La acción del controlador para la actualización podría verse así:
[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); }
Aparte de lo que se ve en el fragmento de arriba:
La acción del controlador es bastante delgada y las preocupaciones están separadas: los problemas de mapeo se abordan en la configuración de AutoMapper, la validación la realiza ModelBinder y el acceso a los datos a través del repositorio.
fuente
Me gustaría decir que reutiliza el término ViewModel para ambas direcciones de la interacción del cliente. Si ha leído suficiente código ASP.NET MVC en la naturaleza, probablemente haya visto la distinción entre un ViewModel y un EditModel. Pienso que es importante.
Un ViewModel representa toda la información necesaria para representar una vista. Esto podría incluir datos que se representan en lugares estáticos no interactivos y también datos puramente para realizar una verificación para decidir qué representar exactamente. Una acción Controller GET generalmente es responsable de empaquetar el ViewModel para su Vista.
Un modelo de edición (o quizás un modelo de acción) representa los datos necesarios para realizar la acción que el usuario deseaba realizar para ese POST. Entonces, un EditModel realmente está tratando de describir una acción. Esto probablemente excluirá algunos datos del ViewModel y, aunque está relacionado, creo que es importante darse cuenta de que son realmente diferentes.
Una idea
Dicho esto, podría tener muy fácilmente una configuración de AutoMapper para ir desde Modelo -> ViewModel y una diferente para ir desde EditModel -> Modelo. Luego, las diferentes acciones del controlador solo necesitan usar AutoMapper. Demonios, el EditModel podría tener funciones para validar sus propiedades contra el modelo y aplicar esos valores al modelo en sí. No está haciendo nada más y tiene ModelBinders en MVC para asignar la Solicitud al EditModel de todos modos.
Otra idea
Más allá de eso, algo en lo que he estado pensando recientemente y que funciona con la idea de un ActionModel es que lo que el cliente te está enviando es en realidad la descripción de varias acciones que realizó el usuario y no solo una gran cantidad de datos. Esto ciertamente requeriría algo de Javascript en el lado del cliente para administrar, pero creo que la idea es intrigante.
Básicamente, a medida que el usuario realiza acciones en la pantalla que le ha presentado, Javascript comenzaría a crear una lista de objetos de acción. Un ejemplo es posiblemente el usuario está en la pantalla de información de un empleado. Actualizan el apellido y agregan una nueva dirección porque el empleado se ha casado recientemente. Bajo las sábanas, esto produce un
ChangeEmployeeName
y unAddEmployeeMailingAddress
objetos en una lista. El usuario hace clic en 'Guardar' para confirmar los cambios y usted envía la lista de dos objetos, cada uno de los cuales contiene solo la información necesaria para realizar cada acción.Necesitaría un ModelBinder más inteligente que el predeterminado, pero un buen serializador JSON debería poder encargarse de la asignación de los objetos de acción del lado del cliente a los del lado del servidor. Los del lado del servidor (si se encuentra en un entorno de 2 niveles) podrían tener fácilmente métodos que completen la acción en el modelo con el que trabajan. Entonces, la acción del controlador termina obteniendo un Id para que la instancia del modelo extraiga y una lista de acciones para realizar en ella. O las acciones tienen la identificación para mantenerlas muy separadas.
Entonces, tal vez algo como esto se realice en el lado del servidor:
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 }
Eso realmente hace que la acción de devolución sea bastante genérica, ya que confía en su ModelBinder para obtener la instancia de IUserAction correcta y su instancia de IUserAction para realizar la lógica correcta en sí misma o (más probablemente) llamar al Modelo con la información.
Si estuviera en un entorno de 3 niveles, IUserAction podría convertirse en simples DTO para disparar a través del límite y realizar con un método similar en la capa de la aplicación. Dependiendo de cómo haga esa capa, podría dividirse muy fácilmente y aún permanecer en una transacción (lo que me viene a la mente es la solicitud / respuesta de Agatha y aprovechar el mapa de identidad de DI y NHibernate).
De todos modos, estoy seguro de que no es una idea perfecta, requeriría algo de JS en el lado del cliente para administrar, y aún no he podido hacer un proyecto para ver cómo se desarrolla, pero la publicación estaba tratando de pensar en cómo ir y volver, así que pensé que daría mis pensamientos. Espero que sea de ayuda y me encantaría conocer otras formas de gestionar las interacciones.
fuente
No necesita mapear el modelo de vista al dominio porque su modelo de vista puede crearse más que el modelo de dominio. Modelos de vista optimizados para pantalla (ui) y diferentes del modelo de dominio.
http://lostechies.com/jimmybogard/2009/06/30/how-we-do-mvc-view-models/
fuente