实现ASP.NET MVC 3接口模型绑定(使用ViewModel)

时间:2011-04-21 21:03:39

标签: .net validation asp.net-mvc-3 viewmodel model-binding

我刚刚学习ASP.NET MVC 3而且我对如何实现类似于此链接中显示的内容感到困惑:

http://www.codethinked.com/easy-and-safe-model-binding-in-aspnet-mvc

为什么我很困惑很可能是基于我对这个主题的无知。在上面链接的页面上的示例中,我看到Justin使用的ViewModel直接包含其“Person”对象的属性。

我想要做的是类似的,但我希望ViewModel实际上包含域类,在我的案例中是公司,而不是Person。我正在使用Company类本身实现ViewModel子集视图(例如IEditCompanyAsUser)。

到目前为止,我所尝试的内容看起来像是这样:

public class Company : IValidatableObject, ICompanyUpdateAsUser
{
    [Key]
    [ScaffoldColumn(false)]
    public virtual int CompanyID { get; set; }

    [Required]
    public virtual Boolean Active { get; set; }

    [Required]
    public virtual string Name { get; set; }

    [Required]
    public virtual string Phone { get; set; }

    // Containing some more members below
 }

使用包含属性的ICompanyUpdateAsUser我希望我的控制器的Edit方法更新:

public interface ICompanyUpdateAsUser
{
    bool Active { get; set; }
    string Name { get; set; }
    string Phone { get; set; }  
}

ViewModel类看起来像这样:

public class CompanyViewModel
{
    public Company Company;
    // Will be adding more members in the future
}

我无法弄清楚如何正确编码控制器。我的控制器目前看起来像这样:

    /// <summary>
    /// POST: /Company/Edit/5
    /// </summary>
    [HttpPost]
    public ActionResult Edit([Bind(Prefix="Company")]Company model)
    {
        var company = _companyService.GetCompany(model.CompanyID);

        if (company == null)
            return RedirectToAction("Index");

        if (ModelState.IsValid)
        {
            if (TryUpdateModel<ICompanyUpdateAsUser>(company))
            {
                _companyService.SaveCompany();
                return RedirectToAction("Details", new { companyId = model.CompanyID });
            }
        }

        return View(model);
    }

我知道我的控制器代码完全有问题,但我无法弄清楚我需要使用哪些组件来实现这样的模式?我需要使用自定义模型绑定器吗?我是否需要使用ASP.NET MVC3的其他一些功能来实现该模式?

任何建议都将不胜感激。

谢谢!

*的 修改 *

我需要做的是让Edit视图回发一个“Company”对象,然后使用post'ed Company上的值(根据传递给TryUpdateModel的接口上的属性)从数据库更新对象。一切似乎都在起作用(验证等),但TryUpdateModel(公司)似乎没有工作,尽管评估为true。当我使用调试器逐步执行代码时,从数据库中提取的公司对象永远不会更新。

经过多一点阅读后,我确定TryUpdateModel方法更新了方法的IValueProvider中传递的项目。如何将传入的“公司模型”与方法的IValueProvider相关联?

2 个答案:

答案 0 :(得分:2)

我最后通过首先进行一些修改来解决问题。在我的问题中,我的控制器无法使用我创建的ViewModel类。一旦我将控制器修改为如此:

 /// <summary>
    /// POST: /Company/Edit/5
    /// </summary>
    [HttpPost]
    public ActionResult Edit(CompanyViewModel viewModel)
    {
        var company = _companyService.GetCompany(viewModel.Company.CompanyID);

        if (company == null)
            return RedirectToAction("Index");

        if (ModelState.IsValid)
        {
            if (TryUpdateModel<ICompanyUpdateAsUser>(company, "Company"))
            {
                _companyService.SaveCompany();
                return RedirectToAction("Details", new { companyId = viewModel.Company.CompanyID });
            }
        }

        return View(viewModel);
    }

我发现一切似乎都正常工作,但正如上面的评论中所述,TryUpdateModel根本无法正常工作,尽管返回true!

筛选了大量Stack Overflow&amp;其他论坛帖子,我最终发现TryUpdateModel不适用于字段,只适用于属性。为了使其正常工作,我必须修改我的ViewModel,如下所示:

public class CompanyViewModel
{
    public Company Company { get; set; }

    public CompanyViewModel(Company company)
    {
        Company = company;
    }

    public CompanyViewModel()
    {
        Company = new Company();
    }
 }

这是一个愚蠢的错误,虽然我觉得我读过的很多模型绑定教程都忽略了这一点。

希望这个答案可以节省一些时间。

答案 1 :(得分:0)

TryUpdateModel应该有一个带有前缀的重载。由于听起来您的原始视图模型包含您的Company对象,因此为Company对象生成的表单字段的名称(假设您使用的是标准表单输入名称)包含前缀“Company”,它看起来像您已经使用该方法所采用的模型识别出这一点,请在更新域对象时尝试使用相同的前缀。

仅供参考,在视图模型中使用域对象通常不是最佳做法,主要原因是可能存在您不希望用户更新的字段。如果用户对您的域有足够的了解,他们可以在帖子数据中添加其他表单字段,而MVC会盲目地更新这些属性/字段,这可能会导致问题。有一些方法可以告诉MVC在绑定时排除某些属性/字段,但它会成为维护的噩梦,通常更容易为视图使用单独的对象。