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

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");
        }
    }
}

核心问题分析

  1. 自定义缓存服务未注册到DI容器:仅注册了IMemoryCache,但MemoryCacheService未被注入容器,导致每次使用时可能创建新实例,无法实现全局共享。
  2. 不必要的可空类型处理:IMemoryCache由DI框架注入,正常场景下不会为null,可空标记和强制转换可能引发空引用异常。
  3. 缓存读取逻辑冗余且有风险:FnGetMappingAccountsInMemCash中重复获取缓存,若Get返回null,强制转换会直接报错。
  4. 部署环境进程隔离限制:如果部署在多实例(如负载均衡、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):

  1. 安装Redis缓存包:Install-Package Microsoft.Extensions.Caching.StackExchangeRedis
  2. 注册分布式缓存:
services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = "你的Redis连接字符串";
    options.InstanceName = "TAAS:";
});
  1. 修改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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 11:05:31