更新多对多关系实体框架

时间:2014-08-08 11:44:09

标签: asp.net-mvc entity-framework-6

我有更新有多对多关系的网友的问题。在我的用户和类别下面:

public class User : IEntity
    {
        [Key]
        public virtual long Id { get; set; }

        private ICollection<Category> _availableCategories;

        public virtual ICollection<Category> AvailableCategories
        {
            get { return _availableCategories ?? (_availableCategories = new List<Category>()); }
            set { _availableCategories = value; }
        }
    }

public class Category : IEntity
    {
        [Key]
        public long Id { get; set; }
        /// <summary>
        /// Full name or description of a category
        /// </summary>
        [StringLength(255)]
        public string FullName { get; set; }
    }

这是我的资源库中的代码段

public override void Edit(User user)
        {
            var dbUser = _context.Users.Include(x => x.AvailableCategories)
                .Single(x => x.Id == user.Id);
            var categories = _context.Categories;

            dbUser.AvailableCategories.Clear();

            foreach (var cat in user.AvailableCategories)
            {
                dbUser.AvailableCategories.Add(cat);
            }
            _context.Entry(dbUser).State = EntityState.Modified;

        }

但是类别不会更新。 EF做的是将空行插入类别表,并使用user设置与这些新行的关系。

如何更新用户以便仅更改数据库中已存在的类别?

我传递给Edit方法的用户拥有只设置了ID的AvailableCategories(其余属性为空)。

2 个答案:

答案 0 :(得分:0)

我注意到您还将user.AvailableCategories直接添加到dbUser.AvailableCategories中。我注意到,当从MVC视图绑定复杂对象时,DB实体不再附加到DbContext。如果查看实体,可以通过检查dbContext.Entry(cat)验证。我认为.State是“分离的”(或意外的)。

您必须从dbContext中查询这些实体(可能使用返回的cat.Id)。或者以其他方式手动将实体设置为“未更改”。然后将这些“非分离”项添加到dbUser.AvailableCategories中。请参阅Chris的回答,因为它显示了具体代码如何完成这项工作。

另外,我可能会使用链接实体。可能是这样的:

public class UserCategory
{
    public User User {get;set;}
    public Category Category {get;set;}
}

并将其添加到DB上下文中。此外,删除当前用户和类别类中的链接列表。这样,您可以操纵UserCategory类(和DbSet)来管理您的多对多关系。

答案 1 :(得分:0)

当您执行回发M2M关系之类的操作时,您必须发布完整对象,就像在这些对象上的每个属性中一样,或者只是发布一个id列表然后使用它们从数据库中查询关联的对象。否则,Entity Framework也会理解您更新对象属性的目的,在这种情况下为空值。

显然第一种选择非常笨重,所以第二种方式是首选和标准方式。通常,为此,您需要使用视图模型,这样您就可以拥有如下所示的属性:

public List<long> SelectedCategories { get; set; }

但是,如果您坚持直接使用该实体,只需执行以下操作即可获得相同的结果:

var selectedCategories = user.AvailableCategories.Select(m => m.Id)

一旦你有了ids:

var newAvailableCategories = _context.Categories.Where(m => selectedCategories.Contains(m.Id));

然后最终在您的用户上设置:

dbUser.AvailableCategories = newAvailableCategories;