Skip to content

feat: add redis cache provider #247

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Cnblogs.Architecture.sln
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cnblogs.Architecture.Ddd.In
EndProject
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}"
EndProject
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}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -94,6 +96,7 @@ Global
{73665E32-3D10-4F71-B893-4C65F36332D0} = {D3A6DF01-017E-4088-936C-B3791F41DF53}
{4BD98FBF-FB98-4172-B352-BB7BF8761FCB} = {D3A6DF01-017E-4088-936C-B3791F41DF53}
{9C76E136-1D79-408C-A17F-FD63632B00A9} = {D3A6DF01-017E-4088-936C-B3791F41DF53}
{1FF58B65-6C83-4F0C-909A-6606B4C754B8} = {D3A6DF01-017E-4088-936C-B3791F41DF53}
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{54D9D850-1CFC-485E-97FE-87F41C220523}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
Expand Down Expand Up @@ -200,5 +203,9 @@ Global
{9C76E136-1D79-408C-A17F-FD63632B00A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9C76E136-1D79-408C-A17F-FD63632B00A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9C76E136-1D79-408C-A17F-FD63632B00A9}.Release|Any CPU.Build.0 = Release|Any CPU
{1FF58B65-6C83-4F0C-909A-6606B4C754B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1FF58B65-6C83-4F0C-909A-6606B4C754B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1FF58B65-6C83-4F0C-909A-6606B4C754B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1FF58B65-6C83-4F0C-909A-6606B4C754B8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<Description>
Provides remote cache provider that implemented with Redis
</Description>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Cnblogs.Architecture.Ddd.Cqrs.DependencyInjection\Cnblogs.Architecture.Ddd.Cqrs.DependencyInjection.csproj" />
<ProjectReference Include="..\Cnblogs.Architecture.Ddd.Infrastructure.Abstractions\Cnblogs.Architecture.Ddd.Infrastructure.Abstractions.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="StackExchange.Redis" Version="2.7.33" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using Cnblogs.Architecture.Ddd.Cqrs.Abstractions;
using Cnblogs.Architecture.Ddd.Cqrs.DependencyInjection;
using Cnblogs.Architecture.Ddd.Infrastructure.CacheProviders.Redis;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using StackExchange.Redis;

// ReSharper disable once CheckNamespace
namespace Microsoft.Extensions.DependencyInjection;

/// <summary>
/// Injectors for redis cache provider.
/// </summary>
public static class Injectors
{
/// <summary>
/// Add redis cache as remote cache.
/// </summary>
/// <param name="injector">The injector.</param>
/// <param name="configuration">The root configuration.</param>
/// <param name="sectionName">The configuration section name for redis, defaults to Redis.</param>
/// <param name="configure">The optional configuration.</param>
/// <returns></returns>
public static CqrsInjector AddRedisCache(
this CqrsInjector injector,
IConfiguration configuration,
string sectionName = "Redis",
Action<CacheableRequestOptions>? configure = null)
{
return AddRedisCache(injector, configuration.GetSection(sectionName), configure);
}

/// <summary>
/// Add redis cache as remote cache.
/// </summary>
/// <param name="injector">The injector.</param>
/// <param name="section">The configuration section for redis.</param>
/// <param name="configure">The optional configuration.</param>
/// <returns></returns>
public static CqrsInjector AddRedisCache(
this CqrsInjector injector,
IConfigurationSection section,
Action<CacheableRequestOptions>? configure = null)
{
injector.Services.Configure<RedisOptions>(section);
return AddRedisCache(injector, configure);
}

/// <summary>
/// Add redis cache as remote cache.
/// </summary>
/// <param name="injector">The injector.</param>
/// <param name="connectionString">The connection string.</param>
/// <param name="redisConfigure">Optional configuration for redis options.</param>
/// <param name="configure">The configure for cacheable request options.</param>
/// <returns></returns>
public static CqrsInjector AddRedisCache(
this CqrsInjector injector,
string connectionString,
Action<RedisOptions>? redisConfigure = null,
Action<CacheableRequestOptions>? configure = null)
{
var options = ConfigurationOptions.Parse(connectionString, true);
injector.Services.Configure<RedisOptions>(o =>
{
o.Configure = options;
redisConfigure?.Invoke(o);
});
return AddRedisCache(injector, configure);
}

private static CqrsInjector AddRedisCache(
this CqrsInjector injector,
Action<CacheableRequestOptions>? configure = null)
{
injector.Services.AddSingleton(
sp => ConnectionMultiplexer.Connect(sp.GetRequiredService<IOptions<RedisOptions>>().Value.Configure));
return injector.AddRemoteQueryCache<RedisCacheProvider>(configure);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System.Text.Json;
using Cnblogs.Architecture.Ddd.Domain.Abstractions;
using Cnblogs.Architecture.Ddd.Infrastructure.Abstractions;
using Microsoft.Extensions.Options;
using StackExchange.Redis;

namespace Cnblogs.Architecture.Ddd.Infrastructure.CacheProviders.Redis;

/// <summary>
/// Remote cache provider implemented with Redis.
/// </summary>
public class RedisCacheProvider
: IRemoteCacheProvider
{
private readonly RedisOptions _options;
private readonly IDatabaseAsync _database;
private readonly IDateTimeProvider _dateTimeProvider;

/// <summary>
/// Remote cache provider implemented with Redis.
/// </summary>
/// <param name="multiplexer">The underlying multiplexer.</param>
/// <param name="options">The options for this provider.</param>
/// <param name="dateTimeProvider">The datetime provider.</param>
public RedisCacheProvider(
ConnectionMultiplexer multiplexer,
IOptions<RedisOptions> options,
IDateTimeProvider dateTimeProvider)
{
_dateTimeProvider = dateTimeProvider;
_options = options.Value;
_database = multiplexer.GetDatabase(_options.Database);
}

/// <inheritdoc />
public Task<bool> AddAsync<TResult>(string cacheKey, TResult value)
{
return _database.StringSetAsync(GetCacheKey(cacheKey), Serialize(value));
}

/// <inheritdoc />
public Task<bool> AddAsync<TResult>(string cacheKey, TimeSpan expires, TResult value)
{
return _database.StringSetAsync(GetCacheKey(cacheKey), Serialize(value), expires);
}

/// <inheritdoc />
public async Task<CacheEntry<TResult>?> GetAsync<TResult>(string cacheKey)
{
var json = await _database.StringGetAsync(GetCacheKey(cacheKey));
if (json.IsNullOrEmpty)
{
return null;
}

return DeSerialize<TResult>(json!);
}

/// <inheritdoc />
public Task<bool> RemoveAsync(string cacheKey)
{
return _database.KeyDeleteAsync(GetCacheKey(cacheKey));
}

/// <inheritdoc />
public Task<bool> UpdateAsync<TResult>(string cacheKey, TResult value)
{
return AddAsync(cacheKey, value);
}

/// <inheritdoc />
public Task<bool> UpdateAsync<TResult>(string cacheKey, TResult value, TimeSpan expires)
{
return AddAsync(cacheKey, expires, value);
}

private string GetCacheKey(string key) => $"{_options.Prefix}{key}";

private string Serialize<TResult>(TResult result)
=> JsonSerializer.Serialize(new CacheEntry<TResult>(result, _dateTimeProvider.UnixSeconds()));

private static CacheEntry<TResult>? DeSerialize<TResult>(string json)
=> JsonSerializer.Deserialize<CacheEntry<TResult>>(json);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using StackExchange.Redis;

namespace Cnblogs.Architecture.Ddd.Infrastructure.CacheProviders.Redis;

/// <summary>
/// Options for redis connection.
/// </summary>
public class RedisOptions
{
/// <summary>
/// Prefix for all redis keys.
/// </summary>
public string Prefix { get; set; } = "cache_";

/// <summary>
/// The number of database to use.
/// </summary>
public int Database { get; set; } = -1;

/// <summary>
/// The redis configuration, https://stackexchange.github.io/StackExchange.Redis/Configuration
/// </summary>
public ConfigurationOptions Configure { get; set; } = new();
}
Loading