Angular14+ .NET Core后端Memory Cache实现异常求助
.NET Core MemoryCache 部署后失效问题排查与修复
问题描述
使用Angular 14搭配.NET Core开发项目,通过MemoryCache实现后端全局数据存储,但部署后无法正常工作,相关代码如下:
Startup.cs 配置
services.AddMemoryCache();
MemoryCacheService.cs 实现
namespace TAAS.MemoryCacheService { public class MemoryCacheService { readonly IMemoryCache? objMemCash; public MemoryCacheService(IMemoryCache objMemoryCache) // ??? { this.objMemCash = (IMemoryCache?)objMemoryCache; // ??? } public void FnSetMappingAccountsInMemCash() { IDictionary<string, long?> dctMappingAcs = new Dictionary<string, long?>(); dctMappingAcs["PROFIT_ACCOUNT"] = 15; dctMappingAcs["DISCOUNT_ACCOUNT"] = 23; this.objMemCash.Set("MAPPING_ACCOUNTS", dctMappingAcs); } public IDictionary<string, long?> FnGetMappingAccountsInMemCash() { IDictionary<string, long?> dctMappingAcs = new Dictionary<string, long?>(); if (!this.objMemCash.TryGetValue("MAPPING_ACCOUNTS", out dctMappingAcs)) { this.FnSetMappingAccountsInMemCash(); } dctMappingAcs = (IDictionary<string, long?>)this.objMemCash.Get("MAPPING_ACCOUNTS"); return dctMappingAcs; } public void FnRemoveMappingAccountsInMemCash() { this.objMemCash.Remove("MAPPING_ACCOUNTS"); } } }
核心问题分析
- 自定义缓存服务未注册到DI容器:仅注册了
IMemoryCache,但MemoryCacheService未被注入容器,导致每次使用时可能创建新实例,无法实现全局共享。 - 不必要的可空类型处理:
IMemoryCache由DI框架注入,正常场景下不会为null,可空标记和强制转换可能引发空引用异常。 - 缓存读取逻辑冗余且有风险:
FnGetMappingAccountsInMemCash中重复获取缓存,若Get返回null,强制转换会直接报错。 - 部署环境进程隔离限制:如果部署在多实例(如负载均衡、Azure多实例)或IIS应用池频繁回收的环境,MemoryCache是进程内缓存,每个进程拥有独立缓存实例,数据无法跨进程共享。
修复方案
1. 注册自定义缓存服务
在Startup.cs的ConfigureServices方法中,将MemoryCacheService注册为单例,确保全局只有一个实例:
services.AddMemoryCache(); services.AddSingleton<MemoryCacheService>(); // 新增注册代码
2. 优化缓存服务实现
去掉冗余的可空处理,简化缓存读取逻辑,使用GetOrCreate方法避免空引用风险:
namespace TAAS.MemoryCacheService { public class MemoryCacheService { private readonly IMemoryCache _memoryCache; public MemoryCacheService(IMemoryCache memoryCache) { _memoryCache = memoryCache; } public void FnSetMappingAccountsInMemCash() { var mappingAccounts = new Dictionary<string, long?> { ["PROFIT_ACCOUNT"] = 15, ["DISCOUNT_ACCOUNT"] = 23 }; // 可添加过期策略,避免内存溢出 _memoryCache.Set("MAPPING_ACCOUNTS", mappingAccounts, new MemoryCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(24) }); } public IDictionary<string, long?> FnGetMappingAccountsInMemCash() { // GetOrCreate会自动处理缓存不存在的情况 return _memoryCache.GetOrCreate("MAPPING_ACCOUNTS", entry => { var mappingAccounts = new Dictionary<string, long?> { ["PROFIT_ACCOUNT"] = 15, ["DISCOUNT_ACCOUNT"] = 23 }; entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(24); return mappingAccounts; }); } public void FnRemoveMappingAccountsInMemCash() { _memoryCache.Remove("MAPPING_ACCOUNTS"); } } }
3. 多实例环境适配
如果部署在多进程/多实例环境,进程内MemoryCache无法满足全局共享需求,需替换为分布式缓存(如Redis):
- 安装Redis缓存包:
Install-Package Microsoft.Extensions.Caching.StackExchangeRedis - 注册分布式缓存:
services.AddStackExchangeRedisCache(options => { options.Configuration = "你的Redis连接字符串"; options.InstanceName = "TAAS:"; });
- 修改
MemoryCacheService依赖IDistributedCache,并处理对象序列化/反序列化:
public class MemoryCacheService { private readonly IDistributedCache _distributedCache; private readonly JsonSerializerOptions _serializerOptions; public MemoryCacheService(IDistributedCache distributedCache) { _distributedCache = distributedCache; _serializerOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; } public async Task FnSetMappingAccountsInMemCashAsync() { var mappingAccounts = new Dictionary<string, long?> { ["PROFIT_ACCOUNT"] = 15, ["DISCOUNT_ACCOUNT"] = 23 }; var json = JsonSerializer.Serialize(mappingAccounts, _serializerOptions); await _distributedCache.SetStringAsync("MAPPING_ACCOUNTS", json, new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(24) }); } public async Task<IDictionary<string, long?>> FnGetMappingAccountsInMemCashAsync() { var json = await _distributedCache.GetStringAsync("MAPPING_ACCOUNTS"); if (string.IsNullOrEmpty(json)) { await FnSetMappingAccountsInMemCashAsync(); json = await _distributedCache.GetStringAsync("MAPPING_ACCOUNTS"); } return JsonSerializer.Deserialize<Dictionary<string, long?>>(json, _serializerOptions); } }
额外注意事项
- 使用
MemoryCacheService时必须通过依赖注入获取实例,禁止手动new,否则无法共享缓存。 - 生产环境建议为缓存项设置合理的过期时间,避免内存占用过高。
内容的提问来源于stack exchange,提问作者C47_C0D3R
相关产品推荐
相关产品推荐

