实体框架:多个到多个插入重复项

时间:2018-03-12 07:29:49

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

我目前正在学习使用Entity Framework在新的MVC应用程序中使用它。我有一些努力在两个表之间设置多对多关系,但是它使它能够显示数据。但是,在更新实体时,EF会为链接表插入重复记录。

我的设置如下: DB Setup

BusinessUnit展示将WindowsLoginsWindowsGroups组合在一起的实体,以便通过BusinessUnit使用。 BusinessUnitWindowsLogin和BusinessUnitWindowsGroup充当多对多关系的联结表。

实体在C#中定义如下:

BusinessUnit

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 个答案:

答案 0 :(得分:0)

进一步了解实体框架对实体所做的更改跟踪,我找到了解决问题的方法。我更改了我的更新语句如下:

 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)。我不知道它是否是最佳解决方案,但它确实有效。