实体框架:多对多插入重复
Posted
技术标签:
【中文标题】实体框架:多对多插入重复【英文标题】:Entity Framework: Many To Many Inserts Duplicates 【发布时间】:2018-08-20 04:05:18 【问题描述】:我目前正在学习通过在新的 MVC 应用程序中使用实体框架来使用它。我在 2 个表之间建立多对多关系时遇到了一些困难,但它可以用于显示数据。但是,更新实体时,EF 会为链接表插入重复记录。
我的设置如下:
BusinessUnit
表示一个实体,该实体将 WindowsLogins
和 WindowsGroups
组合在一起以通过业务单元使用。 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 更好:)以上是关于实体框架:多对多插入重复的主要内容,如果未能解决你的问题,请参考以下文章