2012-03-04 18 views
5

Sto cercando di elaborare il modo migliore di utilizzare un viewmodel nel caso di creazione di un nuovo oggetto.ASP.NET MVC 3 Viewmodel Pattern

Ho un modello di vista molto semplice che contiene un oggetto contatto e un elenco selezionato di società.

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");  
} 

Ho poi regolatore di contatto che utilizza questo ViewModel e mi permettono di selezionare una società collegata per il mio contatto.

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

Il mio problema è con il metodo di creazione sul controller.

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

Il problema è che la vista restituisce un oggetto contatto vuoto, ma! l'ID azienda è popolato, questo perché l'elenco a discesa dichiara esplicitamente il nome del campo.

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

I campi del modulo HTML standard passano i valori oggetti di nuovo nel formato di contact.forename quando si utilizza il HTML.EditorFor aiutante ...

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

posso accedervi se uso un FormCollection come la mia azione creare metodo paremeter e quindi cercare esplicitamente contact.value ma non riesco a utilizzare un oggetto Contact come parametro per mantenere il mio codice piacevole e pulito e non dover creare un nuovo oggetto contatto ogni volta.

Ho provato a passare l'oggetto del modello di vista attuale come parametro ma questo si apre semplicemente con un errore di costruzione (che è confuso visto che la vista è legata al modello di vista e non all'oggetto contatto).

Esiste un modo per definire il nome del campo Html.EditFor in modo che il valore venga mappato correttamente all'oggetto contatto quando viene restituito al metodo di azione di creazione sul mio controller? Oppure ho fatto qualche errore FUBAR da qualche parte (questa è la spiegazione più probabile visto che questo è un esercizio di apprendimento!).

risposta

15

Il tuo modello di visualizzazione sembra sbagliato. I modelli di vista non dovrebbero fare riferimento a nessun servizio. I modelli di vista non dovrebbero fare riferimento a modelli di dominio. I modelli di vista dovrebbero avere costruttori senza parametri in modo che possano essere utilizzati come parametri di azione POST.

Quindi, ecco un modello di visione più realistica per lo scenario:

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

    ... other properties that the view requires 
} 

e quindi si potrebbe avere un'azione GET che preparerà e popolare questo modello vista:

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); 
} 

e un'azione 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); 
    } 
} 

e ora nella vista si lavora con il modello di vista:

@model ContactCompanyViewModel 
@using (Html.BeginForm()) 
{ 
    @Html.DropDownListFor(x => x.SelectedCompanyId, Model.CompanyList) 

    ... other input fields for other properties 

    <button type="submit">Create</button> 
} 
+0

Grazie, che ha risolto il problema che mi permette di mappare il ViewModel al metodo action create, l'unico problema allora che avevo era che la caduta verso il basso il valore non era legato, ho risolto questo cambiando il menu a discesa nome a ** @ Html.DropDownList ("contact.parent_company_id", Model.ContactCompanyList).** La mia unica domanda riguarda il motivo per cui stai mappando manualmente? Hai il modello che contiene un oggetto di contatto popolato, non c'è nulla da mappare? –

+1

@DavidAbraham, ho mappato manualmente per illustrare il processo. In un'applicazione reale uso AutoMapper: http://automapper.org/ per fare questo lavoro. –

+0

Anche in questo caso non c'è bisogno di Automapper, nel mio caso ora ho restituito un oggetto viewmodel che contiene un oggetto dominio di contatto che ora è popolato completamente, non c'è nulla da mappare - da dove viene il mappatore automatico? –