2012-07-03 15 views
9

En un proyecto ASP.NET MVC, estamos utilizando AutoMapper para mapear desde el modelo de dominio al modelo de vista y, a veces, también aplanamos una jerarquía mientras lo hacemos. Esto funciona como un amuleto y hace que la lógica de renderizado de nuestros puntos de vista sea muy simple.Cómo cambiar el modelo de edición/postmodelo al modelo de dominio

La confusión comienza cuando queremos ir al revés de viewmodel (o postmodelo o editmodel) al modelo de dominio, especialmente al actualizar los objetos. No podemos usar automatizado/mapeo bidireccional porque:

  1. tendríamos que unflat la jerarquía aplanada
  2. todas las propiedades en el modelo de dominio tendría que ser mutable/tienen los emisores públicos
  3. los cambios Desde la vista, no siempre son solo las propiedades planas que se vuelven a asignar al dominio, sino que a veces se deben llamar a métodos como "ChangeManagerForEmployee()" o similar.

Esto también se describe en el artículo Bogards Jimmy: The case for two-way mapping in AutoMapper, pero la solución a esto no se describe en detalle, sólo que van:

De EditModel a CommandMessages - que va desde el loosely- tipeado EditModel a mensajes fuertemente tipados y descompuestos. Un único EditModel podría generar media docena de mensajes.

En un similares SO question hay una respuesta por Mark Seeman donde se menciona que

Utilizamos creadores de mapas y servicios abstractos para asignar una PostModel a un objeto de dominio

pero los detalles - la implementación conceptual y técnica, queda fuera.

Nuestra idea en este momento es:

  1. Recibir FormCollection en el método de la acción del controlador
  2. Obtener el modelo de dominio original y aplanarlo a viewModelOriginal y viewModelUpdated
  3. La fusión de la FormCollection en viewModelUpdated usando UpdateModel()
  4. Usa algún método de ayuda genérico para comparar viewModelOriginal con viewModelUpdated
  5. Cualquiera A) Generar CommandMessages un Jimmy Bogard o B la) mutar las diferencias directamente en el modelo de dominio a través de propiedades y métodos (tal vez que trazan los 1-1 propiedades directamente a través de AutoMapper)

Puede alguien proporcionar algunos ejemplos de cómo vienen de FormCollection través editmodel/postmodel a modelo de dominio? "CommandMessages" o "mapeadores y servicios abstractos"?

+1

Daría esta pregunta mil votos a favor si pudiera. He estado buscando infinitamente una respuesta sólida a esto. – devuxer

+0

¿Qué proceso usaste al final? –

+0

Hola Tom. Terminamos usando una solución de mezcla y coincidencia, donde 1. Recuperamos el objeto Dom de DAL/DB, 2. aplanamos el objeto Dom en ViewModel, 3. usamos TryUpdateModel con FormColl en ViewModel, 4.use AutoMapper para hacer un mapeo inverso desde el modelo de vista aplanado a Dom (jerarquía) en propiedades simples y 5. use un servicio para mapear propiedades no triviales del modo de vista a métodos Dom (objeto Dom y/o Dom Service). Espero que esto ayude. No estoy seguro de qué respuesta marcar como correcta, ya que es una mezcla de más de uno ... –

Respuesta

1

uso el siguiente patrón:

[HttpPost] 
public ActionResult Update(UpdateProductViewModel viewModel) 
{ 
    // fetch the domain model that we want to update 
    Product product = repository.Get(viewModel.Id); 

    // Use AutoMapper to update only the properties of this domain model 
    // that are also part of the view model and leave the other properties unchanged 
    AutoMapper.Map<UpdateProductViewModel, Product>(viewModel, product); 

    // Pass the domain model with updated properties to the DAL 
    repository.Update(product); 

    return RedirectToAction("Success"); 
} 
+0

Hola Darin, gracias por tu respuesta, pero estoy buscando cómo mutar/traducir el modelo de vista aplanado de nuevo en la jerarquía/agregado del modelo de dominio no solo a través de mapeo/propiedades sino también mediante métodos. –

+0

Parece que este patrón daría como resultado un mapeo poco claro del objeto de dominio. – Artyom

1

Es posible que desee considerar CQRS (Segregación Responsabilidad comando de consulta - Creo que esto podría ser el concepto que faltaban), posiblemente incluso con evento de abastecimiento.

Es básicamente una práctica de separar la lógica de lectura de una fuente de datos y escribir en una fuente de datos, incluso puede significar tener diferentes modelos de datos para leer y escribir.

Este podría ser un buen lugar para empezar: http://abdullin.com/cqrs/

+0

Hola Pawel, he estado mirando CGRS antes, tal vez lo investigue de nuevo. Ya tengo algunas ideas sobre cómo separar la lógica, pero específicamente estoy buscando cómo traducir el FormCollection/EditModel al modelo de dominio a través de mensajes de comando, pero ¿cómo sé qué mensajes crear? O si está haciendo servicios (mapeadores abstractos) ¿cómo sé lo que ha cambiado? Mi EditModel solo contiene el resultado de los cambios, no los cambios/mensajes mismos. –

+0

Como entiendo su situación es: en la interfaz de usuario tiene una forma grande que tiene, digamos, 20 campos y valores ingresados ​​pero no sabe cuáles a medida que envía todos los valores al servidor (después de que el usuario presionó "Guardar")) Cómo podría abordarlo, tratando de lograr la separación de comando/consulta, es que dividiría la acción "Guardar" en comandos (los comandos provienen de la interfaz de usuario). En la práctica, significaría devolver solo los campos que han sido alterados. Y no creo que vincule los valores con el modelo de vista, sino que los trato simplemente como una lista de comandos. –

+0

Así es, tengo un gran formulario de edición que se completa con el modelo de vista aplanada. Cuando el usuario edita y envía el formulario nuevamente al controlador, no puedo ver qué campos se han cambiado. Mi idea es comparar un conjunto de modelos de vista "Antes" y "Después" (_el original que se completó y uno actualizado que es el original + valores de publicación de formulario fusionados a través de UpdateModel_) y luego generar comandos desde allí (lado del servidor). Así que supongo que depende de lo que quiere decir con "UI": UI como en el lado del cliente JS o UI como en el lado del servidor + controlador? –

0

Opción C: Ponga todo en la acción del controlador. Luego, si eso se pone peludo, descompóngase en servicios (mapeadores abstractos) o en mensajes-como-métodos (el modo de mensaje de comando).

mensaje

Comando manera:

public ActionResult Save(FooSaveModel model) { 
    MessageBroker.Process(model); 

    return RedirectToAction("List"); 
} 

y el procesador:

public class FooSaveModelProcessor : IMessageHandler<FooSaveModel> { 

    public void Process(FooSaveModel message) { 
     // Message handling logic here 
    } 

} 

Esto es realmente sólo acerca de cómo mover el "proceso" de la forma de la acción del controlador y en los controladores individuales, especializados .

Pero, realmente solo iría por esta ruta si las acciones del controlador se ponen peludas. De lo contrario, solo tome el formulario y realice las actualizaciones apropiadas contra los modelos de dominio según sea necesario.

+0

Hola Jimmy, gracias por tu respuesta. Definitivamente estoy tratando de no ponerme los pelos de punta y mantener mis controladores ligeros como en [ASP.NET MVC 4 en acción] (http://manning.com/palermo3/). También he estado viendo los mensajes de comando (por ejemplo, [aquí]) (http://codebetter.com/iancooper/2011/04/27/why-use-the-command-processor-pattern-in-the-service-layer /) y "Usar un bus de aplicación" en el libro), pero ¿cómo sabe qué ha cambiado en su FooSaveModel? En los viejos tiempos de los formularios web, tenía un evento SelectedIndexChanged de mi menú desplegable de CurrentManager. ¿Ahora solo tengo el resultado final en FooSaveModel? –

+0

Jimmy, su implementación de 'Process (mensaje de FooSaveModel)' sería similar a [la sugerencia que hice a Pawel] (http://stackoverflow.com/questions/11313822/how-to-mutate-editmodel-postmodel-to- domain-model # comment14932448_11320582)? Es decir, en realidad no mira los modelos de salvación "antes" y "después" para ver qué ha cambiado, sino que compara y asigna el modelo de SaveModel directamente al modelo de dominio, opcionalmente llamando a los métodos según sea necesario (por ejemplo, 'ChangeManager()'). –

+0

¿Por qué debería preocuparse de qué ha cambiado o no? Si me preocupan los campos específicos que se cambian, eso podría indicar que necesito más pantallas con interfaces específicas de tareas (y validación). –

0

Aquí hay algunas similitudes con lo que he estado haciendo. Mi jerarquía de modelos de vista es solo un tanto aplanada de sus equivalentes de objeto de dominio, pero tengo que lidiar con llamar métodos de servicio explícitos en guardar para hacer cosas como agregar colecciones secundarias, cambiar valores importantes, etc., en lugar de simplemente mapear de forma inversa. También tengo que comparar fotos anteriores y posteriores.

Mi salvación es Ajax publicada como JSON en una acción de MVC y entra en esa acción mágicamente ligada a una estructura de modelo de vista por MVC. Luego uso AutoMapper para transformar el modelo de vista de nivel superior y sus descendientes a su estructura de dominio equivalente. He definido una cantidad de ITypeConverters AutoMapper personalizados para aquellos casos en los que se ha agregado un nuevo elemento secundario en el cliente (estoy usando Knockout.js) y necesito llamar a un método de servicio explícito. Algo así como:

  foreach (ChildViewModel childVM in viewModel.Children) 
     { 
      ChildDomainObject childDO = domainObject.Children.Where(cdo => cdo.ID.Equals(childVM.ID))).SingleOrDefault(); 
      if (childDO != null) 
      { 
       Mapper.Map<ChildViewModel, ChildDomainObject>(childVM, childDO); 
      } 
      else 
      { 
       MyService.CreateChildDO(someData, domainObject); // Supplying parent 
      } 
     } 

hago una cosa similar para eliminaciones y este proceso cascadas bastante bien hacia abajo a través de toda la estructura. Supongo que una estructura aplanada podría ser más fácil de trabajar o más difícil. Tengo un AbstractDomainViewModel con una ID con la que hago la coincidencia anterior, lo que ayuda.

Necesito hacer comparaciones antes y después de la actualización porque mi capa de servicio llama a la validación del desencadenante que puede afectar a otras partes del gráfico del objeto, y esto dicta lo que JSON necesito devolver como respuesta Ajax. Solo me importan los cambios que son relevantes para la UI, entonces transformo el objeto de dominio guardado de nuevo en un nuevo modelo de vista y luego tengo un método de ayuda para comparar los 2 modelos de vista, usando una combinación de comprobaciones manuales iniciales y reflexión.

Cuestiones relacionadas