.NET 6 Web服务配置分布式缓存及无缓存场景的注入兼容方案
解决.NET 6 Web服务缓存注入问题:支持多缓存配置及无缓存场景
问题根源
你的控制器构造函数直接依赖IDistributedCache和IMemoryCache,但当配置中未启用对应缓存时,这些服务并未注册到DI容器,导致启动时依赖注入失败。
解决方案思路
为每个缓存接口提供空实现(调用时不执行实际缓存操作),确保无论是否启用真实缓存,DI容器都能找到对应服务实例;同时保留真实缓存的注册逻辑,根据配置切换。
1. 创建空缓存实现类
分别实现IDistributedCache和IMemoryCache的空版本,用于无缓存场景:
// 空分布式缓存实现 public class NullDistributedCache : IDistributedCache { public byte[]? Get(string key) => null; public Task<byte[]?> GetAsync(string key, CancellationToken token = default) => Task.FromResult<byte[]?>(null); public void Refresh(string key) {} public Task RefreshAsync(string key, CancellationToken token = default) => Task.CompletedTask; public void Remove(string key) {} public Task RemoveAsync(string key, CancellationToken token = default) => Task.CompletedTask; public void Set(string key, byte[] value, DistributedCacheEntryOptions options) {} public Task SetAsync(string key, byte[] value, DistributedCacheEntryOptions options, CancellationToken token = default) => Task.CompletedTask; } // 空内存缓存实现 public class NullMemoryCache : IMemoryCache { public void Dispose() {} public bool TryGetValue(object key, out object? value) { value = null; return false; } public ICacheEntry CreateEntry(object key) => new NullCacheEntry(); public void Remove(object key) {} private class NullCacheEntry : ICacheEntry { public object Key { get; } = default!; public object? Value { get; set; } public DateTimeOffset? AbsoluteExpiration { get; set; } public TimeSpan? AbsoluteExpirationRelativeToNow { get; set; } public TimeSpan? SlidingExpiration { get; set; } public IList<IChangeToken> ExpirationTokens { get; } = new List<IChangeToken>(); public IList<PostEvictionCallbackRegistration> PostEvictionCallbacks { get; } = new List<PostEvictionCallbackRegistration>(); public CacheItemPriority Priority { get; set; } = CacheItemPriority.Normal; public long? Size { get; set; } public void Dispose() {} } }
2. 修改服务注册逻辑
在Program.cs中,调整缓存注册逻辑:无论是否启用真实缓存,都确保IDistributedCache和IMemoryCache有对应的注册(真实实现或空实现):
var dcachesqlserver = Configuration.GetSection("UseCache").GetValue<bool>("SqlServer"); var dcacheredis = Configuration.GetSection("UseCache").GetValue<bool>("Redis"); var dcacheInmemory = Configuration.GetSection("UseCache").GetValue<bool>("InMemory"); // 处理分布式缓存:优先注册启用的真实缓存,否则注册空实现 if (dcachesqlserver) { services.AddDistributedSqlServerCache(options => { options.ConnectionString = Configuration.GetConnectionString("CacheDbConnection"); options.SchemaName = "dbo"; options.TableName = "CacheStore"; }); } else if (dcacheredis) { services.AddStackExchangeRedisCache(options => { options.Configuration = Configuration.GetConnectionString("RedisConnection"); options.InstanceName = "SampleInstance"; }); } else { // 无分布式缓存时注册空实现 services.AddSingleton<IDistributedCache, NullDistributedCache>(); } // 处理内存缓存:启用则注册真实实现,否则注册空实现 if (dcacheInmemory) { services.AddMemoryCache(options => { options.SizeLimit = 1024; }); } else { services.AddSingleton<IMemoryCache, NullMemoryCache>(); }
3. 配置文件示例(appsettings.json)
用户可以通过配置文件灵活选择缓存方案,包括无缓存:
{ "UseCache": { "SqlServer": false, "Redis": false, "InMemory": false // 全部设为false即无缓存场景 }, "ConnectionStrings": { "CacheDbConnection": "你的SQL Server连接字符串", "RedisConnection": "你的Redis连接字符串" } }
- 启用单个缓存:将对应字段设为
true即可(例如只启用Redis,就把Redis设为true,其他为false) - 启用多个缓存:注意
IDistributedCache只能注册一个实现,上述逻辑优先注册SqlServer,其次Redis,若需同时使用多种缓存需改用命名服务或工厂模式(通常单分布式缓存足够满足场景)
4. 控制器代码无需修改
保持原控制器构造函数不变,DI容器会自动注入对应的缓存实现(真实或空):
public WeatherForecastController(ILogger<WeatherForecastController> logger, IDistributedCache distributedCache, IMemoryCache memoryCache) { // 逻辑不变,空实现的缓存方法调用不会报错,也不会执行实际缓存操作 }
额外优化建议
- 若需同时使用内存缓存+分布式缓存,可调整注册逻辑保留
IMemoryCache的真实实现,仅对IDistributedCache做空实现处理,或用工厂模式获取不同缓存实例。 - 可将缓存配置封装为强类型选项类,提升可读性和类型安全:
然后在public class CacheOptions { public bool SqlServer { get; set; } public bool Redis { get; set; } public bool InMemory { get; set; } }Program.cs中绑定:var cacheOptions = Configuration.GetSection("UseCache").Get<CacheOptions>(); // 使用cacheOptions.SqlServer等判断
内容的提问来源于stack exchange,提问作者ZedZip
相关产品推荐
相关产品推荐

