【问题标题】:ASP.NET MVC 3 Viewmodel PatternASP.NET MVC 3 视图模型模式
【发布时间】:2012-03-04 18:13:56
【问题描述】:

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

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

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.EditorFor 帮助器时,标准 html 表单字段以contact.forename 的格式传回对象值...

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

如果我使用 FormCollection 作为我的创建操作方法参数,然后显式搜索 contact.value,我可以访问它们,但我不能使用 Contact 对象作为参数来保持我的代码整洁而不必构建每次一个新的接触对象。

我尝试将实际的视图模型对象作为参数传回,但这只会导致构造函数错误(这会让人感到困惑,因为视图绑定到视图模型而不是联系人对象)。

有没有一种方法可以定义Html.EditFor 字段的名称,以便在将值传递回控制器上的创建操作方法时正确映射回联系人对象?或者我是否在某处犯了一些 FUBAR 错误(这是最有可能的解释,因为这是一个学习练习!)。

【问题讨论】:

    标签: asp.net-mvc-3


    【解决方案1】:

    您的视图模型似乎错误。视图模型不应引用任何服务。视图模型不应引用任何域模型。视图模型应该具有无参数构造函数,以便它们可以用作 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)。 我唯一的问题是您为什么要手动映射?您的模型包含一个填充的联系人对象,没有要映射的内容?
    • @DavidAbraham,我手动映射以说明该过程。在实际应用中,我使用 AutoMapper:automapper.org 来完成这项工作。
    • 但是同样也不需要 Automapper,在我的情况下,我现在已经传回了一个 viewmodel 对象,其中包含一个现在已完全填充的联系人域对象,没有什么可以映射 - auto 在哪里mapper进来了?
    • @DavidAbraham,我回答的重点是视图模型不应包含域模型。
    • 在视图中使用域模型会使视图依赖于域设计。 Using view-models in views and mapping: 如果您的域模型需要更改,最糟糕的是在一个地方更改映射。 Only using domain-models in views: 如果您的域模型发生更改,您需要更新所有使用您的域模型的视图。确保您的设计适合您,并且很可能永远不会中断。最后由你决定:)
    猜你喜欢
    • 2010-11-24
    • 2011-10-24
    • 2011-07-15
    • 1970-01-01
    • 2010-11-18
    • 1970-01-01
    • 1970-01-01
    • 2014-01-07
    • 2012-01-03
    相关资源
    最近更新 更多