使用Entity Framework Core更新PostgreSQL数据时同键实体追踪异常如何解决
问题原因
你先通过FirstOrDefault()查询到了foundFileInfo实例,这个实例已经被当前的EF Core上下文跟踪。此时你调用Update()方法传入外部的fileInfo实例,两个实例拥有相同的FileId主键值,EF Core的变更跟踪器不允许同一个上下文实例中存在两个相同主键的被跟踪实体,因此抛出了冲突异常。
解决方案
方案1:直接更新已被跟踪的实例属性(最推荐)
这个方案不需要调用Update()方法,直接修改已跟踪实例的属性值,EF Core会自动识别变更,提交更新。
public async Task SaveFileInfos(FileInfo fileInfo) { var foundFileInfo = _context.FileInfos.Where(f => f.FileId == fileInfo.FileId).FirstOrDefault(); if (foundFileInfo == null) { await _context.FileInfos.AddAsync(fileInfo); } else { // 直接赋值更新属性,有其他新增字段可依次补充 foundFileInfo.FileName = fileInfo.FileName; } await _context.SaveChangesAsync(); }
方案2:查询时禁用跟踪
如果不需要跟踪查询得到的实例,可以在查询时添加AsNoTracking()方法,这样查询得到的实例不会被上下文跟踪,调用Update()时就不会出现主键冲突。
// 仅需修改查询语句即可 var foundFileInfo = _context.FileInfos.AsNoTracking().Where(f => f.FileId == fileInfo.FileId).FirstOrDefault();
方案3:手动解绑已跟踪的实例
可以先把查询得到的已跟踪实例从上下文中解绑,再执行更新操作:
else { // 解绑已跟踪的旧实例 _context.Entry(foundFileInfo).State = EntityState.Detached; _context.FileInfos.Update(fileInfo); }
额外注意点
- 不要将DbContext注册为单例生命周期,默认的Scoped生命周期(每次请求一个实例)可以最大程度避免此类跟踪冲突问题
- 对于简单的更新场景,优先使用修改已跟踪实例属性的方案,不需要手动调用
Update(),EF Core的变更跟踪会自动处理更新逻辑
内容的提问来源于stack exchange,提问作者ChsharpNewbie
相关产品推荐
相关产品推荐

