Blazor应用中用DbSet读视图后,是否需通知其数据已更新?
首先明确:确实需要注意EF Core的缓存机制,在某些场景下会返回旧数据,得针对性处理
为什么会有缓存问题?
EF Core的DbContext自带一级缓存(跟踪缓存)——当你第一次查询并加载某个实体到上下文后,它会把实体存在内存里。后续如果再执行针对该实体的跟踪查询(比如Find()、SingleOrDefault()不带AsNoTracking()),EF Core会直接返回缓存里的旧数据,不会去数据库查。
而你用context.Database.ExecuteSqlRawAsync("exec ...")调用存储过程时,EF Core完全不知道底层数据已经变更,不会自动更新它的跟踪缓存。
为什么你测试时能拿到新数据?
你的测试场景能获取更新后的数据,大概率是这两种情况:
- 你查询的是之前没被DbContext跟踪过的实体,缓存里本来就没有,所以EF Core会去数据库拉新数据;
- 你无意中用了无跟踪查询(比如代码里加了
.AsNoTracking()),这种查询不会把结果存入缓存,每次都会直接查数据库。
解决办法
根据你的需求,推荐这几种处理方式:
优先用无跟踪查询
在查询视图数据时加上.AsNoTracking(),彻底绕过一级缓存,每次都从数据库获取最新数据:var latestData = await context.EntityName.AsNoTracking().Where(/* 你的条件 */).ToListAsync();这种方式最省心,适合只读场景(你本来就是从视图读数据,更新走存储过程,完全不需要跟踪实体)。
手动清除跟踪缓存
如果必须使用跟踪查询,在调用存储过程更新数据后,清除上下文里相关实体的跟踪状态:- 清除单个实体的跟踪:
context.Entry(targetEntity).State = EntityState.Detached; - 清除所有实体的跟踪:
context.ChangeTracker.Clear();
- 清除单个实体的跟踪:
使用新的DbContext实例
Blazor中DbContext默认是Scoped生命周期,如果你在更新数据后,创建一个新的DbContext实例来查询数据,新实例没有之前的缓存,自然会去数据库拉最新数据。不过要注意Blazor的生命周期,避免不当使用导致资源问题。
额外提醒
默认情况下EF Core没有二级缓存,所以不用考虑全局缓存的问题。但如果你自己加了第三方二级缓存组件,那还需要在更新后手动失效对应缓存。
内容的提问来源于stack exchange,提问作者M.M

