ASP.NET Core应用中Memory Cache未按预期存储键值对问题
问题分析与解决方案
核心问题
你遇到的重复写入缓存问题,根源在于自定义的ContainsKey扩展方法完全不可靠。这个方法通过反射访问MemoryCache内部非公开的EntriesCollection属性来判断缓存键是否存在,但ASP.NET Core的MemoryCache内部实现属于未公开的细节,微软可能在不同版本中修改其结构或属性名称,导致反射无法正确获取缓存条目集合——最终即使缓存已写入,该方法也始终返回false,触发重复加载逻辑。
正确的解决方案
ASP.NET Core的IMemoryCache本身提供了官方推荐的缓存存在性检查方法——TryGetValue,完全不需要自己通过反射实现。
1. 移除自定义的ContainsKey扩展方法
直接抛弃这个依赖内部实现的不稳定方法。
2. 修改中间件的缓存判断逻辑
将SetCache.cs中的判断逻辑替换为TryGetValue:
public sealed class SetCache(RequestDelegate next) { public async Task Invoke(HttpContext context, IHttpContextAccessor httpContextAccessor, IUtilityFactory utilityFactory, IMemoryCache memory) { // 用TryGetValue检查缓存是否存在,不需要缓存值时可以用out _忽略 if (!memory.TryGetValue(CommonStatics.Cached, out _)) { ICacheUtility util = utilityFactory.Get<ICacheUtility>(); ICache cached = new Cache(httpContextAccessor); await cached.LoadAsync(context); memory.Set<ICache>(CommonStatics.Cached, cached, util.CacheEntryOptions); } await next(context); } }
3. 优化缓存过期配置(可选)
你的CacheEntryOptions中使用DateTime.Now设置绝对过期时间,每次访问该属性都会生成新的时间戳,可能导致缓存过期时间不一致。建议改用DateTimeOffset.UtcNow(更符合时区规范):
public MemoryCacheEntryOptions CacheEntryOptions => new() { SlidingExpiration = TimeSpan.FromMinutes(20), AbsoluteExpiration = DateTimeOffset.UtcNow.AddDays(2) };
额外说明
- 永远不要依赖框架的内部非公开API实现业务逻辑,这类API没有兼容性保证,版本升级后很容易失效。
TryGetValue不仅能判断缓存是否存在,还能直接获取缓存值,是官方推荐的缓存检查方式,性能和可靠性都远高于反射实现。
内容的提问来源于stack exchange,提问作者SteinTech
相关产品推荐
相关产品推荐

