Grace.Cache 1.0.0.4

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

Grace.Cache

基于 ABP(Volo.Abp)和 FreeRedis 的 Redis 缓存模块封装。

安装

dotnet add package Grace.Cache

配置

appsettings.json 中添加 Redis 连接配置:

{
  "Redis": {
    "Configuration": "127.0.0.1:6379,defaultDatabase=0,poolsize=5,ssl=false"
  }
}

连接字符串参数:

参数 说明
password Redis 密码
defaultDatabase 默认数据库(0-15)
poolsize 连接池大小(默认 5)
ssl 是否启用 SSL
preheat 是否预热连接

使用

1. 注册模块

在项目的 AbpModule 中添加 DependsOn

[DependsOn(typeof(GraceCacheModule))]
public class MyApplicationModule : AbpModule
{
}

2. 注入并使用

public class MyService : ITransientDependency
{
    private readonly CachingRedisClient _cache;

    public MyService(CachingRedisClient cache)
    {
        _cache = cache;
    }

    public async Task DoSomething()
    {
        // 字符串操作
        await _cache.RedisClient.SetAsync("key", "value");
        var val = await _cache.RedisClient.GetAsync("key");

        // 对象序列化(自动使用 System.Text.Json)
        await _cache.RedisClient.SetAsync("user:1", new User { Name = "Alice" });
        var user = await _cache.RedisClient.GetAsync<User>("user:1");

        // 删除
        await _cache.RedisClient.DelAsync("key");

        // 设置过期时间
        await _cache.RedisClient.ExpireAsync("key", 60);
    }
}

3. FreeRedis 常用操作

var redis = _cache.RedisClient;

// 字符串
redis.Set("key", "value");
string val = redis.Get("key");

// 哈希
redis.HSet("hash", "field", "value");
string hval = redis.HGet("hash", "field");

// 列表
redis.LPush("list", "a", "b");
string[] items = redis.LRange("list", 0, -1);

// 发布/订阅
redis.Subscribe("channel", (_, msg) => Console.WriteLine(msg));
redis.Publish("channel", "hello");

// 管道
redis.Pipeline(p =>
{
    p.Set("key1", "val1");
    p.Set("key2", "val2");
});

所有操作均支持 Async 后缀的异步方法。

4. 资源池(ResourcePool)

为什么需要 ResourcePool?

实际项目中经常遇到:同一份数据在同一个请求内被多次读取。例如:

  • 当前用户信息,多个 Service 都会调用
  • 字典表/配置表,数据不变但多次查询
  • 远程 API 或数据库查询结果暂时复用

如果每次都走 Redis 或数据库,重复请求增加延迟和负载。ResourcePool 提供 请求级别的内存缓存,在同一个 Scoped 范围内复用已加载的数据。

核心价值
特性 说明
一次加载,多处复用 同一标识的资源仅在首次调用时执行 loadFunc,后续直接返回缓存结果
并发等待 多个线程同时请求同一未缓存资源时,只有一个执行 loadFunc,其余自动等待已完成的任务
Scoped 生命周期 缓存随请求结束自动释放(IScopedDependency),不同请求之间不共享,无需手动清理
类型安全 泛型方法 GetAsync<T> 带类型约束,不同资源类型自动加前缀,同名键不冲突
使用场景

场景 1:避免重复查询用户信息

在同一请求中,多个 Service 都可能获取当前用户信息:

public class CurrentUserService : ITransientDependency
{
    private readonly IResourcePool _pool;
    private readonly CachingRedisClient _cache;

    public CurrentUserService(IResourcePool pool, CachingRedisClient cache)
    {
        _pool = pool;
        _cache = cache;
    }

    public async Task<UserProfile> GetCurrentUserAsync(long userId)
    {
        return await _pool.GetAsync($"user:{userId}", async () =>
        {
            // 第一次调用时执行,后续调用直接返回内存缓存
            var json = await _cache.RedisClient.GetAsync($"user:{userId}");
            return JsonSerializer.Deserialize<UserProfile>(json);
        });
    }
}

场景 2:批量加载配置/字典数据

public class ConfigService : ITransientDependency
{
    private readonly IResourcePool _pool;
    private readonly CachingRedisClient _cache;

    public ConfigService(IResourcePool pool, CachingRedisClient cache)
    {
        _pool = pool;
        _cache = cache;
    }

    public async Task<List<City>> GetCitiesAsync()
    {
        // 同一请求内多次调用只查一次 Redis
        return await _pool.GetAsync("city_list", async () =>
        {
            return await _cache.RedisClient.GetAsync<List<City>>("dict:cities")
                   ?? new List<City>();
        });
    }
}

场景 3:远程 API 防重复调用

public async Task<WechatToken> GetAccessTokenAsync()
{
    return await _pool.GetAsync("wechat:access_token", async () =>
    {
        var resp = await _httpClient.GetFromJsonAsync<WechatToken>(
            "https://api.weixin.qq.com/cgi-bin/token");
        return resp;
    });
}
使用技巧
  1. 键自动按类型隔离GetAsync<T>("id") 实际存储键为 typeof(T).FullName + "_" + id,不同类型即使 id 相同也不会冲突。
  2. Guid 直传GetAsync<T>(guid, loadFunc) 无需手动 ToString()
  3. 同步/异步按需选择
    • 异步优先:await _pool.GetAsync("key", async () => await FetchAsync())
    • 同步数据:_pool.Get("key", () => FetchSync())
  4. 两层缓存组合ResourcePool 做请求级内存缓存 + CachingRedisClient 做跨请求 Redis 缓存,减少重复查询。
  5. 不要跨请求复用ResourcePoolIScopedDependency,生命周期与请求绑定,不适合存放跨请求数据(请直接使用 Redis)。

## 依赖

- [FreeRedis](https://github.com/2881099/FreeRedis) — Redis 客户端
- [Volo.Abp.Core](https://abp.io) — ABP 框架
- .NET 8 +
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
1.0.0.4 91 7/16/2026
1.0.0.3 246 6/4/2025
1.0.0.2 253 4/10/2025
1.0.0.1 251 2/18/2024
1.0.0 226 2/18/2024