refactor cache
This commit is contained in:
parent
a9b1449bf6
commit
e72a1f1613
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>$(TargetFramework)</TargetFramework>
|
||||
|
|
@ -37,7 +37,7 @@
|
|||
<PackageReference Include="System.Text.Json" Version="8.0.5" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageReference Include="Rougamo.Fody" Version="4.0.0" />
|
||||
<PackageReference Include="Rougamo.Fody" Version="4.0.4" />
|
||||
<PackageReference Include="AspectInjector" Version="2.8.2" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BotSharp.Abstraction.Infrastructures.Enums
|
||||
{
|
||||
public enum CacheType
|
||||
{
|
||||
MemoryCache,
|
||||
RedisCache,
|
||||
HybridCache
|
||||
}
|
||||
}
|
||||
|
|
@ -6,9 +6,10 @@ using Rougamo.Context;
|
|||
|
||||
namespace BotSharp.Core.Infrastructures;
|
||||
|
||||
public class SharpCacheAttribute : MoAttribute
|
||||
public class SharpCacheAttribute : AsyncMoAttribute
|
||||
{
|
||||
public static IServiceProvider Services { get; set; } = null!;
|
||||
private static readonly object NullMarker = new { __is_null = "$_is_null" };
|
||||
|
||||
private int _minutes;
|
||||
|
||||
|
|
@ -17,8 +18,9 @@ public class SharpCacheAttribute : MoAttribute
|
|||
_minutes = minutes;
|
||||
}
|
||||
|
||||
public override void OnEntry(MethodContext context)
|
||||
public override async ValueTask OnEntryAsync(MethodContext context)
|
||||
{
|
||||
|
||||
var settings = Services.GetRequiredService<SharpCacheSettings>();
|
||||
if (!settings.Enabled)
|
||||
{
|
||||
|
|
@ -26,9 +28,8 @@ public class SharpCacheAttribute : MoAttribute
|
|||
}
|
||||
|
||||
var cache = Services.GetRequiredService<ICacheService>();
|
||||
|
||||
var key = GetCacheKey(settings, context);
|
||||
var value = cache.GetAsync(key, context.TaskReturnType).Result;
|
||||
var value = await cache.GetAsync(key, context.TaskReturnType);
|
||||
if (value != null)
|
||||
{
|
||||
// check if the cache is out of date
|
||||
|
|
@ -41,7 +42,7 @@ public class SharpCacheAttribute : MoAttribute
|
|||
}
|
||||
}
|
||||
|
||||
public override void OnSuccess(MethodContext context)
|
||||
public override async ValueTask OnSuccessAsync(MethodContext context)
|
||||
{
|
||||
var settings = Services.GetRequiredService<SharpCacheSettings>();
|
||||
if (!settings.Enabled)
|
||||
|
|
@ -62,7 +63,7 @@ public class SharpCacheAttribute : MoAttribute
|
|||
if (context.ReturnValue != null)
|
||||
{
|
||||
var key = GetCacheKey(settings, context);
|
||||
cache.SetAsync(key, context.ReturnValue, new TimeSpan(0, _minutes, 0)).Wait();
|
||||
await cache.SetAsync(key, context.ReturnValue, new TimeSpan(0, _minutes, 0));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -71,25 +72,26 @@ public class SharpCacheAttribute : MoAttribute
|
|||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
|
||||
private string GetCacheKey(SharpCacheSettings settings, MethodContext context)
|
||||
{
|
||||
var key = settings.Prefix + ":" + context.Method.Name;
|
||||
foreach (var arg in context.Arguments)
|
||||
{
|
||||
if (arg is null)
|
||||
{
|
||||
key += "-" + "<NULL>";
|
||||
}
|
||||
else if (arg is ICacheKey withCacheKey)
|
||||
{
|
||||
key += "-" + withCacheKey.GetCacheKey();
|
||||
}
|
||||
else
|
||||
{
|
||||
key += "-" + arg.ToString();
|
||||
}
|
||||
}
|
||||
var prefixKey = settings.Prefix + ":" + context.Method.Name;
|
||||
return $"{prefixKey}_{string.Join("_", context.Arguments.Select(arg => GetCacheKey(arg)))}";
|
||||
}
|
||||
|
||||
return key;
|
||||
private string GetCacheKey(object? arg)
|
||||
{
|
||||
if (arg is null)
|
||||
{
|
||||
return NullMarker.GetHashCode().ToString();
|
||||
}
|
||||
else if (arg is ICacheKey withCacheKey)
|
||||
{
|
||||
return withCacheKey.GetCacheKey();
|
||||
}
|
||||
else
|
||||
{
|
||||
return arg.GetHashCode().ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,5 +3,6 @@ namespace BotSharp.Abstraction.Infrastructures;
|
|||
public class SharpCacheSettings
|
||||
{
|
||||
public bool Enabled { get; set; } = false;
|
||||
public string Prefix { get; set; } = "cache";
|
||||
public CacheType CacheType { get; set; } = Enums.CacheType.MemoryCache;
|
||||
public string Prefix { get; set; } = "cache";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -195,6 +195,7 @@
|
|||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.1" />
|
||||
<PackageReference Include="Nanoid" Version="3.1.0" />
|
||||
<PackageReference Include="Rougamo.Fody" Version="4.0.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ using BotSharp.Core.Infrastructures.Events;
|
|||
using BotSharp.Core.Roles.Services;
|
||||
using BotSharp.Abstraction.Templating;
|
||||
using BotSharp.Core.Templating;
|
||||
using BotSharp.Abstraction.Infrastructures.Enums;
|
||||
|
||||
namespace BotSharp.Core;
|
||||
|
||||
|
|
@ -34,15 +35,9 @@ public static class BotSharpCoreExtensions
|
|||
services.AddScoped<IUserService, UserService>();
|
||||
services.AddScoped<ProcessorFactory>();
|
||||
|
||||
// Register cache service
|
||||
var cacheSettings = new SharpCacheSettings();
|
||||
config.Bind("SharpCache", cacheSettings);
|
||||
services.AddSingleton(x => cacheSettings);
|
||||
services.AddSingleton<ICacheService, RedisCacheService>();
|
||||
|
||||
AddRedisEvents(services, config);
|
||||
|
||||
services.AddMemoryCache();
|
||||
// Register cache service
|
||||
AddCacheServices(services, config);
|
||||
|
||||
RegisterPlugins(services, config);
|
||||
AddBotSharpOptions(services, configOptions);
|
||||
|
|
@ -50,6 +45,24 @@ public static class BotSharpCoreExtensions
|
|||
return services;
|
||||
}
|
||||
|
||||
private static void AddCacheServices(IServiceCollection services, IConfiguration config)
|
||||
{
|
||||
services.AddMemoryCache();
|
||||
var cacheSettings = new SharpCacheSettings();
|
||||
config.Bind("SharpCache", cacheSettings);
|
||||
services.AddSingleton(x => cacheSettings);
|
||||
services.AddSingleton<MemoryCacheService>();
|
||||
services.AddSingleton<RedisCacheService>();
|
||||
services.AddSingleton<HybridCacheService>();
|
||||
services.AddSingleton<ICacheService>(sp =>
|
||||
cacheSettings.CacheType switch
|
||||
{
|
||||
CacheType.HybridCache => sp.GetRequiredService<HybridCacheService>(),
|
||||
CacheType.RedisCache => sp.GetRequiredService<RedisCacheService>(),
|
||||
_ => sp.GetRequiredService<MemoryCacheService>(),
|
||||
});
|
||||
}
|
||||
|
||||
public static IServiceCollection UsingSqlServer(this IServiceCollection services, IConfiguration config)
|
||||
{
|
||||
services.AddScoped<IBotSharpRepository>(sp =>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
using BotSharp.Abstraction.Infrastructures;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BotSharp.Core.Infrastructures
|
||||
{
|
||||
public class HybridCacheService : ICacheService
|
||||
{
|
||||
private readonly MemoryCacheService _memoryCache;
|
||||
private readonly RedisCacheService _redisCache;
|
||||
|
||||
public HybridCacheService(
|
||||
MemoryCacheService memoryCache,
|
||||
RedisCacheService redisCache)
|
||||
{
|
||||
_memoryCache = memoryCache;
|
||||
_redisCache = redisCache;
|
||||
}
|
||||
|
||||
public async Task<T?> GetAsync<T>(string key)
|
||||
{
|
||||
var result = await _memoryCache.GetAsync<T>(key);
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
result = await _redisCache.GetAsync<T>(key);
|
||||
if (result != null)
|
||||
{
|
||||
await SetAsync(key, result, TimeSpan.FromMinutes(5));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<object> GetAsync(string key, Type type)
|
||||
{
|
||||
var result = await _memoryCache.GetAsync(key, type);
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
result = await _redisCache.GetAsync(key, type);
|
||||
if (result != null)
|
||||
{
|
||||
await SetAsync(key, result, TimeSpan.FromMinutes(5));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task SetAsync<T>(string key, T value, TimeSpan? expiry)
|
||||
{
|
||||
await _memoryCache.SetAsync(key, value, expiry);
|
||||
await _redisCache.SetAsync(key, value, expiry);
|
||||
}
|
||||
|
||||
public async Task RemoveAsync(string key)
|
||||
{
|
||||
await _memoryCache.RemoveAsync(key);
|
||||
await _redisCache.RemoveAsync(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,14 +5,11 @@ namespace BotSharp.Core.Infrastructures;
|
|||
|
||||
public class MemoryCacheService : ICacheService
|
||||
{
|
||||
private static IMemoryCache _cache = new MemoryCache(new MemoryCacheOptions
|
||||
{
|
||||
});
|
||||
private readonly BotSharpDatabaseSettings _settings;
|
||||
private readonly IMemoryCache _cache;
|
||||
|
||||
public MemoryCacheService(BotSharpDatabaseSettings settings)
|
||||
public MemoryCacheService(IMemoryCache memoryCache)
|
||||
{
|
||||
_settings = settings;
|
||||
this._cache = memoryCache;
|
||||
}
|
||||
|
||||
public async Task<T?> GetAsync<T>(string key)
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
using BotSharp.Abstraction.Infrastructures;
|
||||
using Newtonsoft.Json;
|
||||
using StackExchange.Redis;
|
||||
|
||||
namespace BotSharp.Core.Infrastructures;
|
||||
|
||||
public class RedisCacheService : ICacheService
|
||||
{
|
||||
private IConnectionMultiplexer _redis = null!;
|
||||
|
||||
public RedisCacheService(IConnectionMultiplexer redis)
|
||||
{
|
||||
_redis = redis;
|
||||
}
|
||||
|
||||
public async Task<T?> GetAsync<T>(string key)
|
||||
{
|
||||
var db = _redis.GetDatabase();
|
||||
var value = await db.StringGetAsync(key);
|
||||
|
||||
if (value.HasValue)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<T>(value);
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public async Task<object> GetAsync(string key, Type type)
|
||||
{
|
||||
var db = _redis.GetDatabase();
|
||||
var value = await db.StringGetAsync(key);
|
||||
|
||||
if (value.HasValue)
|
||||
{
|
||||
return JsonConvert.DeserializeObject(value, type);
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
|
||||
public async Task SetAsync<T>(string key, T value, TimeSpan? expiry)
|
||||
{
|
||||
var db = _redis.GetDatabase();
|
||||
await db.StringSetAsync(key, JsonConvert.SerializeObject(value), expiry);
|
||||
}
|
||||
|
||||
public async Task RemoveAsync(string key)
|
||||
{
|
||||
var db = _redis.GetDatabase();
|
||||
await db.KeyDeleteAsync(key);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
using BotSharp.Abstraction.Infrastructures;
|
||||
using Newtonsoft.Json;
|
||||
using StackExchange.Redis;
|
||||
|
||||
namespace BotSharp.Core.Infrastructures;
|
||||
|
||||
public class RedisCacheService : ICacheService
|
||||
{
|
||||
private readonly BotSharpDatabaseSettings _settings;
|
||||
private static ConnectionMultiplexer redis = null!;
|
||||
|
||||
public RedisCacheService(BotSharpDatabaseSettings settings)
|
||||
{
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
public async Task<T?> GetAsync<T>(string key)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_settings.Redis))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
if (redis == null)
|
||||
{
|
||||
redis = ConnectionMultiplexer.Connect(_settings.Redis);
|
||||
}
|
||||
|
||||
var db = redis.GetDatabase();
|
||||
var value = await db.StringGetAsync(key);
|
||||
|
||||
if (value.HasValue)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<T>(value);
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public async Task<object> GetAsync(string key, Type type)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_settings.Redis))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
if (redis == null)
|
||||
{
|
||||
redis = ConnectionMultiplexer.Connect(_settings.Redis);
|
||||
}
|
||||
|
||||
var db = redis.GetDatabase();
|
||||
var value = await db.StringGetAsync(key);
|
||||
|
||||
if (value.HasValue)
|
||||
{
|
||||
return JsonConvert.DeserializeObject(value, type);
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
|
||||
public async Task SetAsync<T>(string key, T value, TimeSpan? expiry)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_settings.Redis))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (redis == null)
|
||||
{
|
||||
redis = ConnectionMultiplexer.Connect(_settings.Redis);
|
||||
}
|
||||
|
||||
var db = redis.GetDatabase();
|
||||
await db.StringSetAsync(key, JsonConvert.SerializeObject(value), expiry);
|
||||
}
|
||||
|
||||
public async Task RemoveAsync(string key)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_settings.Redis))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (redis == null)
|
||||
{
|
||||
redis = ConnectionMultiplexer.Connect(_settings.Redis);
|
||||
}
|
||||
|
||||
var db = redis.GetDatabase();
|
||||
await db.KeyDeleteAsync(key);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue