EasyCore.Redis 8.3.1

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

🔴 EasyCore.Redis

EasyCore.Redis 是面向 .NET 8 的生产级 Redis 工具库。基于 StackExchange.Redis,提供分布式缓存(五大数据类型)、MULTI/EXEC 事务分布式锁,以及基于 Castle DynamicProxy 的 **[ServerCache] 方法结果缓存**。

.NET C# Redis Cache Lock License Version

仓库:github.com/RockyWang0521/EasyCore.Redis


🌍 Language


📚 目录

🧭 第一部分:总览与架构

🚀 第二部分:快速上手

📘 第三部分:API 详解

🏭 第四部分:生产与示例


1. 🎯 项目定位

EasyCore.Redis 解决「在 ASP.NET Core 中安全、清晰地使用 Redis」的问题:

痛点 EasyCore.Redis 做法
SE.Redis 命令多、上手成本高 IDistributedCache 统一封装五大数据类型
多服务争用同一连接 共享 IRedisConnection / ConnectionMultiplexer
键冲突 / 多环境串数据 DistributedName 自动前缀隔离
临界区并发难控 IDistributedLock(SET NX PX + Lua 校验解锁)
方法结果重复计算 [ServerCache] + Castle 代理自动缓存
批量写入要原子 ICacheTransaction(MULTI/EXEC)

1.1 设计原则

原则 说明
低摩擦接入 一个 AddEasyCoreRedis(...) 即可注册全部能力
按需拆分 .Distributed / .Locking / .Service 可独立引用
键空间隔离 所有逻辑键自动加 {DistributedName}: 前缀
锁安全 解锁校验 LockIdDispose 仅在持有时释放
显式事务 在事务对象上排队,再 Commit;未提交丢弃

1.2 解决方案目录

EasyCore.Redis/
├── src/
│   ├── EasyCore.Redis/                 # 元包:一键注册全部能力
│   ├── EasyCore.Redis.Distributed/     # 缓存 + 事务 + 连接
│   ├── EasyCore.Redis.Locking/         # 分布式锁
│   └── EasyCore.Redis.Service/         # [ServerCache] AOP
├── demo/Web.EasyCore.Cache/            # Swagger Demo
├── tests/EasyCore.Cache.Tests/
└── docs/svg/                           # README 架构图(NuGet 用绝对 URL)

2. 🏗 架构与模块关系

2.1 组件关系图

architecture-cn

2.2 核心流程

sequence-cn

2.3 API 能力一览

api-overview-cn

2.4 依赖关系(文字版)

AddEasyCoreRedis() / AddEasyCoreRedis(IConfiguration)
        │
        ├── AddEasyCoreRedisDistributed ──► IRedisConnection
        │                              ├─ IDistributedCache
        │                              └─ IDistributedTransaction → ICacheTransaction
        ├── AddEasyCoreRedisLock ─────────► IDistributedLock  (复用同一连接)
        └── AddEasyCoreRedisService ──────► [ServerCache] Proxy (依赖 IDistributedCache)

3. 📦 NuGet / 项目清单

包名 职责 是否必须
EasyCore.Redis 元包,注册缓存 + 锁 + 服务缓存 ✅ 推荐
EasyCore.Redis.Distributed 连接、IDistributedCache、事务 按需
EasyCore.Redis.Locking IDistributedLock 按需
EasyCore.Redis.Service [ServerCache] Castle 代理 按需

只装锁时需先有连接:可用 AddEasyCoreRedisLock(configure),或先 AddEasyCoreRedisDistributedAddEasyCoreRedisLock()


4. 📊 能力对比

能力 Distributed Locking Service
String / Hash / List / Set / ZSet
键辅助 Exists / TTL / Remove
MULTI/EXEC 事务
SET NX PX 锁 + Lua 解锁
阻塞获取 + 续期 Watchdog
方法结果 AOP 缓存

5. ⚙ 环境要求

要求
.NET 8.0+
宿主 ASP.NET Core / 通用主机均可
Redis 可达的 Redis 实例(Demo 默认 localhost:6379
客户端 StackExchange.Redis(由包引入)

6. 📥 安装

dotnet add package EasyCore.Redis
# 当前版本 8.0.0

# 或按需拆分
dotnet add package EasyCore.Redis.Distributed
dotnet add package EasyCore.Redis.Locking
dotnet add package EasyCore.Redis.Service

7. ⚡ 三分钟快速开始

7️⃣.1️⃣ 代码配置注册

using EasyCore.Redis;

builder.Services.AddEasyCoreRedis(options =>
{
    options.EndPoints = new List<string> { "127.0.0.1:6379" };
    options.ConnectTimeout = TimeSpan.FromSeconds(5);
    options.SyncTimeout = TimeSpan.FromSeconds(5);
    options.DistributedName = "MyApp";   // 键前缀 → MyApp:user:1
    // options.Password = "***";
    // options.DefaultDatabase = 0;
});

7️⃣.2️⃣ 从 appsettings.json 注册

builder.Services.AddEasyCoreRedis(
    builder.Configuration.GetSection("EasyCore:Redis"));
{
  "EasyCore": {
    "Redis": {
      "EndPoints": [ "127.0.0.1:6379" ],
      "ConnectTimeout": "00:00:05",
      "SyncTimeout": "00:00:05",
      "DefaultDatabase": 0,
      "AbortOnConnectFail": false,
      "DistributedName": "MyApp"
    }
  }
}

7️⃣.3️⃣ 注入并使用

public class UserService(IDistributedCache cache, IDistributedLock locks)
{
    public async Task<string?> GetNameAsync(string userId)
    {
        await cache.StringSetAsync($"user:{userId}", "alice", seconds: 60);
        return await cache.StringGetAsync($"user:{userId}");
    }

    public async Task ProcessOrderAsync(string orderId)
    {
        await using var handle = await locks.AcquireLockAsync($"order:{orderId}", expirySeconds: 30);
        if (!handle.IsAcquired) return;
        // 临界区…
    }
}

7️⃣.4️⃣ 按特性拆分注册

builder.Services.AddEasyCoreRedisDistributed(o => { /* EndPoints… */ });
builder.Services.AddEasyCoreRedisLock();   // 复用已注册连接
builder.Services.AddEasyCoreRedisService(); // 自动扫描带 [ServerCache] 的服务
// 或显式:builder.Services.AddServerCacheProxy<IMyService, MyService>();

8. 🧩 配置项完整说明

DistributedOption(缓存与锁共用):

属性 类型 默认 说明
EndPoints List<string> 空(必填) 127.0.0.1:6379
User string? null Redis 6+ ACL 用户名
Password string? null 密码
ConnectTimeout TimeSpan 5s 连接超时
SyncTimeout TimeSpan 5s 同步操作超时
AbortOnConnectFail bool false 初次连接失败是否中止
DefaultDatabase int 0 默认 DB
DistributedName string "EasyCore" 键前缀命名空间

键规则:逻辑键 user:1 → 实际 Redis 键 {DistributedName}:user:1(例如 MyApp:user:1)。


9. 💾 分布式缓存 IDistributedCache

命名空间:EasyCore.Redis.Distributed

几乎所有 API 均提供 同步异步*Async)成对方法;异步方法支持 CancellationToken。复杂对象通过 JSON 序列化存取(StringGet<T> / StringSet<T> / HashGet<T> 等)。

9.1 快捷方法(映射到 String)

方法 Redis 说明
Get / GetAsync GET 取字符串
Get<T> / GetAsync<T> GET JSON 反序列化
Set / SetAsync SET 写字符串;seconds=0 表示不过期
Set<T> / SetAsync<T> SET JSON 序列化写入

9.2 键辅助

方法 Redis 说明
KeyExists / KeyExistsAsync EXISTS 键是否存在
KeyType / KeyTypeAsync TYPE 返回 string/hash/list/set/zset/none
KeyTimeToLive / KeyTimeToLiveAsync TTL 秒;-1 无过期,-2 键不存在
Refresh / RefreshAsync EXPIRE 重置绝对过期(seconds 必须 > 0)
Remove / RemoveAsync DEL 删除键

9.3 String

方法 Redis 说明
StringGet / StringGetAsync GET 取字符串
StringGet<T> / StringGetAsync<T> GET JSON 反序列化
StringSet / StringSetAsync SET / SET NX whenNotExists=true 时 NX;返回是否写入成功
StringSet<T> / StringSetAsync<T> SET JSON 写入
StringGetDeleteAsync GETDEL 原子取并删
StringIncrement / *Async INCRBY / INCRBYFLOAT 整数或浮点自增
StringDecrement / *Async DECRBY 自减
StringAppend / *Async APPEND 追加,返回新长度
StringGetLength / *Async STRLEN 长度
StringSetExpiry / *Async EXPIRE 设置过期
await cache.StringSetAsync("user:1", "alice", seconds: 60);
await cache.StringSetAsync("profile:1", new { Name = "alice", Age = 18 }, seconds: 300);
var ok = await cache.StringSetAsync("once", "v", whenNotExists: true); // SET NX
await cache.StringIncrementAsync("counter");
var prev = await cache.StringGetDeleteAsync("once");

9.4 Hash

方法 Redis 说明
HashSet(key, field, value) HSET 单字段;返回是否新建字段
HashSet<T>(...) HSET JSON 字段值
HashSet(key, IDictionary, seconds) HSET multi 批量;seconds>0 时顺便 EXPIRE
HashGet / HashGet<T> HGET 取字段
HashGetAll HGETALL 全部字段
HashDelete HDEL 删字段,返回删除数
HashExists HEXISTS 字段是否存在
HashGetKeys / HashGetValues HKEYS / HVALS 字段名 / 值
HashGetLength HLEN 字段数
HashIncrement HINCRBY / HINCRBYFLOAT 字段自增
await cache.HashSetAsync("user:1:profile", "age", "18");
await cache.HashSetAsync("user:1:profile", new Dictionary<string, string>
{
    ["name"] = "alice",
    ["city"] = "SH"
}, seconds: 300);
var all = await cache.HashGetAllAsync("user:1:profile");
await cache.HashIncrementAsync("user:1:profile", "score", 10);

9.5 List

方法 Redis 说明
ListLeftPush / ListRightPush LPUSH / RPUSH 头/尾入队(params,至少 1 个)
ListLeftPop / ListRightPop LPOP / RPOP 头/尾出队
ListRange LRANGE 区间;默认 0..-1 全部
ListGetLength LLEN 长度
ListGetByIndex / ListSetByIndex LINDEX / LSET 按下标读写
ListTrim LTRIM 裁剪
ListRemove LREM count:0=全部;正=从头;负=从尾

异步 params 重载中,CancellationToken 位于 values 之前:
ListRightPushAsync(key, cancellationToken, "a", "b")

await cache.ListRightPushAsync("queue", cancellationToken: default, "a", "b", "c");
var items = await cache.ListRangeAsync("queue");
var first = await cache.ListLeftPopAsync("queue");

9.6 Set

方法 Redis 说明
SetAdd / SetRemove SADD / SREM 增删成员
SetGetMembers SMEMBERS 全部成员
SetGetLength SCARD 基数
SetContains SISMEMBER 是否包含
SetPop SPOP 弹出随机成员
SetRandomMember / SetRandomMembers SRANDMEMBER 随机查看
SetMove SMOVE 在集合间移动
SetIntersect / SetUnion SINTER / SUNION 交 / 并
SetDifference SDIFF 差集
await cache.SetAddAsync("tags", cancellationToken: default, "redis", "cache");
var tags = await cache.SetGetMembersAsync("tags");
var both = await cache.SetIntersectAsync(cancellationToken: default, "tags", "tags:hot");

9.7 Sorted Set(ZSet)

使用 RedisSortedSetEntry(Member, Score) 表示带分成员。

方法 Redis 说明
SortedSetAdd ZADD 单条或批量
SortedSetRemove ZREM 删成员
SortedSetGetScore ZSCORE 取分
SortedSetGetRank ZRANK / ZREVRANK ascending=false 为倒序排名
SortedSetRangeByRank ZRANGE / ZREVRANGE 按排名区间
SortedSetRangeByRankWithScores … WITHSCORES 带分
SortedSetRangeByScore ZRANGEBYSCORE / ZREV… 按分区间
SortedSetRangeByScoreWithScores … WITHSCORES 带分
SortedSetGetLength ZCARD 成员数
SortedSetCountByScore ZCOUNT 分区间计数
SortedSetIncrementScore ZINCRBY 加分
SortedSetRemoveRangeByRank ZREMRANGEBYRANK 按排名删
SortedSetRemoveRangeByScore ZREMRANGEBYSCORE 按分删
await cache.SortedSetAddAsync("rank", "alice", 100);
await cache.SortedSetAddAsync("rank", new[]
{
    new RedisSortedSetEntry("bob", 90),
    new RedisSortedSetEntry("carol", 95)
});
var top = await cache.SortedSetRangeByRankWithScoresAsync("rank", 0, 9, ascending: false);

10. 🔄 事务 IDistributedTransaction / ICacheTransaction

命名空间:EasyCore.Redis.Distributed.Transaction

在事务对象上排队写操作,再 Commit / CommitAsync 执行 EXEC
Dispose 且未 Commit → 丢弃,不会执行。
不要在事务未提交时对同一逻辑去调 IDistributedCache 指望原子性——请用事务 API。

API 说明
IDistributedTransaction.CreateTransaction() 创建 ICacheTransaction
Set(key, value, seconds=0) 排队 SET(可带过期)
Set<T>(key, value, seconds=0) 排队 JSON SET
Remove(key) 排队 DEL
Commit / CommitAsync EXEC;成功返回 true
public class RedisTransaction(IDistributedTransaction transactions)
{
    public async Task RunAsync()
    {
        await using var tran = transactions.CreateTransaction();
        tran.Set("key1", "value1", seconds: 60)
            .Set("key2", new { Ok = true }, seconds: 60)
            .Remove("key3");
        await tran.CommitAsync();
    }
}

11. 🔐 分布式锁 IDistributedLock

命名空间:EasyCore.Redis.Locking

行为 实现
加锁 原子 SET key lockId PX expiry NX
解锁 Lua:仅当值等于 LockId 时 DEL
阻塞获取 指数退避(约 20–200ms),非忙等
续期 BlockingLock* 可传 renewalInterval,后台 Watchdog PEXPIRE;解锁/Dispose 自动取消

11.1 LockContext

属性 / 方法 说明
Key 逻辑锁键
LockId 锁令牌(Guid)
IsAcquired 是否持有锁
Dispose / DisposeAsync 持有时自动 UnLock

11.2 API 一览

方法 说明
AcquireLock / AcquireLockAsync 尝试一次;参数为秒或 TimeSpan
BlockingLock / BlockingLockAsync 阻塞直到成功或超时;可选续期间隔
UnLock / UnLockAsync 主动释放(通常用 await using 即可)
// 非阻塞
await using var handle = await locks.AcquireLockAsync("order:42", expirySeconds: 30);
if (handle.IsAcquired)
{
    // 临界区
}

// 阻塞 + 每 10 秒续期(租约 30 秒)
await using var blocking = await locks.BlockingLockAsync(
    "order:42",
    expirySeconds: 30,
    waitTimeoutSeconds: 5,
    renewalIntervalSeconds: 10);

12. ✨ 服务缓存 [ServerCache]

命名空间:EasyCore.Redis.Service(特性在 EasyCore.Redis.Service.Attribute

独立 NuGet 包(不依赖 EasyCore.Invocation)。通过 Castle DynamicProxy 拦截返回 Task<T> 的方法做 cache-aside;也可直接挂在 MVC Controller / Action(特性实现 IFilterFactory)。

12.1 放置位置(与 Invocation 风格一致,但自包含)

挂载位置 命中范围 生效路径
接口类型 [ServerCache] interface IFoo 该接口全部方法 Castle 接口代理
接口方法 仅该方法 Castle 接口代理
实现类 该类对外接口方法 Castle 接口代理
实现方法 仅该方法 Castle 接口代理
Controller / Action 该类或该 Action MVC IFilterFactory(无需全局 Filter)

解析优先级(最具体胜出):实现方法 → 接口方法 → 类 → 接口类型

与 EasyCore.Polly / EasyCore.Invocation 等其它包组合时:各自用 TryAddEnumerable 注册自己的 IAsyncInterceptor不要TryAdd<IAsyncInterceptor>,否则只会留下第一个),代理创建时通过 GetServices<IAsyncInterceptor>() 堆叠,互不引用、互不写死类型名。默认 Order:Invocation 0(最外)→ Polly 50 → ServerCache 100(最内)。

12.2 特性

属性 默认 说明
CacheSeconds 300 TTL(秒)
CacheNullValues false 是否缓存 null 结果
Order 100 MVC / 拦截器堆叠顺序(越小越外)

缓存键格式:svc:{MethodName}:{sha256},哈希输入为 声明类型全名:方法名:参数JSON

12.3 注册方式

using EasyCore.Redis.Service.Attribute;

// 推荐:特性挂在接口上,实现类保持干净
[ServerCache(CacheSeconds = 120)]
public interface IServer
{
    Task<string> GetUser(string userId);

    [ServerCache(CacheSeconds = 60)]
    Task<string> GetHot(string id);
}

public class Server : IServer
{
    public Task<string> GetUser(string userId)
        => Task.FromResult($"user-{userId}");

    public Task<string> GetHot(string id)
        => Task.FromResult($"hot-{id}");
}

// 方式 A:AddEasyCoreRedis / AddEasyCoreRedisService 自动扫描
builder.Services.AddEasyCoreRedisService();
// 或补充程序集:
builder.Services.AddEasyCoreRedisService(o => o.Assemblies.Add(typeof(Server).Assembly));

// 方式 B:显式代理
builder.Services.AddServerCacheProxy<IServer, Server>();

注入 IServer 后,调用会经代理。非泛型 Task(无结果)不会缓存。Controller 上直接标 [ServerCache] 即可,无需额外 Filter 注册。


13. ✅ 生产要点

主题 行为 / 建议
连接 单例共享 IRedisConnection,随宿主释放
超时 使用 TimeSpan 配置 ConnectTimeout / SyncTimeout
取消 异步 API 支持 CancellationToken
锁安全 解锁校验所有权;仅 IsAcquired 时 Dispose 才解锁
服务缓存扫描 可传 Assemblies;默认扫描入口程序集及已加载非框架程序集
密钥 密码放入配置中心 / 密钥库,勿提交仓库
前缀 多环境使用不同 DistributedName,避免键冲突

生产清单

  • EndPoints / 密码来自安全配置源
  • 为每个应用设置独立 DistributedName
  • 长临界区使用 BlockingLock + renewalInterval
  • [ServerCache] 仅用于幂等读;写路径主动失效或短 TTL
  • 事务:只通过 ICacheTransaction 排队后 Commit
  • 监控连接失败与锁争用日志

14. 🧪 Demo 项目

项目 说明 命令
[demo/Web.EasyCore.Cache](demo/Web.EasyCore.Cache) Swagger:缓存 / 事务 / 锁 / [ServerCache] + 交叉堆叠 dotnet run --project demo/Web.EasyCore.Cache

默认 Redis:localhost:6379(见 appsettings.jsonEasyCore:Redis)。

控制器示例:

  • GET /api/demo — 放置场景 A–F 总览
  • A–F Controllers — 接口类型 / 类 / 方法 / 接口方法 / 多接口 / API
  • ServiceCacheController[ServerCache] 参数重载(legacy)
  • ComboStackController/api/combo:三包堆叠联调
  • DistributedCacheController / Transaction / Lock — 底层 API

15. ❓ FAQ

Q: 为什么 Redis 里看不到我写的 user:1
A: 实际键是 {DistributedName}:user:1。检查配置中的 DistributedName

Q: 只想用锁,不装缓存包?
A: 引用 EasyCore.Redis.Locking,调用 AddEasyCoreRedisLock(options => { … }) 会一并注册连接。

Q: 事务里调用 IDistributedCache.SetAsync 会原子执行吗?
A: 不会。请使用 ICacheTransaction.Set(...).CommitAsync()

Q: [ServerCache] 为什么没生效?
A: 服务场景须注入接口(代理目标);特性可挂在接口 / 类 / 方法上;方法需返回 Task<T>;实现需被 AddServerCacheProxy 或自动扫描注册。API 场景可直接挂 Controller / Action。

Q: 能和 EasyCore.Polly / EasyCore.Invocation 一起用吗?
A: 可以。三者是独立 NuGet,互不引用;各包以 TryAddEnumerable 注册 IAsyncInterceptor,代理侧 GetServices 堆叠生效。勿用单槽 TryAdd<IAsyncInterceptor>,否则只会生效先注册的那一个。

Q: List*Async / Set*Asyncparams 参数顺序?
A: CancellationTokenparams 数组之前,例如 SetAddAsync(key, ct, "a", "b")

Q: 版本是多少?
A: 当前为 8.0.0


16. 📄 License

MIT — 详见 LICENSE 与 NuGet 包声明。


🤝 贡献

  1. Fork 并创建特性分支
  2. tests/EasyCore.Cache.Tests 补充测试
  3. 执行 dotnet testdotnet build EasyCore.Redis.sln
  4. 提交 Pull Request

欢迎 Issue / PR 🚀

Product Compatible and additional computed target framework versions.
.NET 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 was computed.  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 was computed.  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
8.3.1 127 7/23/2026
8.3.0 107 7/21/2026
8.0.0 110 7/17/2026