跨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
相关产品推荐
相关产品推荐

