EF Core 6 查询结果与数据库不一致问题排查:等待进程解除忙碌逻辑中重复查询返回异常结果
问题根源:EF Core DbContext的实体跟踪机制
你遇到的这个不一致问题,本质上是EF Core默认的实体跟踪特性在起作用,并不是你额外配置了缓存。
EF Core的DbContext会自动跟踪它从数据库查询出来的实体对象——当你第一次查询到entity后,这个实例就被DbContext存到内存的跟踪列表里了。之后你再查询同一个主键的实体时,DbContext会直接返回内存里的这个旧实例,不会去数据库拉取最新数据,哪怕你手动修改了数据库里的IsBusy值。
结合你的代码具体看:
- 第一次查询得到
entity后,DbContext已经跟踪了这个实例,所以后续的FirstOrDefaultAsync查询同一个实体时,直接返回了内存里的旧对象,导致entity.IsBusy还是true; - 而
Select(x => x.IsBusy)的查询能拿到正确结果,是因为这种投影查询不会返回完整的被跟踪实体,EF Core会直接执行SQL并返回数据库里的最新值,完全绕过了内存中的跟踪实例; - 新变量
test1同样拿到旧值,也是因为查询的是同一个主键的实体,DbContext还是返回内存里的跟踪实例。
解决方法
这里给你几个规范的解决方案:
1. 查询时禁用跟踪
在查询末尾添加AsNoTracking(),这样EF Core不会跟踪查询到的实体,每次查询都会直接去数据库获取最新数据:
entity = await _resource.GetQueryable() .Where(x => x.Slug == slug) .AsNoTracking() .FirstOrDefaultAsync();
2. 手动刷新跟踪实体
如果需要保持实体的跟踪状态(比如后续要修改并保存),可以在查询后调用ReloadAsync()从数据库重新加载最新值:
entity = await _resource.GetQueryable() .Where(x => x.Slug == slug) .FirstOrDefaultAsync(); await _dbContext.Entry(entity).ReloadAsync();
3. 使用作用域隔离的DbContext
如果你的DbContext是作用域注入的,在需要刷新数据的场景下,可以创建一个新的作用域来获取新的DbContext实例,不过这种方式在你的循环场景下可能有点重,前两种方法更合适。
你提到的用bool isBusy = entity.IsBusy;的方式只是临时绕过了问题,并没有从根本上解决跟踪导致的缓存问题,还是上面的方法更符合EF Core的使用规范。
内容的提问来源于stack exchange,提问作者J Flex
相关产品推荐
相关产品推荐

