MVC控制器不绑定ViewModels

时间:2012-09-17 11:36:34

标签: c# asp.net-mvc entity-framework asp.net-mvc-4

我希望你能帮助我,我对MVC和实体框架都很陌生,并试图解决它。我正在使用MVC4和EF5。

以下是我要做的事情的概述......

我有两个实体,Site和SiteType。每个站点可以有多个SiteType,因此它们具有多对多关系。我通过创建3个表,Site,SiteType和SiteSiteType来设置具有典型的多对多关系的数据库。 SiteSiteType只包含2列,即SiteId和SiteType Id。

我更新了实体模型,它完美地为网站创建了我的实体......

public partial class Site
{
    public Site()
    {
        this.SiteTypes = new HashSet<SiteType>();
    }

    public int Id { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }

    public virtual ICollection<SiteType> SiteTypes { get; set; }
}

这适用于SiteType

public partial class SiteType
{
    public SiteType()
    {
        this.Sites = new HashSet<Site>();
    }    
    public int Id { get; set; }
    public string Name { get; set; }    
    public virtual ICollection<Site> Sites { get; set; }
}

现在我正在使用强类型视图,但是我没有使用Site作为类型,而是使用我创建的名为SiteViewModel的自定义ViewModel,它看起来像这样......

public class SiteViewModel
{
    public Site Site { get; set; }
    public bool WasPosted { get; set; }
    public IEnumerable<SiteType> AvailableSiteTypes { get; set; }
    public IEnumerable<SiteType> SelectedSiteTypes { get; set; }
}

原因是我想对MVC使用CheckBoxList扩展,如here所述

现在一切都在我创建新网站并向其添加SiteTypes时有效,但是当我尝试编辑和现有记录时,我开始遇到问题。

这是我在控制器中的编辑GET方法....

    public ActionResult Edit(int id = 0)
    {
        Site site = _repository.GetSiteByID(id);
        if (site == null)
        {
            return HttpNotFound();
        }

        var model = new SiteViewModel();
        model.AvailableSiteTypes = _repository.GetSiteTypes();
        model.SelectedSiteTypes = site.SiteTypes;
        model.Site = site;

        return View(model);
    }

post方法最初看起来像这样....

    [HttpPost]
    public ActionResult Edit(SiteViewModel siteViewModel, int[] siteTypes)
    {

        if (ModelState.IsValid)
        {
            _repository.UpdateSite(siteViewModel.Site, siteTypes);
            _repository.Save();
            return RedirectToAction("Index");
        }

        siteViewModel.AvailableSiteTypes = _repository.GetSiteTypes();
        List<SiteType> selectedSiteTypes = _repository.GetSiteTypes(siteTypes).ToList();
        if (selectedSiteTypes.Count > 0)
        {
            siteViewModel.SelectedSiteTypes = selectedSiteTypes;
        }
        return View(siteViewModel);

    }

现在我遇到的问题是siteViewModel.Site正在从包含更新数据的表单中正确填充,但Id列被设置为“0”,即使它是现有记录而不是新记录。我已经尝试了一切,但我无法理解为什么。我能让它工作的唯一方法是接受一个“id”参数并手动将它设置为Site对象,就像这样......

HttpPost]
    public ActionResult Edit(int id, SiteViewModel siteViewModel, int[] siteTypes)
    {
        siteViewModel.Site.Id = id;
        ......

但与其他一切联系在一起的情况相比,它感觉非常混乱。

这是唯一的方法吗?我错过了什么吗?

非常感谢您花时间阅读我的帖子。对不起,它太长了,我不知道怎么解释它......

3 个答案:

答案 0 :(得分:2)

将实际实体发送到您的视图绝不是一个好主意,您的视图模型应该只获取向用户呈现视图所需的数据。如果您需要将有关每个Site的信息传递给视图,则创建一个单独的视图模型或DTO,然后传递它,例如。

siteViewModel.Site = new SiteDto()
{
    Id = site.Id,
    Name = site.Name,
    ...
}

实体具有隐藏的属性,用于将其映射回数据库中的记录,当您将实体传入/从视图传递时,此信息通常会丢失。正确的方法是向视图发送所需的所有数据,而不是更多。然后在您的POST方法中,重新拉取您的实体并应用视图模型中的更改。

如果您发现自己在整个地方都这样做,可以使用AutoMapper

答案 1 :(得分:1)

遇到同样的问题,最终将ViewModel referance名称改为Model,对于您的情况:

[HttpPost]
    public ActionResult Edit(SiteViewModel siteViewModel, int[] siteTypes)
{
...you controller code.
}

将其更改为:

[HttpPost]
    public ActionResult Edit(SiteViewModel model, int[] siteTypes)
{
...you controller code.
}

答案 2 :(得分:0)

您可能需要在编辑页面中添加SiteId,看起来像

@Html.HiddenFor(model => model.Site.Id)

然后整个模型绑定/ MVC框架应该工作并确保保留id。