vista una pasar mvc modelo datos controlador asp.net-mvc domain-driven-design viewmodel automapper separation-of-concerns

asp.net mvc - pasar - ¿Cómo volver a asignar el Modelo de Vista al Modelo de Dominio en una acción POST?



pasar datos de la vista al controlador mvc c# (4)

Herramientas como AutoMapper se pueden usar para actualizar objetos existentes con datos del objeto fuente. La acción del controlador para la actualización puede ser similar a:

[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); }

Además de lo que está visible en el fragmento de arriba:

  • Los datos de POST para ver la validación de modelo + se hacen en ModelBinder (podrían ser ampliados con enlaces personalizados)
  • El manejo de errores (es decir, la captura de excepciones de acceso a datos por Depósito) se puede hacer mediante el filtro [HandleError]

La acción del controlador es bastante delgada y las preocupaciones se separan: los problemas de asignación se abordan en la configuración de AutoMapper, la validación la realiza ModelBinder y el acceso a los datos por parte del Repositorio.

Cada artículo encontrado en Internet sobre el uso de ViewModels y la utilización de Automapper da las pautas del mapeo de dirección "Controlador -> Vista". Usted toma un modelo de dominio junto con todas las listas seleccionadas en un modelo de vista especializado y lo pasa a la vista. Eso está claro y bien.
La vista tiene una forma y, finalmente, estamos en la acción POST. Aquí todos los Carpetas Modelo vienen a la escena junto con [obviamente] otro Modelo de Vista que [obviamente] está relacionado con el Modelo de Vista original al menos en la parte de las convenciones de nombres por el bien del enlace y la validación.

¿Cómo se lo asigna a su modelo de dominio?

Deje 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 el modelo de vista y guardar en el repositorio.

ADDENDUM 1 (9 de febrero de 2010): A veces, la asignación de las propiedades del Modelo no es suficiente. Debería tomarse alguna medida contra el Modelo de dominio de acuerdo con los valores de Ver modelo. Es decir, se deben invocar algunos métodos en el Modelo de Dominio. Probablemente, debería haber una especie de capa de servicio de aplicación que se encuentra entre el controlador y el dominio para procesar 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 SoC
  • seguir los principios de diseño impulsados ​​por el dominio
  • ser seco
  • continuará ...

Me gustaría decir que reutilizar 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 modelo de vista y un modelo de edición. Pienso que es importante.

Un ViewModel representa toda la información requerida para representar una vista. Esto podría incluir datos que se procesan en lugares no interactivos estáticos y también datos puramente para realizar una comprobación para decidir qué representar exactamente. Una acción Controller GET generalmente es responsable de empaquetar ViewModel para su Vista.

Un EditModel (o quizás un ActionModel) representa los datos necesarios para realizar la acción que el usuario quería hacer para ese POST. Entonces, EditModel realmente está tratando de describir una acción. Esto probablemente excluirá algunos datos del ViewModel y, aunque relacionado, creo que es importante darse cuenta de que son realmente diferentes.

Una idea

Dicho esto, podría tener fácilmente una configuración de AutoMapper para ir desde Modelo -> Modelo de vista y una diferente para ir desde Modelo de Edición -> Modelo. Entonces las diferentes acciones del Controlador solo necesitan usar AutoMapper. Hell the EditModel podría tener funciones para validar sus propiedades contra el modelo y aplicar esos valores al mismo modelo. No está haciendo nada más y tiene ModelBinders en MVC para mapear la Solicitud al EditModel de todos modos.

Otra idea

Más allá de eso en lo que he estado pensando recientemente que funciona de la idea de un modelo de acción 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 la idea es intrigante, creo.

Básicamente, a medida que el usuario realiza acciones en la pantalla que usted ha presentado, Javascript comenzará a crear una lista de objetos de acción. Un ejemplo es posiblemente que el usuario esté en una pantalla de información del empleado. Actualizan el apellido y agregan una nueva dirección porque el empleado se ha casado recientemente. Bajo las cubiertas esto produce un ChangeEmployeeName y un AddEmployeeMailingAddress objetos a una lista. El usuario hace clic en ''Guardar'' para confirmar los cambios y usted envía la lista de dos objetos, cada uno con 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 ser capaz de encargarse de la asignación de los objetos de acción del lado del cliente a los del lado del servidor. Las 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 lo extraiga y una lista de acciones para realizar en él. O las acciones tienen la identificación en ellos para mantenerlos muy separados.

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 o (más probablemente) llamar al Modelo con la información.

Si estuvieras en un entorno de 3 niveles, IUserAction podría simplemente convertirse en DTO simples que se filmarían a través del límite y se realizarían de forma similar en la capa de la aplicación. Dependiendo de cómo se haga esa capa, podría dividirse fácilmente y permanecer en una transacción (lo que le 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 un poco de JS en el lado del cliente para gestionarla, y aún no he podido hacer un proyecto para ver cómo se desarrolla, pero el post estaba tratando de pensar cómo hacerlo. llegar y volver otra vez, así que pensé que iba a dar mis pensamientos. Espero que ayude y me gustaría saber de otras formas de gestionar las interacciones.



Uso una interfaz de IBuilder e implemento usando el ValueInjecter

public interface IBuilder<TEntity, TViewModel> { TEntity BuildEntity(TViewModel viewModel); TViewModel BuildViewModel(TEntity entity); TViewModel RebuildViewModel(TViewModel viewModel); }

... (implementación) RebuildViewModel solo llama a BuildViewModel(BuilEntity(viewModel))

[HttpPost] public ActionResult Update(ViewModel model) { if(!ModelState.IsValid) { return View(builder.RebuildViewModel(model); } service.SaveOrUpdate(builder.BuildEntity(model)); return RedirectToAction("Index"); }

Por cierto, no escribo ViewModel. Escribo Input porque es mucho más corto, pero eso no es realmente importante.
Espero eso ayude

Actualización: Estoy utilizando este enfoque ahora en la aplicación ProDinner ASP.net MVC Demo , se llama IMapper ahora, también hay un pdf proporcionado donde este enfoque se explica en detalle