Skip to content

Commit a5f8025

Browse files
authored
Merge pull request #247 from cnblogs/add-redis-cache-provider
feat: add redis cache provider
2 parents aa0ee44 + d35606b commit a5f8025

File tree

5 files changed

+213
-0
lines changed

5 files changed

+213
-0
lines changed

Cnblogs.Architecture.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cnblogs.Architecture.Ddd.In
6262
EndProject
6363
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cnblogs.Architecture.Ddd.Infrastructure.FileProviders.AliyunOss", "src\Cnblogs.Architecture.Ddd.Infrastructure.FileProviders.AliyunOss\Cnblogs.Architecture.Ddd.Infrastructure.FileProviders.AliyunOss.csproj", "{9C76E136-1D79-408C-A17F-FD63632B00A9}"
6464
EndProject
65+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cnblogs.Architecture.Ddd.Infrastructure.CacheProviders.Redis", "src\Cnblogs.Architecture.Ddd.Infrastructure.CacheProviders.Redis\Cnblogs.Architecture.Ddd.Infrastructure.CacheProviders.Redis.csproj", "{1FF58B65-6C83-4F0C-909A-6606B4C754B8}"
66+
EndProject
6567
Global
6668
GlobalSection(SolutionConfigurationPlatforms) = preSolution
6769
Debug|Any CPU = Debug|Any CPU
@@ -94,6 +96,7 @@ Global
9496
{73665E32-3D10-4F71-B893-4C65F36332D0} = {D3A6DF01-017E-4088-936C-B3791F41DF53}
9597
{4BD98FBF-FB98-4172-B352-BB7BF8761FCB} = {D3A6DF01-017E-4088-936C-B3791F41DF53}
9698
{9C76E136-1D79-408C-A17F-FD63632B00A9} = {D3A6DF01-017E-4088-936C-B3791F41DF53}
99+
{1FF58B65-6C83-4F0C-909A-6606B4C754B8} = {D3A6DF01-017E-4088-936C-B3791F41DF53}
97100
EndGlobalSection
98101
GlobalSection(ProjectConfigurationPlatforms) = postSolution
99102
{54D9D850-1CFC-485E-97FE-87F41C220523}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
@@ -200,5 +203,9 @@ Global
200203
{9C76E136-1D79-408C-A17F-FD63632B00A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
201204
{9C76E136-1D79-408C-A17F-FD63632B00A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
202205
{9C76E136-1D79-408C-A17F-FD63632B00A9}.Release|Any CPU.Build.0 = Release|Any CPU
206+
{1FF58B65-6C83-4F0C-909A-6606B4C754B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
207+
{1FF58B65-6C83-4F0C-909A-6606B4C754B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
208+
{1FF58B65-6C83-4F0C-909A-6606B4C754B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
209+
{1FF58B65-6C83-4F0C-909A-6606B4C754B8}.Release|Any CPU.Build.0 = Release|Any CPU
203210
EndGlobalSection
204211
EndGlobal
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<Description>
5+
Provides remote cache provider that implemented with Redis
6+
</Description>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<ProjectReference Include="..\Cnblogs.Architecture.Ddd.Cqrs.DependencyInjection\Cnblogs.Architecture.Ddd.Cqrs.DependencyInjection.csproj" />
11+
<ProjectReference Include="..\Cnblogs.Architecture.Ddd.Infrastructure.Abstractions\Cnblogs.Architecture.Ddd.Infrastructure.Abstractions.csproj" />
12+
</ItemGroup>
13+
14+
<ItemGroup>
15+
<PackageReference Include="StackExchange.Redis" Version="2.7.33" />
16+
</ItemGroup>
17+
18+
</Project>
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
using Cnblogs.Architecture.Ddd.Cqrs.Abstractions;
2+
using Cnblogs.Architecture.Ddd.Cqrs.DependencyInjection;
3+
using Cnblogs.Architecture.Ddd.Infrastructure.CacheProviders.Redis;
4+
using Microsoft.Extensions.Configuration;
5+
using Microsoft.Extensions.Options;
6+
using StackExchange.Redis;
7+
8+
// ReSharper disable once CheckNamespace
9+
namespace Microsoft.Extensions.DependencyInjection;
10+
11+
/// <summary>
12+
/// Injectors for redis cache provider.
13+
/// </summary>
14+
public static class Injectors
15+
{
16+
/// <summary>
17+
/// Add redis cache as remote cache.
18+
/// </summary>
19+
/// <param name="injector">The injector.</param>
20+
/// <param name="configuration">The root configuration.</param>
21+
/// <param name="sectionName">The configuration section name for redis, defaults to Redis.</param>
22+
/// <param name="configure">The optional configuration.</param>
23+
/// <returns></returns>
24+
public static CqrsInjector AddRedisCache(
25+
this CqrsInjector injector,
26+
IConfiguration configuration,
27+
string sectionName = "Redis",
28+
Action<CacheableRequestOptions>? configure = null)
29+
{
30+
return AddRedisCache(injector, configuration.GetSection(sectionName), configure);
31+
}
32+
33+
/// <summary>
34+
/// Add redis cache as remote cache.
35+
/// </summary>
36+
/// <param name="injector">The injector.</param>
37+
/// <param name="section">The configuration section for redis.</param>
38+
/// <param name="configure">The optional configuration.</param>
39+
/// <returns></returns>
40+
public static CqrsInjector AddRedisCache(
41+
this CqrsInjector injector,
42+
IConfigurationSection section,
43+
Action<CacheableRequestOptions>? configure = null)
44+
{
45+
injector.Services.Configure<RedisOptions>(section);
46+
return AddRedisCache(injector, configure);
47+
}
48+
49+
/// <summary>
50+
/// Add redis cache as remote cache.
51+
/// </summary>
52+
/// <param name="injector">The injector.</param>
53+
/// <param name="connectionString">The connection string.</param>
54+
/// <param name="redisConfigure">Optional configuration for redis options.</param>
55+
/// <param name="configure">The configure for cacheable request options.</param>
56+
/// <returns></returns>
57+
public static CqrsInjector AddRedisCache(
58+
this CqrsInjector injector,
59+
string connectionString,
60+
Action<RedisOptions>? redisConfigure = null,
61+
Action<CacheableRequestOptions>? configure = null)
62+
{
63+
var options = ConfigurationOptions.Parse(connectionString, true);
64+
injector.Services.Configure<RedisOptions>(o =>
65+
{
66+
o.Configure = options;
67+
redisConfigure?.Invoke(o);
68+
});
69+
return AddRedisCache(injector, configure);
70+
}
71+
72+
private static CqrsInjector AddRedisCache(
73+
this CqrsInjector injector,
74+
Action<CacheableRequestOptions>? configure = null)
75+
{
76+
injector.Services.AddSingleton(
77+
sp => ConnectionMultiplexer.Connect(sp.GetRequiredService<IOptions<RedisOptions>>().Value.Configure));
78+
return injector.AddRemoteQueryCache<RedisCacheProvider>(configure);
79+
}
80+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
using System.Text.Json;
2+
using Cnblogs.Architecture.Ddd.Domain.Abstractions;
3+
using Cnblogs.Architecture.Ddd.Infrastructure.Abstractions;
4+
using Microsoft.Extensions.Options;
5+
using StackExchange.Redis;
6+
7+
namespace Cnblogs.Architecture.Ddd.Infrastructure.CacheProviders.Redis;
8+
9+
/// <summary>
10+
/// Remote cache provider implemented with Redis.
11+
/// </summary>
12+
public class RedisCacheProvider
13+
: IRemoteCacheProvider
14+
{
15+
private readonly RedisOptions _options;
16+
private readonly IDatabaseAsync _database;
17+
private readonly IDateTimeProvider _dateTimeProvider;
18+
19+
/// <summary>
20+
/// Remote cache provider implemented with Redis.
21+
/// </summary>
22+
/// <param name="multiplexer">The underlying multiplexer.</param>
23+
/// <param name="options">The options for this provider.</param>
24+
/// <param name="dateTimeProvider">The datetime provider.</param>
25+
public RedisCacheProvider(
26+
ConnectionMultiplexer multiplexer,
27+
IOptions<RedisOptions> options,
28+
IDateTimeProvider dateTimeProvider)
29+
{
30+
_dateTimeProvider = dateTimeProvider;
31+
_options = options.Value;
32+
_database = multiplexer.GetDatabase(_options.Database);
33+
}
34+
35+
/// <inheritdoc />
36+
public Task<bool> AddAsync<TResult>(string cacheKey, TResult value)
37+
{
38+
return _database.StringSetAsync(GetCacheKey(cacheKey), Serialize(value));
39+
}
40+
41+
/// <inheritdoc />
42+
public Task<bool> AddAsync<TResult>(string cacheKey, TimeSpan expires, TResult value)
43+
{
44+
return _database.StringSetAsync(GetCacheKey(cacheKey), Serialize(value), expires);
45+
}
46+
47+
/// <inheritdoc />
48+
public async Task<CacheEntry<TResult>?> GetAsync<TResult>(string cacheKey)
49+
{
50+
var json = await _database.StringGetAsync(GetCacheKey(cacheKey));
51+
if (json.IsNullOrEmpty)
52+
{
53+
return null;
54+
}
55+
56+
return DeSerialize<TResult>(json!);
57+
}
58+
59+
/// <inheritdoc />
60+
public Task<bool> RemoveAsync(string cacheKey)
61+
{
62+
return _database.KeyDeleteAsync(GetCacheKey(cacheKey));
63+
}
64+
65+
/// <inheritdoc />
66+
public Task<bool> UpdateAsync<TResult>(string cacheKey, TResult value)
67+
{
68+
return AddAsync(cacheKey, value);
69+
}
70+
71+
/// <inheritdoc />
72+
public Task<bool> UpdateAsync<TResult>(string cacheKey, TResult value, TimeSpan expires)
73+
{
74+
return AddAsync(cacheKey, expires, value);
75+
}
76+
77+
private string GetCacheKey(string key) => $"{_options.Prefix}{key}";
78+
79+
private string Serialize<TResult>(TResult result)
80+
=> JsonSerializer.Serialize(new CacheEntry<TResult>(result, _dateTimeProvider.UnixSeconds()));
81+
82+
private static CacheEntry<TResult>? DeSerialize<TResult>(string json)
83+
=> JsonSerializer.Deserialize<CacheEntry<TResult>>(json);
84+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using StackExchange.Redis;
2+
3+
namespace Cnblogs.Architecture.Ddd.Infrastructure.CacheProviders.Redis;
4+
5+
/// <summary>
6+
/// Options for redis connection.
7+
/// </summary>
8+
public class RedisOptions
9+
{
10+
/// <summary>
11+
/// Prefix for all redis keys.
12+
/// </summary>
13+
public string Prefix { get; set; } = "cache_";
14+
15+
/// <summary>
16+
/// The number of database to use.
17+
/// </summary>
18+
public int Database { get; set; } = -1;
19+
20+
/// <summary>
21+
/// The redis configuration, https://stackexchange.github.io/StackExchange.Redis/Configuration
22+
/// </summary>
23+
public ConfigurationOptions Configure { get; set; } = new();
24+
}

0 commit comments

Comments
 (0)