![](/img/trans.png)
[英]EF Core : Update column only for modified properties from disconnected entity
[英]EF Core disconnected update of collection navigation properties via full replacement
使用 EF Core 5.0。 我有一个 SPA 页面,它从 API 中加载了一个Group
实体及其Employee
实体集合:
var groupToUpdate = await context.Groups
.Include(g => g.Employees)
.FirstOrDefaultAsync(...);
//Used for UI, list of additional employees for selective adding
var employeeList = await context.Employees.
.Where(...)
.ToListAsync();
然后,用户通过 Javascript UI 修改groupToUpdate
实体,包括一些非导航属性,例如名称/注释。
在同一屏幕上,用户将一些员工添加到组中,从组中删除一些员工,并保留组中的一些现有员工。 所有员工都是数据库中具有现有主键的现有实体。 到目前为止所做的所有更改都只是针对内存中断开连接的实体。
当用户单击保存时, groupToUpdate
实体将发送到我的后端代码。 请注意,我们没有跟踪添加/删除/留下哪些员工,我们只是想让这个groupToUpdate
完全覆盖旧实体,特别是用新的Employees
替换旧的集合。
为了实现这一点,后端代码首先从数据库中再次加载组以开始在上下文中跟踪它。 然后我尝试更新实体,包括用新集合替换旧集合:
public async Task UpdateGroupAsync(Group groupToUpdate)
{
var groupFromDb = await context.Groups
.Include(g => g.Employees)
.FirstOrDefaultAsync(...);
// Update non-navigation properties such as groupFromDb.Note = groupToUpdate.Note...
groupFromDb.Employees = groupToUpdate.Employees;
await context.SaveChangesAsync();
}
现在,如果对Employees
集合的更改是完全替换(删除所有旧的,添加所有新的),则此方法成功。 但是只要有一些现有的Employees
被搁置,EF 核心就会抛出异常:
无法跟踪实体类型 'Employee' 的实例,因为另一个具有键值 ... 的实例已被跟踪
因此,EF Core 似乎试图同时跟踪从数据库中使用groupFromDb
新加载的Employee
实体和来自groupToUpdate
的Employee
实体,即使后者只是从断开连接状态作为参数传入。
我的问题是如何以最少的并发症处理这种更新? 是否有必要手动跟踪添加/删除的实体并添加/删除它们而不是尝试替换整个集合?
您必须指示 ChangeTracker 更新导航集合需要哪些操作。 只是替换集合不是正确的方法。
这是有助于自动执行此操作的扩展:
context.MergeCollections(groupFromDb.Employees, groupToUpdate.Employees, x => x.Id);
执行:
public static void MergeCollections<T, TKey>(this DbContext context, ICollection<T> currentItems, ICollection<T> newItems, Func<T, TKey> keyFunc)
where T : class
{
List<T> toRemove = null;
foreach (var item in currentItems)
{
var currentKey = keyFunc(item);
var found = newItems.FirstOrDefault(x => currentKey.Equals(keyFunc(x)));
if (found == null)
{
toRemove ??= new List<T>();
toRemove.Add(item);
}
else
{
if (!ReferenceEquals(found, item))
context.Entry(item).CurrentValues.SetValues(found);
}
}
if (toRemove != null)
{
foreach (var item in toRemove)
{
currentItems.Remove(item);
// If the item should be deleted from Db: context.Set<T>().Remove(item);
}
}
foreach (var newItem in newItems)
{
var newKey = keyFunc(newItem);
var found = currentItems.FirstOrDefault(x => newKey.Equals(keyFunc(x)));
if (found == null)
{
currentItems.Add(newItem);
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.