【问题标题】:Updating many-to-many relationship entity framework更新多对多关系实体框架
【发布时间】:2014-10-01 21:21:03
【问题描述】:

我在更新具有多对多关系的实体时遇到问题。在我的用户和类别类下面:

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

这是我存储库中的代码 sn-p

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 所做的是将空行插入到类别表中,并与用户设置与这些新行的关系。

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

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

【问题讨论】:

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


    【解决方案1】:

    我注意到您还将 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;}
    }
    

    并将其添加到数据库上下文中。此外,将链接列表放在当前的用户和类别类中。通过这种方式,您可以操纵 UserCategory 类(和 DbSet)来管理您的多对多关系。

    【讨论】:

    • 那么使用 ORM 有什么意义呢?我可以对原始查询执行相同的操作。此外,同一张表是由 EF 自动制作的
    • 好的,我仔细研究了您的原始帖子,我认为我可能有一个解决方案。我以前遇到过这个问题,不得不从 DbContext 中重新查询列表项才能正常工作。我最初的想法(我保留在答案中以防万一)可能不适用。请再看我的回答。
    • 实体不会“丢失”。发布的信息不附加到上下文中;您基本上只是将实体类用作数据传输对象。它也被实体框架用作实体类型的事实是无关紧要的。这也是为什么最好在这些任务中使用视图模型的另一个原因,因为它可以非常清楚地区分,并迫使您记住正确映射数据。
    • “丢失”是指实体不再附加到 DbContext。我应该更好地措辞@ChrisPratt
    【解决方案2】:

    当您在执行诸如回发 M2M 关系之类的操作时,您要么必须发布 full 对象,就像在这些对象的每个属性中一样,要么只需发布一个 id 列表,然后使用这些从数据库中查询相关的对象。否则,Entity Framework 会理解您的目的是更新对象的属性,在本例中为空值。

    显然,第一种方式相当笨拙,因此第二种方式是首选和标准方式。通常,为此,您需要使用视图模型,这样您就可以拥有如下属性,您可以将其发布到:

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

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

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

    一旦你有了 ID:

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

    然后最后将其设置在您的用户身上:

    dbUser.AvailableCategories = newAvailableCategories;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-14
      • 1970-01-01
      • 1970-01-01
      • 2017-01-28
      • 2011-12-17
      相关资源
      最近更新 更多