EF Core 3.1.0下基于AsNoTracking获取SQL Server视图字段变更的异常问题
问题核心根因
- EF Core 3.1 的 Change Tracker(变更跟踪器)内置一级缓存机制:同一个上下文实例内,只要加载过相同主键的实体,后续对同主键数据的查询,会直接返回跟踪器中缓存的实例,不会真正向数据库发送查询请求。你第一次查询
originalView时,该对象已经被上下文纳入跟踪,后续执行SaveChangesAsync()完成更新后,再调用MyView.FirstOrDefault()查询时,只要主键匹配,返回的依然是之前缓存的那份originalView实例;哪怕你对updatedView调用Reload()强制重载,因为两个变量指向同一个缓存引用,重载后两边的值会同步变化,自然永远相等,拿不到前后对比值。 - 示例代码存在低级逻辑错误:判断空值的语句
if (updatedObject = null)是赋值操作,并非等值判断,这行代码运行时会直接把updatedObject置为null,后续访问updatedObject.SomeProperty会直接抛出空引用异常,根本走不到真正的数据库更新流程,这也是数据始终没有变化的直接诱因。 - 对
AsNoTracking()的适用场景存在误解:AsNoTracking()从EF Core 3.1版本开始就完全适配官方推荐的Microsoft.Data.SqlClient,不存在仅适配旧版System.Data.SqlClient的限制,这个扩展方法的核心作用就是绕开变更跟踪器,让每次查询都直接从数据库读取最新数据,不会被一级缓存干扰。
可直接复用的实现方案
- 先修正基础代码bug:将判空逻辑改为
if (updatedObject == null),保证更新流程能正常执行。 - 拉取变更前的视图快照时,添加
AsNoTracking()绕开跟踪缓存:
这步拿到的var originalView = dbContext.MyView .AsNoTracking() .FirstOrDefault(item => item.Id == id);originalView不会被上下文纳入跟踪,不会占用缓存名额,值会固定为查询时刻的状态。 - 完成数据更新、执行完
SaveChangesAsync()之后,拉取更新后的视图数据时,同样添加AsNoTracking():
这步查询会真正向数据库发起请求,拿到的是更新完成后的最新视图数据,不会被之前的缓存实例干扰。await dbContext.SaveChangesAsync(); var updatedView = dbContext.MyView .AsNoTracking() .FirstOrDefault(item => item.Id == id); - 如果不需要依赖视图做对比,仅需要获取关联表字段的变更前后值,完全不需要反复查询数据库:EF Core 变更跟踪器本身会自动缓存每个跟踪实体的原始值、当前值,直接通过
dbContext.Entry(updatedObject).OriginalValues和dbContext.Entry(updatedObject).CurrentValues就能遍历提取所有字段的原值、新值,直接生成变更列表,性能远优于重复查询视图。
避坑提醒
不要直接拿EF跟踪的实体引用做前后对比:只要实体被上下文跟踪,后续的任何重载、值变更操作都会修改同一个内存实例,永远存不住固定的原值快照。要么第一次查原值就用AsNoTracking()取独立快照,要么手动把查询到的原值映射成无跟踪的DTO对象,避免被跟踪逻辑干扰。
内容的提问来源于stack exchange,提问作者OldShaterhan
相关产品推荐
相关产品推荐

