2012-03-04 3 views
5

Я пытаюсь разработать лучший способ использования viewmodel в случае создания нового объекта.ASP.NET MVC 3 Viewmodel Pattern

У меня есть очень простая модель просмотра, которая содержит контактный объект и список компаний.

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

У меня есть контактный контроллер, который использует эту модель просмотра и позволяет мне выбрать связанную компанию для моего контакта.

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

Моя проблема заключается в создании метода на контроллере.

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

Проблема в том, что представление возвращает пустой контактный объект, но! идентификатор компании заполнен, это связано с тем, что раскрывающийся список явно объявляет имя своего поля.

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

Стандартных полей формы HTML передать значение объектов обратно в формате contact.forename при использовании HTML.EditorFor помощника ...

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

я могу получить доступ к ним, если я использую FormCollection как мое создание действия метод paremeter, а затем явно искать contact.value, но я не могу использовать объект Contact в качестве параметра, чтобы мой код был красивым и чистым и не нужно каждый раз создавать новый контактный объект.

Я попытался передать объект модели реального объекта в качестве параметра, но это просто взрывает ошибку конструктора (что путает видя, что представление привязано к модели представления, а не к объекту-контакту).

Есть ли способ, которым я могу определить имя поля Html.EditFor, чтобы значение правильно вернулось обратно к контактному объекту при его возврате к методу создания действия на моем контроллере? Или я допустил ошибку FUBAR где-то (это наиболее вероятное объяснение, поскольку это упражнение!).

ответ

15

Ваш взгляд модели кажется неправильным. Модели не должны ссылаться на какие-либо сервисы. Модели просмотра не должны ссылаться на любые модели домена. Модели просмотра должны иметь конструкторы без параметров, чтобы их можно было использовать в качестве параметров действия POST.

Так вот более реалистичный вид модели для сценария:

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

    ... other properties that the view requires 
} 

и тогда вы могли бы иметь действие GET, который будет готовить и заселить эту точку зрения модели:

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

и действие 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); 
    } 
} 

и теперь, на ваш взгляд, вы работаете с вашей моделью:

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

    ... other input fields for other properties 

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

Спасибо, что решил проблему, позволив мне сопоставить viewmodel с методом create action, единственной проблемой, с которой я тогда столкнулся, было то, что значение drop down не было связано, я решил это, изменив выпадающее имя до ** @ Html.DropDownList ("contact.parent_company_id", Model.ContactCompanyList).** Мой вопрос только в том, почему вы вручную сопоставляете? У вас есть модель, которая содержит населенный контактный объект, нет ничего, что можно было бы отобразить? –

+1

@DavidAbraham, я сопоставлен вручную, чтобы проиллюстрировать процесс. В реальном приложении я использую AutoMapper: http://automapper.org/ для выполнения этой работы. –

+0

Но опять-таки нет необходимости в Automapper, в моем случае я теперь передал объект viewmodel, который содержит объект домена-адреса, который теперь заполнен полностью, нет ничего, чтобы отобразить - куда в него входит auto-mapper? –