EF Core使用AsNoTracking查询后OriginalValues返回当前值问题
问题根源
你的问题核心是AsNoTracking()查询的特性和EF Core变更跟踪的原始值填充逻辑冲突导致的。
原理说明
- 当你使用
AsNoTracking()执行查询时,EF Core为了优化性能,不会对返回的实体做属性快照,也不会将实体纳入上下文的跟踪列表,实体和上下文完全独立。 - 你后续调用
DataContext.Entry(projeFound)将实体附加到上下文时,EF 没有该实体的历史快照数据,会用实体当前的属性值(也就是数据库查询出来的旧值)同时初始化OriginalValues和CurrentValues两个集合。 - 你的代码先调用
entry.CurrentValues.SetValues(proje)将CurrentValues更新为传入的新值,再手动设置entry.State = EntityState.Modified。这时EF 检测到这是一个刚附加的、没有历史快照的实体,为了生成全字段更新的SQL语句,会将OriginalValues覆盖为当前CurrentValues的最新值,这就是你拿到的OriginalValues是当前值的原因。 - 去掉
AsNoTracking()后,EF 在查询阶段就会为实体生成原始属性快照,后续不管你怎么修改CurrentValues、怎么修改实体状态,OriginalValues都会保留查询时的初始值,因此可以正常拿到旧值。
解决方案
方案1(推荐)
直接移除查询语句中的AsNoTracking(),使用默认的跟踪查询,不需要调整其他逻辑,EF 会自动维护原始值快照,是最稳妥的方案。
方案2(保留AsNoTracking)
如果你出于性能考量一定要用AsNoTracking(),调整代码执行顺序即可:先设置实体状态为Modified,再更新CurrentValues,避免OriginalValues被覆盖:
public async Task<ActionResult<int>> ProjeleriGuncelle(Proje proje) { var projeFound = DataContext.Projeler.AsNoTracking().FirstOrDefault(p => p.EntegrasyonId == proje.EntegrasyonId); var entry = DataContext.Entry<Proje>(projeFound); // 先设置状态,此时OriginalValues和CurrentValues都是查询得到的旧值 entry.State = EntityState.Modified; // 再更新CurrentValues为新值,OriginalValues不会被修改 entry.CurrentValues.SetValues(proje); await DataContext.SaveChangesAsync(); }
内容的提问来源于stack exchange,提问作者serkanz
相关产品推荐
相关产品推荐

