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
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Common.Cache.MemoryCache" Version="3.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Common.Cache.MemoryCache" Version="3.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Common.Cache.MemoryCache" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Common.Cache.MemoryCache --version 3.0.0
                    
#r "nuget: Common.Cache.MemoryCache, 3.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Common.Cache.MemoryCache@3.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Common.Cache.MemoryCache&version=3.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Common.Cache.MemoryCache&version=3.0.0
                    
Install as a Cake Tool

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: 是否缓存空集合和空字符串,默认值为 true
  • FailThrowException: 缓存读取/写入等缓存操作失败时是否抛出异常,默认值为 true。业务工厂方法自身抛出的异常始终透出,不受此配置吞掉。

行为说明

1. 合法默认值会被正常缓存

从当前实现开始,0falseDateTime.MinValue 这类合法业务值会被正常缓存,不再被误判为空值。

await _cacheProvider.SetAsync("bool:false", false);
var value = await _cacheProvider.GetAsync<bool>("bool:false"); // false

2. CacheEmptyCollections 只控制空集合和空字符串

CacheEmptyCollections = false 时:

  • null 不会缓存
  • 空字符串不会缓存
  • 空集合不会缓存
  • 0false 等合法默认值仍然会缓存

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.MemoryCacheCommon.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:内存缓存能力(含 GetAllKeysGetAllAsyncRemoveAllKeyAsync 等本地特有方法)
  • IRedisProvider:Redis 缓存能力(含发布订阅等 Redis 特有方法)

注:ICacheProvider 适合"项目只用单一缓存"的场景,作为默认抽象使用。两种缓存共存时,请使用上述具体接口。

版本更新记录

  • 3.0.0
    • 破坏性更新:跟随 Azrng.Cache.Core 1.0.0,GetAsync(string) 返回 Task<string?>GetAsync<T> 返回 Task<T?>,如实表达未命中返回 null 的语义
    • 移除因旧接口非 null 契约而添加的 !(null-forgiving)
    • 修复GetOrCreateAsync 不再把业务工厂方法异常当作缓存异常吞掉;即使 FailThrowException = false,工厂方法异常也会原样透出
    • 修复:单 Key 并发同步锁在等待队列结束后及时清理,避免高基数 Key 场景下锁对象持续增长
    • 依赖升级:Azrng.Cache.Core 1.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 补充 PackageLicenseExpressionRepositoryType、符号包(snupkg)等发布元数据
  • 2.1.0
    • 新增FailThrowException 配置项,允许控制缓存操作失败时的行为
      • true(默认):记录日志并抛出异常,与 2.0.0 行为一致
      • false:记录日志并返回默认值,不抛出异常,提供更灵活的错误处理策略
    • 优化GetOrCreateAsync 支持 FailThrowException 配置,统一异常处理行为
  • 2.0.0
    • 破坏性更新GetOrCreateAsync 发生异常时不再吞掉异常并返回 default,改为记录日志后继续抛出异常
    • 破坏性更新RemoveMatchKeyAsync 改为按通配符语义匹配,支持 *?[],不再直接把输入当作正则表达式
    • 破坏性更新GetAllKeys 改为返回当前 Provider 管理并跟踪的键,不再依赖反射读取 MemoryCache 内部私有字段
    • 修复:支持缓存合法默认值,例如 0falseDateTime.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
    • 修改方法 KeyDeleteInBatchAsyncRemoveMatchKeyAsync
    • 修改方法 GetAllCacheKeysGetAllKeys
    • 修改方法 RemoveCacheAllAsyncRemoveAllKeyAsync
Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
Loading failed