为何复用DbContext查询实体时返回过时值而非数据库最新数据?
问题原因解答
核心原理
你遇到的现象是Entity Framework 中 DbContext 的默认一级跟踪缓存机制导致的:
当你第一次通过当前DbContext查询到主键为X的Fubar实体时,EF会将该实体实例存入内置的跟踪缓存中。后续重复查询相同主键的实体时,哪怕EF仍然会向数据库发送查询请求拿到最新的行数据,也不会更新已经存在于缓存中的实体属性,只会直接返回第一次缓存的旧实例,因此你拿到的MyProp永远是初始查询时的过时值。
可选解决方案
除了你已经验证的「每次循环重新实例化DbContext」之外,还有以下可行方案:
- 单次查询禁用跟踪:查询时加上
AsNoTracking()扩展方法,让EF不把返回的实体加入缓存,每次直接返回数据库最新数据
// 每次查询都会拿到最新数据,不会复用缓存实例 Fubar fubar = context.Fubars.AsNoTracking().Single(f => f.Id == X);
注意:该方案返回的实体不会被DbContext跟踪,如果需要修改后保存,需要手动调用Update()方法附加实体。
- 全局关闭DbContext的跟踪功能:在DbContext配置时指定跟踪行为为不跟踪,适用于只读场景
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); }
- 手动刷新缓存实体:如果你必须复用同一个DbContext实例,可以手动调用Reload方法强制刷新指定实体的缓存值
Fubar fubar = context.Fubars.Single(f => f.Id == X); context.Entry(fubar).Reload();
调用后会直接用数据库的最新值覆盖当前缓存实体的所有属性。
内容的提问来源于stack exchange,提问作者A.D.
相关产品推荐
相关产品推荐

