Common.Cache.MemoryCache
3.0.0
dotnet add package Common.Cache.MemoryCache --version 3.0.0
NuGet\Install-Package Common.Cache.MemoryCache -Version 3.0.0
<PackageReference Include="Common.Cache.MemoryCache" Version="3.0.0" />
<PackageVersion Include="Common.Cache.MemoryCache" Version="3.0.0" />
<PackageReference Include="Common.Cache.MemoryCache" />
paket add Common.Cache.MemoryCache --version 3.0.0
#r "nuget: Common.Cache.MemoryCache, 3.0.0"
#:package Common.Cache.MemoryCache@3.0.0
#addin nuget:?package=Common.Cache.MemoryCache&version=3.0.0
#tool nuget:?package=Common.Cache.MemoryCache&version=3.0.0
Common.Cache.MemoryCache
Common.Cache.MemoryCache 是一个基于 Microsoft.Extensions.Caching.Memory 的轻量封装,提供统一的缓存读写、批量删除和按模式删除能力。
功能特性
- 基于 ASP.NET Core
IMemoryCache - 支持字符串、对象、集合等多种缓存值
- 支持
GetOrCreateAsync - 支持批量删除和按通配符删除
- 支持获取当前由 Provider 管理的全部缓存键
- 支持 .NET 6.0 / 7.0 / 8.0 / 9.0 / 10.0
安装
Install-Package Common.Cache.MemoryCache
或:
dotnet add package Common.Cache.MemoryCache
服务注册
推荐使用 AddMemoryCacheStore:
services.AddMemoryCacheStore(options =>
{
options.DefaultExpiry = TimeSpan.FromSeconds(30);
options.CacheEmptyCollections = false;
options.FailThrowException = true; // 缓存操作失败时是否抛出异常(默认为true)
});
旧方法 AddMemoryCacheExtension 已标记为过时,但仍可继续使用:
services.AddMemoryCacheExtension(options =>
{
options.DefaultExpiry = TimeSpan.FromSeconds(30);
options.CacheEmptyCollections = false;
options.FailThrowException = true; // 缓存操作失败时是否抛出异常(默认为true)
});
基本使用
public class MyService
{
private readonly ICacheProvider _cacheProvider;
public MyService(ICacheProvider cacheProvider)
{
_cacheProvider = cacheProvider;
}
public async Task<UserInfo> GetUserAsync(string userId)
{
return await _cacheProvider.GetOrCreateAsync(
$"user:{userId}",
async () => await LoadUserFromDatabase(userId),
TimeSpan.FromMinutes(10));
}
}
读写缓存
await _cacheProvider.SetAsync("name", "azrng", TimeSpan.FromMinutes(5));
var name = await _cacheProvider.GetAsync("name");
var count = await _cacheProvider.GetAsync<int>("count");
var user = await _cacheProvider.GetAsync<UserInfo>("user:1");
GetOrCreateAsync
var count = await _cacheProvider.GetOrCreateAsync(
"counter",
() => 0,
TimeSpan.FromMinutes(1));
var user = await _cacheProvider.GetOrCreateAsync(
"user:1",
async () => await LoadUserFromDatabase("1"),
TimeSpan.FromMinutes(10));
批量操作
await _cacheProvider.RemoveAsync(new[] { "user:1", "user:2", "user:3" });
await _cacheProvider.RemoveMatchKeyAsync("user:*");
var allKeys = ((IMemoryCacheProvider)_cacheProvider).GetAllKeys();
var allValues = await ((IMemoryCacheProvider)_cacheProvider).GetAllAsync(allKeys);
await ((IMemoryCacheProvider)_cacheProvider).RemoveAllKeyAsync();
配置项
MemoryCacheOptions 提供以下配置:
DefaultExpiry: 默认过期时间,默认值为 5 秒CacheEmptyCollections: 是否缓存空集合和空字符串,默认值为trueFailThrowException: 缓存读取/写入等缓存操作失败时是否抛出异常,默认值为true。业务工厂方法自身抛出的异常始终透出,不受此配置吞掉。
行为说明
1. 合法默认值会被正常缓存
从当前实现开始,0、false、DateTime.MinValue 这类合法业务值会被正常缓存,不再被误判为空值。
await _cacheProvider.SetAsync("bool:false", false);
var value = await _cacheProvider.GetAsync<bool>("bool:false"); // false
2. CacheEmptyCollections 只控制空集合和空字符串
当 CacheEmptyCollections = false 时:
null不会缓存- 空字符串不会缓存
- 空集合不会缓存
0、false等合法默认值仍然会缓存
3. 异常处理可通过配置控制
从 2.1.0 开始,可通过 FailThrowException 配置控制缓存操作失败时的行为:
true(默认):记录日志并抛出异常,避免把真实故障误判成”缓存未命中”false:记录日志并返回默认值,不抛出异常,提供更灵活的错误处理策略
FailThrowException 只控制缓存读取、写入等缓存组件自身异常。GetOrCreateAsync 的工厂方法用于加载业务数据,工厂方法抛出的异常会始终原样透出,避免把数据库、远程服务或业务加载失败误判为缓存未命中。
4. 并发访问同一个 Key 时会做单 Key 同步
同一个 Key 在高并发下首次未命中时,Provider 会按 Key 做同步,避免多个线程同时重复执行工厂方法。
这能减少数据库或远程调用的重复压力。同步锁会在本次同 Key 等待队列结束后释放并清理,避免高基数 Key 长时间运行时持续占用锁对象。
5. RemoveMatchKeyAsync 使用通配符语义
RemoveMatchKeyAsync 不是正则表达式,而是通配符匹配:
*匹配任意多个字符?匹配任意单个字符[]匹配指定字符范围
示例:
await _cacheProvider.RemoveMatchKeyAsync("user:*");
await _cacheProvider.RemoveMatchKeyAsync("order:2026-03-??");
6. GetAllKeys 只返回当前 Provider 管理的键
GetAllKeys 不再通过反射读取 MemoryCache 内部私有字段,而是返回通过当前 Provider 写入并跟踪的键集合。
这意味着:
- 通过
IMemoryCache直接写入的键不会出现在GetAllKeys中 - 通过 Provider 删除、覆盖、驱逐的键会同步更新跟踪集合
- 实现更稳定,不依赖 .NET 运行时内部结构
7. GetAllAsync 只返回命中的键
GetAllAsync 从 2.1.1 起,只返回实际命中的 key,不存在的 key 不会以 null 值出现在结果字典中。
await provider.SetAsync("ga:1", "v1");
var dict = await ((IMemoryCacheProvider)provider).GetAllAsync(new[] { "ga:1", "ga:missing" });
// dict 只包含 ga:1,不包含 ga:missing
注意事项
- 不建议使用
IEnumerable<T>、IQueryable<T>、IAsyncEnumerable<T>作为GetOrCreateAsync<T>的T - 如果需要缓存集合,请优先使用
List<T>或数组 SetAsync<T>(key, null)会返回false,不会写入缓存
同时使用内存缓存与 Redis 缓存
Common.Cache.MemoryCache 和 Common.Cache.Redis 都会把各自的 Provider 注册到同一个 ICacheProvider 抽象上。由于 ICacheProvider 在依赖注入容器中只能绑定一个实现(TryAdd 不覆盖,先注册者占位),两个包同时注册时,只有一个会真正绑定到 ICacheProvider,另一个对 ICacheProvider 的绑定会被静默忽略。
如果需要在同一个项目里同时使用两种缓存,不要注入 ICacheProvider,改为注入各自的具体接口:
// 注册两种缓存(顺序无关)
services.AddMemoryCacheStore();
services.AddRedisCacheStore();
public class MyService
{
// 直接注入具体接口,两者共存不冲突
public MyService(IMemoryCacheProvider memoryCache, IRedisProvider redisCache)
{
_memoryCache = memoryCache;
_redisCache = redisCache;
}
}
IMemoryCacheProvider:内存缓存能力(含GetAllKeys、GetAllAsync、RemoveAllKeyAsync等本地特有方法)IRedisProvider:Redis 缓存能力(含发布订阅等 Redis 特有方法)
注:
ICacheProvider适合"项目只用单一缓存"的场景,作为默认抽象使用。两种缓存共存时,请使用上述具体接口。
版本更新记录
- 3.0.0
- 破坏性更新:跟随
Azrng.Cache.Core1.0.0,GetAsync(string)返回Task<string?>、GetAsync<T>返回Task<T?>,如实表达未命中返回null的语义 - 移除因旧接口非 null 契约而添加的
!(null-forgiving) - 修复:
GetOrCreateAsync不再把业务工厂方法异常当作缓存异常吞掉;即使FailThrowException = false,工厂方法异常也会原样透出 - 修复:单 Key 并发同步锁在等待队列结束后及时清理,避免高基数 Key 场景下锁对象持续增长
- 依赖升级:
Azrng.Cache.Core1.0.0
- 破坏性更新:跟随
- 2.1.1
- 重命名(破坏性):
MemoryConfig重命名为MemoryCacheOptions,并补全 XML 注释、启用 nullable- 迁移指引:将原
Configure<MemoryConfig>/new MemoryConfig()/Action<MemoryConfig>全部替换为MemoryCacheOptions
- 迁移指引:将原
- 修复:缓存组件异常且
FailThrowException = false时,GetOrCreateAsync不再重复调用工厂方法,改为返回default,对齐文档承诺 - 修复:
GetAllAsync不存在的 key 不再以null进入结果字典,改为只返回命中的 key - 优化:csproj 补充
PackageLicenseExpression、RepositoryType、符号包(snupkg)等发布元数据
- 重命名(破坏性):
- 2.1.0
- 新增:
FailThrowException配置项,允许控制缓存操作失败时的行为true(默认):记录日志并抛出异常,与 2.0.0 行为一致false:记录日志并返回默认值,不抛出异常,提供更灵活的错误处理策略
- 优化:
GetOrCreateAsync支持FailThrowException配置,统一异常处理行为
- 新增:
- 2.0.0
- 破坏性更新:
GetOrCreateAsync发生异常时不再吞掉异常并返回default,改为记录日志后继续抛出异常 - 破坏性更新:
RemoveMatchKeyAsync改为按通配符语义匹配,支持*、?、[],不再直接把输入当作正则表达式 - 破坏性更新:
GetAllKeys改为返回当前 Provider 管理并跟踪的键,不再依赖反射读取MemoryCache内部私有字段 - 修复:支持缓存合法默认值,例如
0、false、DateTime.MinValue - 修复:
GetOrCreateAsync增加单 Key 并发保护,避免并发未命中时重复执行工厂方法 - 修复:
RemoveAsync(IEnumerable<string>)过滤空 key 和重复 key,并返回更准确的删除数量 - 优化:依赖注入注册改为复用同一个作用域内的
MemoryCacheProvider实例
- 破坏性更新:
- 1.3.2
- 更新异常信息输出
- 1.3.1
- 更新批量删除缓存的日志输出
- 1.3.0
- 修复 .NET 8 模糊匹配生效问题
- 引用 .NET 10 正式包
- 1.3.0-beta9
- 更新 .NET 10
- 1.3.0-beta8
- 修复设置不缓存空值时的问题
- 1.3.0-beta7
- 修复 GetOrCreateAsync 读取不到缓存仍写入的问题
- 1.3.0-beta6
- 更新命名空间
- 1.3.0-beta5
- 支持 .NET 9
- 移除对 netstandard2.1 的支持
- 更新注入方法
AddMemoryCacheStore
- 1.3.0-beta4
- 修改方法
KeyDeleteInBatchAsync为RemoveMatchKeyAsync - 修改方法
GetAllCacheKeys为GetAllKeys - 修改方法
RemoveCacheAllAsync为RemoveAllKeyAsync
- 修改方法
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 is compatible. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Azrng.Cache.Core (>= 1.0.0)
- Microsoft.Extensions.Caching.Memory (>= 10.0.0)
- Microsoft.Extensions.DependencyInjection (>= 10.0.0)
-
net6.0
- Azrng.Cache.Core (>= 1.0.0)
- Microsoft.Extensions.Caching.Memory (>= 6.0.1)
- Microsoft.Extensions.DependencyInjection (>= 6.0.0)
-
net7.0
- Azrng.Cache.Core (>= 1.0.0)
- Microsoft.Extensions.Caching.Memory (>= 7.0.0)
- Microsoft.Extensions.DependencyInjection (>= 7.0.0)
-
net8.0
- Azrng.Cache.Core (>= 1.0.0)
- Microsoft.Extensions.Caching.Memory (>= 8.0.0)
- Microsoft.Extensions.DependencyInjection (>= 8.0.0)
-
net9.0
- Azrng.Cache.Core (>= 1.0.0)
- Microsoft.Extensions.Caching.Memory (>= 9.0.0)
- Microsoft.Extensions.DependencyInjection (>= 9.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 3.0.0 | 48 | 7/3/2026 |
| 2.1.0 | 112 | 3/26/2026 |
| 2.0.0 | 129 | 3/23/2026 |
| 1.3.2 | 330 | 12/17/2025 |
| 1.3.1 | 483 | 12/10/2025 |
| 1.3.0 | 385 | 11/30/2025 |
| 1.3.0-beta9 | 194 | 11/6/2025 |
| 1.3.0-beta8 | 223 | 10/9/2025 |
| 1.3.0-beta7 | 260 | 8/28/2025 |
| 1.3.0-beta5 | 219 | 7/14/2025 |
| 1.3.0-beta4 | 433 | 6/5/2024 |
| 1.3.0-beta3 | 199 | 2/9/2024 |
| 1.3.0-beta2 | 184 | 2/8/2024 |
| 1.3.0-beta11 | 309 | 11/12/2025 |
| 1.3.0-beta10 | 138 | 11/7/2025 |
| 1.3.0-beta1 | 312 | 11/14/2022 |
| 1.2.0 | 584 | 11/12/2022 |
| 1.1.1 | 639 | 7/9/2022 |
| 1.1.0 | 746 | 11/11/2020 |
| 1.0.1 | 640 | 10/9/2020 |