Blazor项目XPO UnitOfWork数据始终缓存 无法获取数据库最新数据
问题根因
该缓存问题是长生命周期XPO UnitOfWork/Session的一级缓存机制与Blazor托管模型不匹配导致,与DxDataGrid组件本身的缓存逻辑无关:
- XPO的
UnitOfWork继承自Session,所有被该实例加载过的持久化对象都会存入实例内部的身份映射(Identity Map)缓存,后续同实例查询同主键对象时,会直接返回缓存实例,不会主动拉取数据库最新值 - 你将
readUnitOfWork设置为类初始化时创建的只读长生命周期实例,在Blazor Server托管模式下,Scoped服务沿电路存活、组件实例跨多次渲染复用,这个UoW会一直持有第一次加载的旧数据 - Update方法中调用的
readUnitOfWork.GetObjectByKeyAsync<T>(item.Oid, true)虽然传入了强制加载参数,但上层调用返回的延迟执行IQueryable时仍会优先命中身份映射缓存,无法拿到最新值。
可落地修复方案
1. 废弃长生命周期readUnitOfWork,按操作粒度创建短生命周期读会话
XPO官方明确不推荐Web场景下复用长生命周期Session/UnitOfWork:读操作不需要用带变更追踪的UnitOfWork,直接用轻量普通Session即可,且每次查询新建实例、用完即释放。修改Get方法逻辑如下:
public async Task<List<T>> Get(System.Linq.Expressions.Expression<System.Func<T, bool>> criteria = null) { // 每次查询新建短生命周期读会话,using包裹自动释放 using var readSession = CreateReadSession(); var query = (IQueryable<T>)readSession.Query<T>(); if (criteria != null) query = query.Where(criteria); // 立即执行查询将数据加载到内存,禁止返回未执行的IQueryable到上层,避免Session释放后查询执行异常 return await query.ToListAsync(); }
注意:如果需要关联对象数据,必须在查询时用
Include预加载所有需要用到的关联属性,禁止在Session释放后访问持久化对象的延迟加载属性。
2. 修正Update方法逻辑,不要尝试刷新长生命周期UoW缓存
保留写操作独立新建UnitOfWork的逻辑,写完后不需要操作旧的长生命周期readUnitOfWork,直接从当前写上下文返回最新数据即可:
public async Task<T> Update(T item, IDictionary<string, object> values) { try { T updatedItem; using (UnitOfWork uow = CreateModificationUnitOfWork()) { var persistentItem = uow.GetObjectByKey<T>(item.Oid); PopulateObjectHelper.PopulateObject(persistentItem, values); await uow.CommitChangesAsync(); // 提交完成后直接从当前写UoW获取最新值,不需要切换到旧读UoW updatedItem = uow.GetObjectByKey<T>(item.Oid); } return updatedItem; } catch (Exception e) { _Logger.Error(e); throw; } }
3. Blazor侧适配调整
- 数据服务在DI容器中注册为Scoped生命周期,禁止注册为Singleton,避免不同电路/用户共享UoW实例
- DxDataGrid在新增、编辑、删除操作的完成回调中,主动调用组件的
Refresh()方法,触发数据源重新执行查询 - 跨组件数据更新通知可通过Scoped级别的事件通知服务实现,数据变更后通知所有绑定对应数据的组件触发刷新,不要依赖UoW自动跨实例同步数据。
4. 性能优化可选方案
如果频繁新建Session查询存在性能压力,可开启XPO官方提供的二级数据缓存,该缓存会自动感知同进程内通过XPO提交的数据变更、自动失效对应缓存项,不会出现数据不一致问题,不要自行通过复用长生命周期UoW实现业务层缓存。
避坑提醒
- 不要尝试调用
DropIdentityMap()手动清空长生命周期UoW的缓存,该操作会清空实例加载的所有持久化对象,若有组件正在引用对应对象会出现状态异常、数据丢失问题 - 不要在Blazor组件中复用跨渲染周期的UnitOfWork/Session,Blazor组件基于长连接多次迭代渲染,长生命周期UoW会积累大量缓存对象,还会触发跨线程访问异常
- 不要在不同Session/UnitOfWork之间直接传递XPO持久化对象,跨会话传递仅传主键即可,在新会话中通过主键重新加载对象。
内容的提问来源于stack exchange,提问作者Barry Andrews
相关产品推荐
相关产品推荐

