RuoVea.ExCache 10.0.0.4

dotnet add package RuoVea.ExCache --version 10.0.0.4
                    
NuGet\Install-Package RuoVea.ExCache -Version 10.0.0.4
                    
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="RuoVea.ExCache" Version="10.0.0.4" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="RuoVea.ExCache" Version="10.0.0.4" />
                    
Directory.Packages.props
<PackageReference Include="RuoVea.ExCache" />
                    
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 RuoVea.ExCache --version 10.0.0.4
                    
#r "nuget: RuoVea.ExCache, 10.0.0.4"
                    
#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 RuoVea.ExCache@10.0.0.4
                    
#: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=RuoVea.ExCache&version=10.0.0.4
                    
Install as a Cake Addin
#tool nuget:?package=RuoVea.ExCache&version=10.0.0.4
                    
Install as a Cake Tool

🔗 RuoVea.ExCache

NuGet NuGet Downloads Target Framework License

统一缓存抽象层 — 提供 ICache 统一接口,一行配置在内存缓存Redis 分布式缓存之间无缝切换,业务代码零改动。


📖 目录


📋 概览

RuoVea.ExCache 为 .NET 应用提供轻量级的缓存抽象层,通过 appsettings.json 配置驱动,运行时自动选择缓存后端。

 ┌─────────────────────────────────────────────────────┐
 │                  RuoVea.ExCache                       │
 ├─────────────────────────────────────────────────────┤
 │                                                       │
 │   appsettings.json                                    │
 │   ┌──────────────────┐                                │
 │   │ Cache:CacheType   │──── 配置驱动 ────┐             │
 │   │ Cache:Prefix      │                  │             │
 │   │ Cache:RedisConn   │                  ▼             │
 │   └──────────────────┘    ┌──────────────────────┐    │
 │                           │    CacheFactery       │    │
 │                           │  (静态工厂 / 配置中心)  │    │
 │                           └──────────┬───────────┘    │
 │                                      │                 │
 │                         ┌────────────▼─────────────┐   │
 │                         │        ICache             │   │
 │                         │   (统一缓存接口)           │   │
 │                         └────────┬─────────────────┘   │
 │                                  │                     │
 │                   ┌──────────────┼──────────────┐      │
 │                   ▼                             ▼      │
 │     ┌──────────────────────┐    ┌──────────────────┐  │
 │     │   MemoryCaches       │    │   RedisCache      │  │
 │     │ (进程内 MemoryCache)  │    │ (CSRedis 分布式)   │  │
 │     └──────────────────────┘    └──────────────────┘  │
 └─────────────────────────────────────────────────────┘

设计原则

原则 说明
配置驱动 通过 Cache:CacheType 切换后端,不修改业务代码
统一接口 ICache 17 个方法覆盖读写删查全生命周期
键前缀隔离 所有 Key 自动加全局前缀,多租户/多环境键空间隔离
双工 API 每个操作同时提供同步与异步版本
零 DI 依赖 无需 IServiceCollection 注册,CacheFactery.Cache 直接获取

📦 安装

.NET CLI

# .NET 8.0
dotnet add package RuoVea.ExCache --version 8.0.*

# .NET 10.0
dotnet add package RuoVea.ExCache --version 10.0.*

Package Manager

Install-Package RuoVea.ExCache -Version 8.0.*

自动安装的依赖

依赖包 最低版本 用途
CSRedisCore 3.8.807 Redis 客户端驱动
Microsoft.Extensions.Caching.Memory 8.0.1 内存缓存后端
RuoVea.ExConfig 8.0.* 内部配置读取

支持的 Target Framework

TFM 最低版本
net8.0 8.0.1.3
net10.0 10.0.0.3

⚡ 30 秒快速开始

1. 配置 appsettings.json

{
  "Cache": {
    "Prefix": "MyApp",
    "CacheType": "MemoryCache",
    "RedisConnectionString": "127.0.0.1:6379,password=,defaultDatabase=2"
  }
}

2. 第一行缓存代码

using RuoVea.ExCache;
using RuoVea.ExCache.Cache;

// <inheritdoc cref="CacheFactery.Cache"/>
// 获取缓存实例(根据配置自动选择 MemoryCache 或 RedisCache)
ICache cache = CacheFactery.Cache;

// <inheritdoc cref="ICache.Write(string, object, TimeSpan)"/>
// 写入缓存(30 分钟过期)
cache.Write("user:1001", new { Name = "张三", Role = "Admin" }, TimeSpan.FromMinutes(30));

// <inheritdoc cref="ICache.Read{T}(string)"/>
// 泛型读取
var user = cache.Read<UserInfo>("user:1001");

// <inheritdoc cref="ICache.Exists"/>
if (cache.Exists("user:1001"))
    Console.WriteLine("缓存命中 ✓");

// <inheritdoc cref="ICache.Del"/>
cache.Del("user:1001");

3. 异步版本

// <inheritdoc cref="ICache.WriteAsync(string, object, TimeSpan)"/>
await cache.WriteAsync("metrics:daily", data, TimeSpan.FromHours(1));

// <inheritdoc cref="ICache.ReadAsync{T}(string)"/>
var result = await cache.ReadAsync<Metrics>("metrics:daily");

// <inheritdoc cref="ICache.ExistsAsync"/>
bool exists = await cache.ExistsAsync("metrics:daily");

// <inheritdoc cref="ICache.DelAsync(string[])"/>
await cache.DelAsync("metrics:daily", "metrics:weekly");

30 秒内你完成了:配置 → 获取实例 → 写入 → 泛型读取 → 存在检查 → 删除,同步/异步全覆盖。


🧩 核心场景

场景一:Read-Through 缓存(防穿透)

┌───────┐   Read(key)    ┌──────────┐
│ 业务层  │ ────────────▶ │  缓存层   │
└───┬───┘                └────┬─────┘
    │                         │
    │   命中 → 直接返回         │ 未命中 → 调用 getFun
    │                         │     ↓
    │                         │  ┌──────────┐
    │                         │  │ 数据库/API │
    │                         │  └────┬─────┘
    │                         │       │ 写回缓存 + 返回
    │                         │  ◀────┘
    ▼                         ▼
  返回结果                   返回结果
// <inheritdoc cref="ICache.Cof_ReadWrite{T}"/>
// 同步 Read-Through:缓存未命中自动回源
public Product GetProduct(long productId)
{
    return CacheFactery.Cache.Cof_ReadWrite<Product>(
        $"product:{productId}",
        getFun: () =>
        {
            // 缓存未命中 → 从数据库查询
            return _db.Products.FirstOrDefault(p => p.Id == productId);
        },
        timeSpanMin: TimeSpan.FromMinutes(10)
    );
}
// <inheritdoc cref="ICache.ReadAsync{T}(string)"/>
// 异步 Read-Through(手动实现——ICache 未提供 Cof_ReadWrite 的异步版本)
public async Task<Product> GetProductAsync(long productId)
{
    var key = $"product:{productId}";

    // 1. 先查缓存
    var cached = await CacheFactery.Cache.ReadAsync<Product>(key);
    if (cached != null) return cached;

    // 2. 回源
    var product = await _db.Products.FindAsync(productId);
    if (product != null)
        // 3. 写回缓存
        await CacheFactery.Cache.WriteAsync(key, product, TimeSpan.FromMinutes(10));

    return product;
}

⚠️ 线程安全说明Cof_ReadWrite<T> 内部使用键级 SemaphoreSlim + 双重检查来大幅减少高并发下的重复回源调用。但不同进程/实例之间不互斥,严格防击穿请配合分布式锁

// 推荐:带分布式锁的安全 Read-Through
public async Task<T> GetSafeAsync<T>(string key, Func<Task<T>> factory, TimeSpan ttl)
    where T : class
{
    var cached = await cache.ReadAsync<T>(key);
    if (cached != null) return cached;

    using var lockHandle = await _distributedLock.AcquireAsync($"lock:{key}");
    cached = await cache.ReadAsync<T>(key);  // 双重检查
    if (cached != null) return cached;

    var result = await factory();
    if (result != null)
        await cache.WriteAsync(key, result, ttl);
    return result;
}

场景二:模式批量删除

// <inheritdoc cref="ICache.DelByPattern"/>
// 同步 —— 删除匹配 "product:category:*" 的所有键
public void InvalidateCategoryCache(long categoryId)
{
    long deleted = CacheFactery.Cache.DelByPattern($"product:category:{categoryId}:*");
    _logger.LogInformation("失效分类缓存 {CategoryId},删除 {Count} 键", categoryId, deleted);
}

// <inheritdoc cref="ICache.DelByPrefixAsync"/>
// 异步 —— 按前缀删除
public async Task InvalidateUserSessionsAsync(string sessionPrefix)
{
    long deleted = await CacheFactery.Cache.DelByPrefixAsync(sessionPrefix);
    _logger.LogInformation("清理会话 {Prefix},删除 {Count}", sessionPrefix, deleted);
}

性能陷阱:Redis 模式下 DelByPattern / DelByPrefix 底层使用 KEYS 命令(O(N) 全库扫描)。生产环境数据量大时严禁高频调用。MemoryCache 模式也执行 O(N) 全量正则匹配遍历。


场景三:统一会话管理

public class SessionManager
{
    // ✅ 持有单例实例,避免每次访问 CacheFactery.Cache 创建新对象
    private static readonly ICache _cache = CacheFactery.Cache;

    // <inheritdoc cref="ICache.Write(string, object, TimeSpan)"/>
    public void CreateSession(string sessionId, UserSession session)
    {
        // 实际键 = "MyApp:session:{sessionId}"(全局前缀自动添加)
        var success = _cache.Write($"session:{sessionId}", session, TimeSpan.FromHours(2));
        if (!success)
            throw new InvalidOperationException("创建会话缓存失败");
    }

    // <inheritdoc cref="ICache.Read{T}(string)"/>
    public UserSession GetSession(string sessionId)
        => _cache.Read<UserSession>($"session:{sessionId}");

    // <inheritdoc cref="ICache.DelAsync(string[])"/>
    public async Task LogoutAsync(params string[] sessionIds)
    {
        var keys = sessionIds.Select(id => $"session:{id}").ToArray();
        await _cache.DelAsync(keys);
    }
}

场景四:键空间巡检

// <inheritdoc cref="ICache.GetAllKeysByPrefix"/>
// 同步 —— 列出指定前缀下的所有缓存键(运维/监控场景)
public IReadOnlyList<string> GetActiveSessionKeys()
    => CacheFactery.Cache.GetAllKeysByPrefix("session:");

// <inheritdoc cref="ICache.GetAllKeysByPrefixAsync"/>
// 异步版本
public async Task<IReadOnlyList<string>> GetActiveSessionKeysAsync()
    => await CacheFactery.Cache.GetAllKeysByPrefixAsync("session:");

性能陷阱:Redis 模式触发 KEYS 命令,MemoryCache 模式做 O(N) 扫描。仅限管理后台/调试工具等低频场景,禁止放入热路径。


⚙️ 配置选项详解

配置节点路径

全部读取自 IConfiguration(通过 RuoVea.ExConfig.AppSettings):

路径 类型 必填 默认值 说明
Cache:Prefix string 全局键前缀。最终键格式:{Prefix}:{yourKey}
Cache:CacheType string MemoryCache "MemoryCache"(进程内)或 "RedisCache"/"Redis"(分布式)
Cache:RedisConnectionString string Redis 时必填 Redis 连接字符串 host:port,password=,defaultDatabase=0

配置示例

开发环境 — 内存缓存

{
  "Cache": {
    "Prefix": "DevApp",
    "CacheType": "MemoryCache"
  }
}

生产环境 — Redis 哨兵

{
  "Cache": {
    "Prefix": "ProdApp",
    "CacheType": "Redis",
    "RedisConnectionString": "10.0.1.10:6379,password=SecureP@ss,defaultDatabase=0,prefix=prod"
  }
}

运行时覆盖

// ⚠️ 必须在首次调用 CacheFactery.Cache 之前设置
CacheFactery.CacheType = CacheEnum.RedisCache;
CacheFactery.Prefix = "DynamicPrefix";
CacheFactery.RedisConnectionString = "redis-master:6379,password=xxx,defaultDatabase=1";

ICache cache = CacheFactery.Cache; // 获取 RedisCache 实例

🛡️ 错误处理与日志

常见异常

异常类型 触发条件 处理建议
CSRedis.CSRedisException Redis 连接失败/超时/OOM 启用连接池重试,设置合理超时
ObjectDisposedException Redis 客户端已释放后调用 确保应用生命周期内不提前释放
InvalidCastException Read<T> 类型与写入类型不匹配 读写使用同一类型

推荐的弹性缓存模式

public class ResilientCacheService
{
    private static readonly ICache _cache = CacheFactery.Cache;
    private readonly ILogger<ResilientCacheService> _logger;

    public ResilientCacheService(ILogger<ResilientCacheService> logger) => _logger = logger;

    /// <summary>
    /// 安全读取:缓存异常时降级到回源,不影响业务
    /// </summary>
    public async Task<T> GetOrFallbackAsync<T>(string key, Func<Task<T>> fallback, TimeSpan ttl)
    {
        try
        {
            var cached = await _cache.ReadAsync<T>(key);
            if (cached != null)
            {
                _logger.LogDebug("缓存命中 Key={Key}", key);
                return cached;
            }
        }
        catch (Exception ex)
        {
            // 缓存不可用时降级,不阻断业务
            _logger.LogWarning(ex, "缓存读取异常,降级回源 Key={Key}", key);
        }

        var result = await fallback();
        if (result != null)
        {
            try { await _cache.WriteAsync(key, result, ttl); }
            catch (Exception ex) { _logger.LogWarning(ex, "缓存回写失败 Key={Key}", key); }
        }
        return result;
    }
}

🧵 线程安全

组件 线程安全 说明
CacheFactery.CacheType / Prefix / RedisConnectionString ⚠️ 条件安全 启动阶段赋值安全;运行时修改需调用 ResetCacheInstance() 使其生效。调用 SealConfig() 后可禁止修改
CacheFactery.Cache getter ✅ 是 Lazy<ICache> + LazyThreadSafetyMode.ExecutionAndPublication 保证单例且线程安全
CacheFactery.RedisClient ✅ 是 Lazy<ICache> 内部初始化,单次赋值
MemoryCaches._cacheKeys ✅ 是 lock (_lock) 保护读写
MemoryCaches._memoryCache ✅ 是 MemoryCache 本身线程安全
MemoryCaches.Cof_ReadWrite<T> ⚠️ 部分 使用键级 SemaphoreSlim + 双重检查大幅减少重复回源调用,但非严格原子性(不同进程/实例间不互斥)
RedisCache 全部方法 ✅ 是 委托给 CSRedis.RedisHelper(线程安全)
RedisCache.Cof_ReadWrite<T> ⚠️ 部分 同上——单实例内键级锁保护,分布式环境需配合分布式锁

改进说明(v8.0.1.3+):CacheFactery.Cache 已改为 Lazy<ICache> 单例模式,不再每次创建新实例。Cof_ReadWrite<T> 新增键级 SemaphoreSlim 双重检查以减少并发回源。如需运行时切换配置,使用 SealConfig() / ResetCacheInstance()


📊 API 完整速查

ICache 接口(17 个成员)

分类 同步方法 异步方法 说明
写入 Write(key, value) WriteAsync(key, value) 写入(无过期)
Write(key, value, expire) WriteAsync(key, value, expire) 写入(指定过期)
读取 Read(key)string ReadAsStringAsync(key) 读取字符串
Read<T>(key)T ReadAsync<T>(key) 读取泛型
删除 Del(keys...) DelAsync(keys...) 删除一个或多个键
DelByPattern(pattern) DelByPatternAsync(pattern) 通配符模式删除
DelByPrefixAsync(prefix) 前缀删除(仅异步)
存在 Exists(key) ExistsAsync(key) 检查键是否存在
穿透 Cof_ReadWrite<T>(key, factory, ttl) Read-Through(仅同步)
巡检 GetAllKeysByPrefix(prefix) GetAllKeysByPrefixAsync(prefix) 列出前缀下所有键

CacheEnum 枚举

说明
MemoryCache 进程内 System.Runtime.Caching.MemoryCache
RedisCache CSRedis 分布式缓存

🗺️ 版本迁移指南

从 v7.x → v8.0

变更项 说明
ICache 接口新增 DelByPatternDelByPatternAsyncGetAllKeysByPrefixGetAllKeysByPrefixAsync
ICache.ReadAsync<T> 签名兼容,底层序列化器升级至 CSRedisCore 最新版
配置路径 无变化Cache 节点完全兼容
命名空间 无变化RuoVea.ExCache / RuoVea.ExCache.Cache

迁移步骤:

  1. 更新 NuGet 版本至 8.0.*
  2. 重新编译确认无错误(接口向下兼容)
  3. (可选)将手写 DelByPrefix 替换为内置的 DelByPattern / DelByPrefixAsync
  4. (可选)利用新增 GetAllKeysByPrefix 替换反射/私有字段访问的巡检代码

📄 License

MIT License © RuoVea


🔗 相关资源: NuGet Gallery · 问题反馈

Product Compatible and additional computed target framework versions.
.NET 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 (1)

Showing the top 1 NuGet packages that depend on RuoVea.ExCache:

Package Downloads
RuoVea.ExSugar

Sqlsugar扩展 快速注入,支持简体中文、繁体中文、粤语、日语、法语、英语.使用方式:service.AddSqlsugar();继承RestFulLog 重写异常日志,操作日志,差异日志

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
10.0.0.4 54 6/24/2026
10.0.0.3 627 5/28/2026
10.0.0.2 592 1/26/2026
10.0.0.1 164 1/12/2026
9.0.0.3 141 5/28/2026
9.0.0.2 1,168 1/26/2026
9.0.0.1 166 1/12/2026
8.0.1.4 51 6/24/2026
8.0.1.3 1,203 5/28/2026
8.0.1.2 1,658 1/26/2026
8.0.1.1 891 1/12/2026
7.0.1.3 613 5/28/2026
7.0.1.2 1,729 1/26/2026
7.0.1.1 1,020 1/12/2026
6.0.6.3 711 5/28/2026
6.0.6.2 2,069 1/26/2026
6.0.6.1 1,238 1/12/2026
5.0.5.3 121 5/28/2026
5.0.5.2 235 1/26/2026
5.0.5.1 194 1/12/2026
Loading failed