实体框架:多对多插入重复

Posted

技术标签:

【中文标题】实体框架:多对多插入重复【英文标题】:Entity Framework: Many To Many Inserts Duplicates 【发布时间】:2018-08-20 04:05:18 【问题描述】:

我目前正在学习通过在新的 MVC 应用程序中使用实体框架来使用它。我在 2 个表之间建立多对多关系时遇到了一些困难,但它可以用于显示数据。但是,更新实体时,EF 会为链接表插入重复记录。

我的设置如下:

BusinessUnit 表示一个实体,该实体将 WindowsLoginsWindowsGroups 组合在一起以通过业务单元使用。 BusinessUnitWindowsLogin 和 BusinessUnitWindowsGroup 用作多对多关系的连接表。

实体在 C# 中定义如下:

业务单位

public class BusinessUnit

    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id  get; set; 
    public string Name  get; set; 
    public bool IsPersonal  get; set; 
    public virtual IList<WindowsGroup> WindowsGroups  get; set; 
    public virtual IList<WindowsLogin> WindowsLogins  get; set; 

WindowsGroup(WindowsLogin 类似)

public class WindowsGroup

    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id  get; set; 
    public string Domain  get; set; 
    public string Name  get; set; 
    public ICollection<BusinessUnit> BusinessUnits  get; set; 

我在我的 DbContext 的 OnModelCreating 方法中写了这个来注册联结表和外键:

    modelBuilder.Entity<BusinessUnit>()
        .HasMany(businessUnit => businessUnit.WindowsGroups)
        .WithMany(windowsGroup => windowsGroup.BusinessUnits)
        .Map(mc =>
        
            mc.MapLeftKey("BusinessUnitId");
            mc.MapRightKey("WindowsGroupId");
            mc.ToTable("BusinessUnitWindowsGroup", "Config");
        );

    modelBuilder.Entity<BusinessUnit>()
        .HasMany(businessUnit => businessUnit.WindowsLogins)
        .WithMany(windowsLogin => windowsLogin.BusinessUnits)
        .Map(mc =>
        
            mc.MapLeftKey("BusinessUnitId");
            mc.MapRightKey("WindowsLoginId");
            mc.ToTable("BusinessUnitWindowsLogin", "Config");
        );

我的更新是这样写的:

public void Update(BusinessUnit businessUnit)
        
            var oldBusinessUnit = _unitOfWork.BusinessUnits.GetById(businessUnit.Id);

            oldBusinessUnit.Name = businessUnit.Name;
            oldBusinessUnit.WindowsGroups.Clear();
            oldBusinessUnit.WindowsLogins.Clear();
            oldBusinessUnit.WindowsGroups = businessUnit.WindowsGroups;
            oldBusinessUnit.WindowsLogins = businessUnit.WindowsLogins;

            _unitOfWork.Complete();
        

我必须清除 WindowsGroups 和 WindowsLogins 列表才能正确更新联结表,现在可以正常工作了。但是,一旦我分配了新的 WindowsGroups 或 WindowsLogins 列表,Entity Framework 就会插入重复的 WindowsGroups 或 WindowsLogins。联结表使用新的 Id 更新,因此它在应用程序中看起来正确,但在数据库中是错误的。

我愿意接受任何建议和反馈。提前谢谢!

【问题讨论】:

【参考方案1】:

深入了解 Entity Framework 对实体所做的更改跟踪,我找到了解决问题的方法。我将更新语句更改如下:

 public void Update(BusinessUnit businessUnit)
    
        var oldBusinessUnit = _unitOfWork.BusinessUnits.GetById(businessUnit.Id);

        oldBusinessUnit.Name = businessUnit.Name;
        oldBusinessUnit.WindowsGroups.Clear();
        oldBusinessUnit.WindowsLogins.Clear();

        if (businessUnit.WindowsGroups != null)
        
            var windowsGroupIds = businessUnit.WindowsGroups.Select(x => x.Id).ToList();

            foreach (var winGroup in _unitOfWork.WindowsGroups.Find(winGroup => windowsGroupIds.Contains(winGroup.Id)).ToList())
            
                oldBusinessUnit.WindowsGroups.Add(_unitOfWork.WindowsGroups.GetById(winGroup.Id));
            
        

        if (businessUnit.WindowsLogins != null)
        
            var windowsLoginIds = businessUnit.WindowsLogins.Select(x => x.Id).ToList();

            foreach (var winLogin in _unitOfWork.WindowsLogins.Find(winLogin => windowsLoginIds.Contains(winLogin.Id)).ToList())
            
                oldBusinessUnit.WindowsLogins.Add(_unitOfWork.WindowsLogins.GetById(winLogin.Id));
            
        

        _unitOfWork.Complete();
    

我将首先检查两个列表是否确实包含某些内容。然后我遍历从我的视图返回的列表,并使用 Id 直接从我的上下文中获取实体(通过 UoW)。我不知道这是否是最好的解决方案,但它确实有效。

【讨论】:

假设_unitOfWork 是您的DbContext 实例,这将对每个集合中的每个项目进行数据库调用,这对于大型集合集来说效率低下。您可能需要考虑修改 DbContext 类上的实体状态以在分离的上下文状态下工作:***.com/questions/30987806/… 感谢您的回复 Max。我已经稍微更改了我的代码以产生更少的数据库调用。我现在对 WindowsGroups 进行一次调用,对 WindowsLogins 进行一次调用,以根据我要链接的 Id 一次获取所有这些。这似乎比每次迭代都调用 db 更好:)

以上是关于实体框架:多对多插入重复的主要内容,如果未能解决你的问题,请参考以下文章

实体框架在多对多关系中重复条目

EF Core 过滤掉多对多关系中的重复实体

插入/更新多对多实体框架。我该怎么做?

多对多实体框架和存储库模式插入/更新

具有存储库模式的实体框架,将数据插入到具有多对多关系的表中

删除多对多实体框架