2012-03-04 32 views
5

Estoy tratando de encontrar la mejor manera de usar un modelo de vista en el caso de crear un objeto nuevo.ASP.NET MVC 3 Viewmodel Patrón

Tengo un modelo de vista muy simple que contiene un objeto de contacto y una lista de empresas.

private ICompanyService _Service; 
public SelectList ContactCompanyList { get; private set; } 
public Contact contact { get; private set; } 

public ContactCompanyViewModel(Contact _Contact) 
{ 
    _Service = new CompanyService(); 
    contact = _Contact; 
    ContactCompanyList = GetCompanyList(); 
} 

private SelectList GetCompanyList() 
{ 
    IEnumerable<Company> _CompanyList = _Service.GetAll(); 
    return new SelectList(_CompanyList, "id", "name");  
} 

Tengo un controlador de contacto que utiliza este modelo de vista y me permite seleccionar una empresa relacionada para mi contacto.

[Authorize] 
public ActionResult Create() 
{      
    return View(new ContactCompanyViewModel(new Contact())); 
} 

Mi problema es con el método create en el controlador.

[Authorize] 
[AcceptVerbs(HttpVerbs.Post)] 
public ActionResult Create(Contact _Contact) 
{ 
    try 
    { 
     _Service.Save(_Contact); 
     return RedirectToAction("Index"); 
    } 
    catch 
    { 
     return View(); 
    } 
} 

El problema es que la vista devuelve un objeto de contacto vacío, pero! la identificación de la empresa está poblada, esto se debe a que la lista desplegable declara explícitamente su nombre de campo.

@Html.DropDownList("parent_company_id",Model.ContactCompanyList) 

Los campos de formulario HTML estándar que pasan los valores de los objetos de vuelta en el formato de contact.forename cuando se utiliza el ayudante HTML.EditorFor ...

@Html.EditorFor(model => model.contact.forename) 

puedo acceder a ellos si uso un FormCollection como mi acción create método paremeter y luego buscar explícitamente contact.value pero no puedo usar un objeto de contacto como parámetro para mantener mi código limpio y agradable y no tener que crear un nuevo objeto de contacto cada vez.

Intenté pasar el objeto del modelo de vista real de nuevo como parámetro, pero eso simplemente explota con un error de constructor (lo cual es confuso ya que la vista está vinculada al modelo de vista, no al objeto de contacto).

¿Hay alguna forma de que pueda definir el nombre del campo Html.EditFor para que el valor se correlacione correctamente con el objeto de contacto cuando se devuelve al método de acción Crear en mi controlador? O he cometido un error FUBAR en alguna parte (¡esta es la explicación más probable ya que es un ejercicio de aprendizaje!).

Respuesta

15

Su modelo de vista parece incorrecto. Los modelos de vista no deben hacer referencia a ningún servicio. Los modelos de vista no deben hacer referencia a ningún modelo de dominio. Los modelos de vista deben tener constructores sin parámetros para que puedan usarse como parámetros de acción POST.

Así que aquí está un modelo de visión más realista para su escenario:

public class ContactCompanyViewModel 
{ 
    public string SelectedCompanyId { get; set; } 
    public IEnumerable<SelectListItem> CompanyList { get; set; } 

    ... other properties that the view requires 
} 

y entonces usted podría tener una acción GET que preparará y poblar esta vista del modelo:

public ActionResult Create() 
{ 
    var model = new ContactCompanyViewModel(); 
    model.CompanyList = _Service.GetAll().ToList().Select(x => new SelectListItem 
    { 
     Value = x.id.ToString(), 
     Text = x.name 
    }); 
    return View(model); 
} 

y una acción POST :

[HttpPost] 
public ActionResult Create(ContactCompanyViewModel model) 
{ 
    try 
    { 
     // TODO: to avoid this manual mapping you could use a mapper tool 
     // such as AutoMapper 
     var contact = new Contact 
     { 
      ... map the contact domain model properties from the view model 
     }; 
     _Service.Save(contact); 
     return RedirectToAction("Index"); 
    } 
    catch 
    { 
     model.CompanyList = _Service.GetAll().ToList().Select(x => new SelectListItem 
     { 
      Value = x.id.ToString(), 
      Text = x.name 
     }); 
     return View(model); 
    } 
} 

y ahora en su vista usted trabaja con su modelo de vista:

+0

Gracias, eso solucionó el problema al permitirme asignar el modelo de vista al método de crear acción, el único problema que tuve fue que el valor desplegable no estaba vinculado, lo resolví cambiando el nombre desplegable a ** @ Html.DropDownList ("contact.parent_company_id", Model.ContactCompanyList).** Mi única pregunta es acerca de por qué está mapeando manualmente? ¿Tiene el modelo que contiene un objeto de contacto poblado, no hay nada que asignar? –

+1

@DavidAbraham, me mapeé manualmente para ilustrar el proceso. En una aplicación real, uso AutoMapper: http://automapper.org/ para hacer este trabajo. –

+0

Pero tampoco hay necesidad de Automapper, en mi caso ahora he pasado de nuevo un objeto de viewmodel que contiene un objeto de dominio de contacto que ahora está completo, no hay nada que asignar: ¿dónde entra el auto mapper? –