2011-07-04 10 views
5

Me preguntaba cuál es el mejor enfoque para tener una lista de selección en un formulario que contiene valores de una base de datos sin duplicar ningún código.¿Cómo puedo generar una lista de selección a partir de los valores de la base de datos?

Lo que pensé que tendría sentido sería cargar estos datos en el controlador y pasarlos a un modelo de vista, por lo que puedo usar SelectListFor<> o lo que sea para generar la lista. Sin embargo, esto significa que tengo que duplicar toda la carga de la lista en los métodos GET y POST. La otra forma que puedo ver sería pasar el contexto de la base de datos al constructor del modelo de vista y hacer que cargue la lista, pero esto presenta dos problemas más:

1) ¿Debería el modelo de vista conocer el contexto de la base de datos?

2) No puedo usar el enlace de modelo aceptando el tipo de modelo de vista como un argumento de método porque no tiene un constructor sin argumentos (si creo un constructor sin argumento entonces no tendrá las listas si Quiero volver a mostrar la vista que contiene el formulario).

¿Hay una mejor manera de hacerlo? Parece que debe ser un escenario bastante común y cualquier consejo sería apreciado.

+0

¿Qué quiere decir con "duplicar toda la carga de la lista en los métodos GET y POST"? ¿Cómo se ve tu código actual? –

+0

@Tom Excelente pregunta, me gustaría poder hacer 5 votos a favor, pero +1 sin embargo –

Respuesta

1

normalmente Implementamos nuestras búsquedas a través de un ReferenceDataRepository que se usa dentro de los controladores de la misma manera que cualquier otro tipo de interacción repositorio. Este repositorio generalmente recibirá un gran número de llamadas para datos de solo lectura predominantemente estáticos, por lo que podemos implementar un CachedReferenceDataRepository derivado sobre esto usando una abstracción de su esquema de caché de elección (Session, AppFabric, etc.).

1

¿Por qué no obtener su DB o repositorio o regla de negocio? ¿Cómo lo llame? Envíe de vuelta un IDictionary ???

En este ejemplo se supone que tiene una lista de usuarios, se va a enviar de vuelta una clave con su ID y el valor con que digamos que primero nombre + apellidos:

A continuación, utilice esta dentro de la vista ....

<%= Html.DropDownListFor(model => model.UserID, new SelectList(Model.AvailableUsers, "Key", "Value",Model.UserID))%> 

model.UserID = Key 
Model.AvailableUsers = IDictionary<int,string> 

creo mis listas en algún código de ayuda a veces a continuación Puedo búsqueda de esos valores mediante esta ayuda ... así que hay una clase centralizado (normalmente estática) que va a generar estos "usuarios" ...

Pase estos usuarios a la vista directa o alternativamente una V iewModel como en su caso, que es lo que recomiendo

NOTA: No conectará su contexto de datos con la Lista/Encuadernación del modelo, que hace que las cosas sean demasiado complejas. Basta con echar en el ID de usuario como el usuario seleccionado de la lista a continuación en su puesto manejar apporpriately ...

modelo de vista:

public class UsersViewModel 
{ 
    public int UserID { get; set; } 
    public IDictionary<int,string> AvailableUsers{ get; set; } 
} 

En tu post ...

[HttpPost] 
    [ValidateAntiForgeryToken] 
    [DemandAuthorization(RoleNames.AdminUser, AlwaysAllowLocalRequests = true)] 
    public ActionResult AllUsers(int UserID) 
    { 
     try 
     { 
      //save to db or whatever... 
      return RedirectToAction("Index", "Users"); 
     } 
     catch (RulesException ex) 
     { 
      ex.CopyTo(ModelState); //custom validation copied to model state 
     } 
     var _users= _service.GetUsers(); 
     return View("AllUsers", new UsersViewModel 
     { 
      UserID = UserID, 
      AvailableUsers = _users 
     }); 

    } 
0

Pues bien, en En mi opinión, debe tener el contexto o el objeto de repositorio en su modelo de vista para mantenerse seco en este escenario. Además, también es correcto que su modelo de vista no sepa sobre su base de datos. Para hacer frente a este problema que puede tener el constructor del modelo de vista aceptar una interfaz como

public Interface IUserRepository 
{ 
    public IEnumerable<User> GetAll(); 
} 

y usted puede tener su modelo de vista como

public class CreateVM 
{ 
    private IUserRepository _repo; 
    public CreateVM(IUserRepository repo) 
    { 
     _repo = repo; 
    } 
    public IEnumerable<SelectListItem> AvailableUsers 
    { 
     get{ 
      return _repo.GetAll().Where(x=>x.isAvailable).Select(x=>new SelectListinItem{Text = x.UserName, Value = x.UserID}); 
      } 
    } 
} 

Última pieza del rompecabezas es la configuración de DI. dile a tu contenedor IOC que inyecte IUserRepository en el constructor de tu viewmodel siempre que se cree una instancia. No sé si DI puede funcionar cuando modelbinder crea una instancia de su modelo de vista, pero lo es, al menos en teoría.su viewmodel no conoce su repositorio sino solo una interfaz y su lista se crea en un solo punto para que usted también esté seco.

+2

Cada uno por su cuenta, pero no me gusta ver a los Repositorios referenciados dentro del ViewModel. Creemos que es tarea del controlador proporcionar al ViewModel toda la información que necesita a través de repositorios inyectados solo en el controlador. –

+0

@Daz tiene toda la razón, pero entonces no vamos a mantener SECO. es una paradoja, ¿no? (renunciar a un patrón a favor del otro) –

0

El mayor problema que veo con el paso de IRepository al modelo de vista es que esto puede causar fácilmente problemas de rendimiento - seleccione n + 1 de son tan natural, en este caso, que es difícil de evitar. Desea tener tan poco rountrips a DB como sea posible para la solicitud, y tener IRepository pasado por todos esos ViewModels multinivel simplemente no ayuda.

su lugar, puede introducir fábrica modelo de vista que es responsable de la creación de ViewModels tipo deseado. MyViewModelFactory dependería de IRepository, y crearía MyViewModel que es simplemente DTO.

Cuestiones relacionadas