如何在不更改过期时间的情况下更新IMemoryCache中的值类型?
解决IMemoryCache存储int值无法持久化更新的问题
这个问题我太熟了!你猜的完全没错——就是值类型的特性在IMemoryCache里搞出的麻烦。
问题根源
IMemoryCache本质是基于对象存储的,当你把int这种值类型存进去时,CLR会自动把它装箱成object。等你从缓存里取出这个值时,得到的是装箱对象的值副本(值类型的赋值都是按值传递的)。所以你修改这个副本的数值,根本不会影响缓存里原来那个装箱后的对象,自然看不到更新效果。至于加ref关键字没用,是因为ref只是在方法调用时传递变量的引用,但你从缓存取出的过程已经完成了值拷贝,ref也救不了这个问题。
解决方案
方案一:用自定义引用类型包装int(最推荐)
写一个简单的包装类,把int包在里面,这样存进缓存的就是引用类型了,修改的时候直接操作引用指向的对象,自然就能持久化:
// 定义包装类 public class IntHolder { public int Value { get; set; } } // 初始化缓存 _memoryCache.Set("UserCount", new IntHolder { Value = 0 }); // 更新值 var holder = _memoryCache.Get<IntHolder>("UserCount"); if (holder != null) { holder.Value++; // 这里修改的是缓存里对象的实际值 }
这个方法逻辑清晰,更新时不需要重新写入整个缓存,性能也更好,适合大多数场景。
方案二:每次更新直接重新Set缓存
如果不想写额外的类,也可以每次更新时先取出当前值,计算新值后重新存入缓存:
// 获取当前值(不存在则默认0) var currentCount = _memoryCache.Get<int?>("UserCount") ?? 0; // 存入新值 _memoryCache.Set("UserCount", currentCount + 1);
这个方法简单直接,但要注意并发场景下可能出现竞态条件(比如两个请求同时取到相同的currentCount,导致最终计数少加了)。如果需要并发安全,可以结合Interlocked来处理:
// 并发安全的写法 var currentCount = _memoryCache.Get<int?>("UserCount") ?? 0; var newCount = Interlocked.Increment(ref currentCount); _memoryCache.Set("UserCount", newCount);
方案三:用现成的引用类型“凑数”
如果你嫌写包装类麻烦,也可以用.NET自带的引用类型来装int,比如List<int>(虽然有点大材小用,但胜在快捷):
// 初始化 _memoryCache.Set("UserCount", new List<int> { 0 }); // 更新 var countList = _memoryCache.Get<List<int>>("UserCount"); if (countList != null) { countList[0]++; }
这个方法不用写额外代码,但可读性不如自定义包装类,适合快速测试或者简单场景。
总结
核心就是避开值类型的装箱副本问题,要么用引用类型包裹,要么每次更新时重新覆盖缓存值。根据你的场景选最顺手的就行~
内容的提问来源于stack exchange,提问作者user3953989
相关产品推荐
相关产品推荐

