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

跨ViewModel访问的多DataTable内存缓存最佳方案

最优实现方案:带懒加载的全局缓存仓储层

你之前想的两个方案都能跑,但都有明显的维护短板,更适配你场景的方案是把数据加载、缓存逻辑从ViewModel里抽离,做统一收口的仓储层,完全匹配你「按需加载、一次加载常驻内存、增删改后可控更新」的需求。

先讲你原有两个思路的问题

  • 静态变量存全量表数据:你自己已经意识到冗余问题,除此之外这个方案几乎没法做缓存失效控制,后续做增删改操作后很容易读到脏数据,而且所有表不管用不用都提前占内存,完全不符合你懒加载的要求。
  • DataSet按需加表:比静态全量加载的思路合理,但DataSet本身是弱类型容器,没有内置缓存依赖、失效逻辑,你需要自己维护表的加载状态、关联查询结果和基础表的同步关系,项目稍微复杂点就容易出现重复加载、数据不一致的问题。

具体落地方案(和你现有DataTable的技术栈完全兼容,改造成本极低)

核心逻辑就是把所有数据存取、缓存操作全放到独立的仓储类里,所有ViewModel不直接查库、不自己持有独立的DataTable实例,统一从仓储层拿数据,全局同一份数据只存一次,从根源避免内存浪费和重复请求。

缓存分层设计,完全规避冗余

把要缓存的数据分成两类,从来不会加载没用的内容:

  • 基础单表缓存:针对员工表、部门表这种会被频繁关联、需要展示全量的基础表,第一次被任何业务请求的时候才查库加载,加载完常驻内存,后续所有请求直接拿内存实例,不会重复访问数据库。
  • 关联查询结果缓存:针对你需要inner join拿外键名称的场景,不要每次都拼SQL去库上做join,第一次请求对应关联视图的时候,直接基于内存里已经缓存的基础表,用DataRelation或者Linq to DataSet在内存里做关联生成展示用的DataTable,把结果也存入缓存。同时记录这个关联结果依赖了哪些基础表,只要依赖的基础表有增删改操作,就自动把对应的关联缓存标记为失效,下次请求的时候直接基于最新的基础表内存数据重新生成就行,全程不用连库。

你的场景里所有增删改都是客户端用户主动触发的,根本不需要做复杂的缓存一致性校验、定时过期策略,只要在你提交完增删改的数据库操作后,调用仓储层的缓存移除方法,删掉对应基础表的缓存、以及所有依赖这个表的关联结果缓存就行,一致性完全可控,实现成本极低。

技术实现细节

不用引入任何重型框架,用.NET自带的组件就能搭:

  • 缓存容器直接用MemoryCache,天生支持按key存储、主动移除、优先级设置,不用自己写静态字典或者手动维护DataSet里的表集合。
  • 单表查询的懒加载逻辑参考如下代码,非常简单:
// 仓储层全局单例,所有ViewModel共用这一个实例
public class DataRepository
{
    private readonly MemoryCache _cache = MemoryCache.Default;
    private readonly HashSet<string> _baseTableKeys = new();

    public DataTable GetEmployeeList()
    {
        const string cacheKey = "BaseTbl_Employee";
        // 缓存存在直接返回,不查库
        if (_cache.Contains(cacheKey))
            return (DataTable)_cache.Get(cacheKey);
        
        // 缓存不存在才查数据库
        var dt = MySqlHelper.ExecuteQuery("SELECT * FROM Employee");
        // 存入缓存,设置为常驻,除非主动移除
        _cache.Set(cacheKey, dt, DateTimeOffset.MaxValue);
        // 记录这个key是基础员工表,后续更新的时候用
        _baseTableKeys.Add(cacheKey);
        return dt;
    }

    // 增删改提交后调用这个方法清缓存
    public void RemoveBaseTableCache(string tableKey)
    {
        _cache.Remove(tableKey);
        // 这里可以加逻辑,移除所有依赖这个基础表的关联查询缓存
    }
}
  • 你ViewModel里原来的代码几乎不用大改,只是把原来直接查库给_EmployeeList赋值的逻辑,改成调用仓储层拿数据就行:
private DataTable _EmployeeList;
public DataTable EmployeeList
{
    get
    {
        if (_EmployeeList == null)
            _EmployeeList = _globalRepository.GetEmployeeList();
        return _EmployeeList;
    }
}

方案优势

  • 完全满足懒加载要求:哪个表、哪个关联视图没被实际用到,永远不会加载进内存,不会占用无意义的RAM
  • 无重复内存占用:所有数据全局只存一份,不会出现多个ViewModel各自持有一份相同DataTable副本的浪费问题
  • 无冗余加载:不需要提前预存所有可能的join结果,用到哪个关联场景才生成哪个
  • 数据一致性可控:增删改后主动清对应缓存,不会出现长期脏读的问题
  • 改造成本极低:你原来用DataTable绑定DataGrid的逻辑完全不需要调整,不需要重写现有UI层的代码

内容的提问来源于stack exchange,提问作者PaRcS

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 00:32:23