【问题标题】: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 更新,因此它在应用程序中看起来正确,但在数据库中是错误的。

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

【问题讨论】:

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


    【解决方案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 类上的实体状态以在分离的上下文状态下工作:stackoverflow.com/questions/30987806/…
    • 感谢您的回复 Max。我已经稍微更改了我的代码以产生更少的数据库调用。我现在对 WindowsGroups 进行一次调用,对 WindowsLogins 进行一次调用,以根据我要链接的 Id 一次获取所有这些。这似乎比每次迭代都调用 db 更好:)
    猜你喜欢
    • 1970-01-01
    • 2011-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多