Asp.net mvc 更新多对多关系实体框架

Asp.net mvc 更新多对多关系实体框架,asp.net-mvc,entity-framework-6,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> Ava

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

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; }
    }
但是,类别不会得到更新。EF所做的是在类别表中插入空行,并与用户设置与新行的关系

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


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

我注意到您还将User.AvailableCategories直接添加到dbUser.AvailableCategories中。我注意到,从MVC视图绑定复杂对象时,DB实体不再附加到DbContext。如果查看实体,可以通过检查dbContext.Entrycat.State是否已分离或我认为是意外情况来进行验证

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

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

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

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

当您执行类似于回传M2M关系的操作时,您必须发布完整对象,就像在这些对象上的每个属性中一样,或者简单地发布一个ID列表,然后使用这些ID从数据库中查询相关对象。否则,实体框架理解您的目的是更新对象的属性,在本例中是使用空值

显然,第一种方法相当笨拙,因此第二种方法是首选的标准方法。通常,为此,您希望使用视图模型,以便拥有如下属性,并将其发布到:

public List<long> SelectedCategories { get; set; }
一旦您有了ID:

var newAvailableCategories = _context.Categories.Where(m => selectedCategories.Contains(m.Id));
最后在用户上设置:

dbUser.AvailableCategories = newAvailableCategories;

那么,使用ORM有什么意义呢?对于原始查询,我也可以这样做。此外,同样的表格是由EFOk自动制作的,我进一步查看了您的原始帖子,我想我可能有一个解决方案。我以前遇到过这个问题,必须在DbContext之外重新查询列表项才能使事情正常进行。我保留在答案中的原始想法可能不适用,以防万一。请再次查看我的答案。实体不会丢失。发布的信息未附加到上下文中;您基本上只是将实体类用作数据传输对象。实体框架也使用它作为实体类型,这一事实无关紧要。这就是为什么在这些任务中最好使用视图模型的另一个原因,因为它非常清楚区别,并迫使您记住正确映射数据。我应该用更好的措辞@ChrisPratt
var newAvailableCategories = _context.Categories.Where(m => selectedCategories.Contains(m.Id));
dbUser.AvailableCategories = newAvailableCategories;