80

Cada artículo encontrado en Internet sobre el uso de ViewModels y la utilización de Automapper da las pautas de la asignación 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 un formulario 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 es [obviamente] relacionado con al modelo de vista original al menos en la parte de convenciones de nombres por el bien del enlace y la validación.¿Cómo volver a asignar el Modelo de Vista al Modelo de Dominio en una acción POST?

¿Cómo se puede asignar a su modelo de dominio?

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, no debería haber una especie de una capa de servicios de aplicaciones que se sitúa entre el controlador de dominio y con el fin de procesar Visualización de modelos ...


Cómo organizar el código y dónde colocarla a lograr los siguientes objetivos ?

  • mantener los controladores delgadas
  • de honor práctica SoC
  • seguimiento de dominio-Driven Principios de diseño
  • estar seco
  • Continuará ...

Respuesta

6

herramientas como puede ser AutoMapper utilizado para actualizar el objeto existente con datos del objeto fuente. La acción del controlador para la actualización podría ser:

[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 es visible en el fragmento anterior:

  • datos POST para ver modelo + validación se realiza en ModelBinder (podría ser Exended con los enlaces personalizados)
  • Tratamiento de errores (es decir, la captura de acceso a datos excepción lanza por la UAB) se puede hacer por [HandleError] filtrado

acción del controlador es bastante t hin 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.

+6

no estoy seguro AutoMapper es útil aquí, ya que no puede revertir el aplanamiento. Después de todo, el Modelo de Dominio no es un DTO simple como el Modelo de Vista, por lo tanto, puede no ser suficiente asignarle algunas propiedades. Probablemente, algunas acciones se deben realizar contra el Modelo de Dominio según el contenido del Modelo de Vista. Sin embargo, +1 para compartir un enfoque bastante bueno. –

+0

@Anton ValueInjecter puede revertir el aplanamiento;) – Omu

+0

con este enfoque no se mantiene delgado el controlador, viola SoC y DRY ... como Omu mencionó que debe tener una capa separada que se encargue de las tareas de mapeo. – Rookian

34

que utilizan una interfaz iBuilder y hacerlo operativo del ValueInjecter

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

...(Aplicación) RebuildViewModel sólo llama 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 modelo de vista escribo entrada primo es mucho más corto, pero eso no es realmente importante
espero que ayude

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

+0

Me gusta este enfoque. Una cosa en la que no tengo claro es la implementación de IBuilder, especialmente a la luz de una aplicación escalonada. Por ejemplo, mi ViewModel tiene 3 listas de selección. ¿Cómo recupera la implementación del constructor los valores de la lista de selección del repositorio? –

+0

@Matt Murrell mira http://prodinner.codeplex.com Hago esto allí, y lo llamo IMapper allí en lugar de IBuilder – Omu

+5

Me gusta este enfoque, implementé una muestra de esto aquí: https: // gist. github.com/2379583 –

4

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 modelo de vista y un modelo de edición. Pienso que es importante.

Un ViewModel representa toda la información necesaria 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 modelo de edición (o tal vez un modelo de acción) representa los datos necesarios para realizar la acción que el usuario quería hacer para esa 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 fácilmente se podría tener una configuración AutoMapper para ir de Modelo -> modelo de vista y otro diferente para pasar de EditModel -> 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 algo que he estado pensando recientemente ese tipo de obras de la idea de un ActionModel es que lo que el cliente es la publicación de nuevo a usted es en realidad la descripción de varias acciones del usuario realizado 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.

Esencialmente, como el usuario realiza acciones en la pantalla que usted ha presentado, Javascript comenzaría 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.

Así que tal vez algo como esto se realiza 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 
} 

que realmente hace que la publicación posterior acción bastante genérico, ya que está confiando en su ModelBinder para conseguir que la instancia IUserAction correcta y la instancia IUserAction para realizar la lógica correcta o (más probable) 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 gestionarlo, y no he podido hacer un proyecto para ver cómo se desarrolla, pero el post estaba tratando de pensar en cómo llegar y volver, así que pensé que iba a dar mis pensamientos. Espero que ayude y me gustaría saber de otras formas de gestionar las interacciones.

+0

Interesante. En cuanto a la distinción entre ViewModel y EditModel ... ¿está sugiriendo que para una función de edición use un ViewModel para crear el formulario y luego se una a un EditModel cuando el usuario lo publique? De ser así, ¿cómo manejaría las situaciones en las que tendría que volver a publicar el formulario debido a errores de validación (por ejemplo, cuando ViewModel contenía elementos para llenar un menú desplegable)? ¿Incluiría también los elementos desplegables en EditModel también? En ese caso, ¿cuál sería la diferencia entre los dos? – UpTheCreek

+0

Supongo que su preocupación es que si uso un EditModel y hay un error, entonces tengo que reconstruir mi ViewModel, lo que podría ser muy costoso. Yo diría que simplemente reconstruya ViewModel y asegúrese de que tenga un lugar para poner mensajes de notificación del usuario (probablemente tanto positivos como negativos, como errores de validación). Si resulta ser un problema de rendimiento, siempre puede almacenar en caché ViewModel hasta que finalice la siguiente solicitud de la sesión (probablemente sea la publicación de EditModel). –

Cuestiones relacionadas