You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 18:06:25