ASP.NET MVC 3 视图模型模式

5

我正在尝试找出在创建新对象的情况下使用视图模型的最佳方法。

我的视图模型非常简单,包含一个联系人对象和公司的选择列表。

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

我的问题出现在控制器上的create方法。

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

问题在于视图返回了一个空的联系人对象,但是!公司ID已经填充,这是因为下拉列表显式声明了其字段名称。
 @Html.DropDownList("parent_company_id",Model.ContactCompanyList)

标准的HTML表单字段在使用HTML.EditorFor助手时,以contact.forename格式将对象值传递回去...
@Html.EditorFor(model => model.contact.forename)

如果我使用FormCollection作为我的创建操作方法参数,并显式搜索contact.value,则可以访问它们,但我不能使用Contact对象作为参数,以保持我的代码简洁清晰而不必每次构建新的联系人对象。
我尝试将实际的视图模型对象作为参数传递回去,但这只会导致构造函数错误(这很令人困惑,因为视图绑定到视图模型而不是联系人对象)。
有没有一种方法可以定义Html.EditFor字段的名称,使值在传递回控制器上的创建操作方法时正确映射到联系人对象?或者我是否犯了一些错误(这是一个学习练习,这是最有可能的解释!)。
1个回答

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

谢谢,这解决了我的问题,使我能够将视图模型映射到创建操作方法,唯一的问题是下拉框的值没有绑定,我通过更改下拉框名称来解决了这个问题@Html.DropDownList("contact.parent_company_id",Model.ContactCompanyList).我唯一的问题是为什么您要手动映射?您有包含填充的联系对象的模型,没有需要映射的内容? - David Abraham
1
@DavidAbraham,我手动映射以说明该过程。 在实际应用程序中,我使用AutoMapper:http://automapper.org/来完成此任务。 - Darin Dimitrov
3
@DavidAbraham,我的回答的重点是视图模型不应包含领域模型。 - Darin Dimitrov
让我们在聊天中继续这个讨论:http://chat.stackoverflow.com/rooms/8495/discussion-between-david-abraham-and-darin-dimitrov - David Abraham
1
在视图中使用您的领域模型会使视图依赖于领域设计。在视图中使用视图模型和映射:如果您的领域模型需要更改,最糟糕的情况是在一个地方更改映射。仅在视图中使用领域模型:如果您的领域模型发生更改,您需要更新所有使用您的领域模型的视图。当然,您的设计适用于您,并且很可能永远不会出现问题。这取决于您的选择 :) - Nope
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接