2011-10-30 13 views
34

Tengo curiosidad de todas las formas en que las personas construyen sus ViewModels y por qué eligen ese método.¿Cómo está rellenando/validando sus ViewModels?

me ocurren varias maneras aquí:

-1. Repositorio inyectado: el controlador carga el modelo y se asigna al ViewModel. Aquí, el constructor de ViewModel podría tomar varias colecciones para establecerlas de forma interna para ex. en una lista de selección como:

 

public CustomerController(ISomeRepository repository) 
{ 
    _repository = repository; 
} 

public ActionResult Create() 
{ 
    CustomerCreateViewModel model = new CustomerCreateViewModel(_repository.GetShipTypes, 
                   _repository.GetStates); 
.. 
.. 
} 
 

-2. ViewModelBuilder: ya sea inyectado o instanciado en el controlador con una instancia del repositorio inyectado. Llamado a través de algo así como

>var orderViewModel = orderViewModelBuilder.WithStates().Build(orderId);

o,

var orderViewModel = orderViewModelBuilder.WithStates().Build(orderId);

-3. Directamente en el controlador (no se requiere código - es complicado)

-4. Algún otro servicio (inyectado o no) que devuelve modelo de dominio, que el controlador asigna a continuación, o un modelo de vista (cualquiera que haga esto para devolver un modelo de vista que no se nombra específicamente/observa como una clase constructor de modelo de vista?)


public JobCreateViewModel BuildJobCreateViewModel(int parentId) 
{ 
    JobCreateViewModel model = new JobCreateViewModel(); 
    model.JobStatus = _unitOfWork.JobRepository.GetJobStatuses(); 
    model.States=_unitOfWork.StateRepository.GetAll(); 
    return model; 
} 

Ahora, en el viaje de regreso - con respecto a la validación de sus modelos de vista - hereda de una clase base ViewModel para validaciones estándar, o copia sus validaciones (ej. Atributos de anotación de datos) entre todos sus modelos de vista, o simplemente confiando en la validación del lado del servidor ¿todo se puede validar contra su objeto de dominio?

¿Algún otro? ¿Algo mejor? ¿Por qué?

EDIT Basado en un enlace a continuación, encontré un buen artículo de Jimmy Bogard sobre la arquitectura de ViewModels. Si bien no aborda la pregunta anterior directamente, es una gran referencia para cualquiera que venga aquí para obtener información de ViewModel. http://lostechies.com/jimmybogard/2009/06/30/how-we-do-mvc-view-models/

+0

había un voto negativo aquí, solo curiosidad, ¿algo que pueda aclarar? –

+4

En mi teléfono e inadvertidamente hice clic mientras intentaba protagonizar, lo siento. Ahora es muy tarde para deshacer. – Jason

+0

ah no se preocupe, entonces:) –

Respuesta

14

Inyecto un servicio en el controlador, no en un repositorio, y luego uso AutoMapper para convertirlo en un modelo de vista. El beneficio de la capa de servicio en este caso es que podría agregar múltiples operaciones simples de uno o más repositorios en una sola operación exponiendo un modelo de dominio. Ejemplo:

private readonly ICustomerService _service; 
public CustomerController(ICustomerService service) 
{ 
    _service = service; 
} 

[AutoMap(typeof(Customer), typeof(CustomerViewModel))] 
public ActionResult Create(int id) 
{ 
    Customer customer = _service.GetCustomer(id); 
    return View(customer); 
} 

en este ejemplo AutoMapa es un filtro de acción personalizada que puedo escribir que se ejecuta después de la acción del controlador, inspecciona el objeto devuelto y utiliza las asignaciones de AutoMapper definidos para asignar al tipo de destino especificado. Por lo tanto, la vista obtiene el ClienteViewModel correspondiente como tipo de modelo. Habría sido equivalente a:

public ActionResult Create(int id) 
{ 
    Customer customer = _service.GetCustomer(id); 
    CustomerViewModel vm = Mapper.Map<Customer, CustomerViewModel>(customer); 
    return View(vm); 
} 

es sólo que es demasiado plomería y código repetitivo que podría centralizarse.

También le recomendaría que vea el putting your controllers on a diet video de Jimmy Bogard.

+0

Gracias Darin. Vi a alguien hacer una demostración de un atributo AutoMap similar: veo que Jimmy tiene uno disponible. ¿Qué haces para la validación del cliente de estos modelos de vista (¿o no?) Y compartir la lógica de validación entre ellos? ¿Estás inyectando tus repositorios en tu capa de servicio? –

+0

@AdamTuliper, utilizo FluentValidation.NET para la validación del lado del servidor y para escenarios simples de validación del lado del cliente (requerido, fecha mayor que, ...). Para escenarios de validación más complejos del lado del cliente (propiedades dependientes, ...) decido si necesito manejarlo en el cliente y si hay un requisito para realizar la validación del lado del cliente para esos escenarios, escribo jquery validate appenders. En lo que respecta a los repositorios en la capa de servicio, sí, la capa de servicio usa la inyección ctor para tener todos los repositorios que permiten realizar operaciones simples CRUD en modelos de dominio. –

+0

Gracias Darin, otra buena respuesta:) –

0

Nuestro método consiste en inyectar el repositorio en el controlador y asignarlo a ViewModel utilizando Automapper http://automapper.org/. Nuestros ViewModels contienen atributos de anotación de datos para permitir que la validación ocurra en el cliente.

Llamamos a métodos en el repositorio que devuelven objetos de dominio (Entity Framework). Los objetos de dominio se asignan a ViewModel. Tendemos a usar el mismo modelo de vista para ediciones y agregaciones para que las anotaciones de datos sean necesarias una vez. En su forma más simple, se ve como el siguiente código:

public ActionResult List(int custId, int projId) 
    { 
     var users = _userRepository.GetByCustomerId(custId); 
     var userList = Mapper.Map<IEnumerable<CMUser>, IEnumerable<UserListViewModel>>(users); 
     return View(userList); 
    } 
+0

pero un repositorio no está asignado a un modelo de vista. Se debe llamar a algún método en el repositorio para devolver un objeto de dominio o un modelo de vista. Además, esto no cubre si usa anotaciones separadas para cada modelo o ubicación compartida, etc. –

+0

Además, si usa el mismo modelo de vista para editar/agregar, ¿qué está haciendo con un campo de ID entero que se requiere de forma predeterminada con anotaciones de datos? ? Si falta en el formulario, fallará validaton. Los Ints son obligatorios por defecto, por lo que tendrías que inicializar a 0 para el escenario de "crear", ¿no? –

1

Acabo de terminar un proyecto en el que hicimos una variación en el # 4. Tuvimos una clase de servicio inyectada en el controlador. La clase de servicio tenía dependencias en el repositorio y una clase de generador de modelos (lo llamamos fábrica de modelos).

El controlador llamó a la clase de servicio, que manejó la lógica de validación comercial, y luego buscó modelos de vista de la fábrica apropiada. Los modelos en sí mismos se basaron en anotaciones de datos para validación de entrada.

Funcionó muy bien para nuestro equipo. Hubo suficiente separación de preocupaciones para permitir a los desarrolladores hacer su trabajo sin afectarse unos a otros, pero fue lo suficientemente manejable como para entender lo que estaba sucediendo.

Es la primera vez que lo probamos y nos mantendremos con él. Me interesa ver cómo responden los demás.

+0

gracias. ¿Entonces su clase de servicio fue inyectada a su vez con el repositorio? ¿Cómo se configuraron sus métodos para ex. en la clase de servicio? ¿Cómo manejó la validación del lado del cliente en varios modelos de vista pero similares? (por ejemplo, editar frente a crear) –

+0

es muy similar al ejemplo de Darin a continuación, teníamos un requisito loco de que no pudiéramos usar software de código abierto, por lo que no podíamos usar Automapper. La próxima vez reemplazaremos la fábrica de modelos de visualización con Automapper. Usamos plantillas de visualización y edición para servir las diferentes vistas del mismo modelo, la anotación de datos nos proporcionó toda la validación que necesitábamos. – Jason

+0

Y la clase de generador de modelos recibe el objeto de repositorio desde un parámetro de constructor (servicio maneja el objeto de repositorio) o el constructor de modelos usa directamente el repositorio en sí mismo? ¿Puedes darnos un ejemplo muy simple? – Muflix

0

Utilizo una capa de servicio que oculta el modelo de dominio del controlador y devuelve ViewModels de los métodos de servicio. Esto me permite realizar cambios en el modelo de dominio sin afectar al cliente.

Cuestiones relacionadas