Skip to content

Commit b364620

Browse files
committed
refactor cache
1 parent a9b1449 commit b364620

File tree

10 files changed

+188
-134
lines changed

10 files changed

+188
-134
lines changed

src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<TargetFramework>$(TargetFramework)</TargetFramework>
@@ -37,7 +37,7 @@
3737
<PackageReference Include="System.Text.Json" Version="8.0.5" />
3838
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
3939
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
40-
<PackageReference Include="Rougamo.Fody" Version="4.0.0" />
40+
<PackageReference Include="Rougamo.Fody" Version="4.0.4" />
4141
<PackageReference Include="AspectInjector" Version="2.8.2" />
4242
</ItemGroup>
4343

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace BotSharp.Abstraction.Infrastructures.Enums
8+
{
9+
public enum CacheType
10+
{
11+
MemoryCache,
12+
RedisCache,
13+
HybridCache
14+
}
15+
}

src/Infrastructure/BotSharp.Abstraction/Infrastructures/SharpCacheAttribute.cs

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66

77
namespace BotSharp.Core.Infrastructures;
88

9-
public class SharpCacheAttribute : MoAttribute
9+
public class SharpCacheAttribute : AsyncMoAttribute
1010
{
1111
public static IServiceProvider Services { get; set; } = null!;
12+
private static readonly object NullMarker = new { __is_null = "$_is_null" };
1213

1314
private int _minutes;
1415

@@ -17,18 +18,18 @@ public SharpCacheAttribute(int minutes = 60)
1718
_minutes = minutes;
1819
}
1920

20-
public override void OnEntry(MethodContext context)
21+
public override async ValueTask OnEntryAsync(MethodContext context)
2122
{
23+
2224
var settings = Services.GetRequiredService<SharpCacheSettings>();
2325
if (!settings.Enabled)
2426
{
2527
return;
2628
}
2729

2830
var cache = Services.GetRequiredService<ICacheService>();
29-
3031
var key = GetCacheKey(settings, context);
31-
var value = cache.GetAsync(key, context.TaskReturnType).Result;
32+
var value = await cache.GetAsync(key, context.TaskReturnType);
3233
if (value != null)
3334
{
3435
// check if the cache is out of date
@@ -41,7 +42,7 @@ public override void OnEntry(MethodContext context)
4142
}
4243
}
4344

44-
public override void OnSuccess(MethodContext context)
45+
public override async ValueTask OnSuccessAsync(MethodContext context)
4546
{
4647
var settings = Services.GetRequiredService<SharpCacheSettings>();
4748
if (!settings.Enabled)
@@ -62,7 +63,7 @@ public override void OnSuccess(MethodContext context)
6263
if (context.ReturnValue != null)
6364
{
6465
var key = GetCacheKey(settings, context);
65-
cache.SetAsync(key, context.ReturnValue, new TimeSpan(0, _minutes, 0)).Wait();
66+
await cache.SetAsync(key, context.ReturnValue, new TimeSpan(0, _minutes, 0));
6667
}
6768
}
6869

@@ -71,25 +72,26 @@ public virtual Task<bool> IsOutOfDate(MethodContext context, object value)
7172
return Task.FromResult(false);
7273
}
7374

75+
7476
private string GetCacheKey(SharpCacheSettings settings, MethodContext context)
7577
{
76-
var key = settings.Prefix + ":" + context.Method.Name;
77-
foreach (var arg in context.Arguments)
78+
var prefixKey = settings.Prefix + ":" + context.Method.Name;
79+
return $"{prefixKey}_{string.Join("_", context.Arguments.Select(arg => GetCacheKey(arg)))}";
80+
}
81+
82+
private string GetCacheKey(object? arg)
83+
{
84+
if (arg is null)
7885
{
79-
if (arg is null)
80-
{
81-
key += "-" + "<NULL>";
82-
}
83-
else if (arg is ICacheKey withCacheKey)
84-
{
85-
key += "-" + withCacheKey.GetCacheKey();
86-
}
87-
else
88-
{
89-
key += "-" + arg.ToString();
90-
}
86+
return NullMarker.GetHashCode().ToString();
87+
}
88+
else if (arg is ICacheKey withCacheKey)
89+
{
90+
return withCacheKey.GetCacheKey();
91+
}
92+
else
93+
{
94+
return arg.GetHashCode().ToString();
9195
}
92-
93-
return key;
9496
}
9597
}

src/Infrastructure/BotSharp.Abstraction/Infrastructures/SharpCacheSettings.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ namespace BotSharp.Abstraction.Infrastructures;
33
public class SharpCacheSettings
44
{
55
public bool Enabled { get; set; } = false;
6-
public string Prefix { get; set; } = "cache";
6+
public CacheType CacheType { get; set; } = Enums.CacheType.MemoryCache;
7+
public string Prefix { get; set; } = "cache";
78
}

src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@
195195
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
196196
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.1" />
197197
<PackageReference Include="Nanoid" Version="3.1.0" />
198+
<PackageReference Include="Rougamo.Fody" Version="4.0.4" />
198199
</ItemGroup>
199200

200201
<ItemGroup>

src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
using BotSharp.Core.Roles.Services;
1515
using BotSharp.Abstraction.Templating;
1616
using BotSharp.Core.Templating;
17+
using BotSharp.Abstraction.Infrastructures.Enums;
1718

1819
namespace BotSharp.Core;
1920

@@ -34,22 +35,34 @@ public static IServiceCollection AddBotSharpCore(this IServiceCollection service
3435
services.AddScoped<IUserService, UserService>();
3536
services.AddScoped<ProcessorFactory>();
3637

37-
// Register cache service
38-
var cacheSettings = new SharpCacheSettings();
39-
config.Bind("SharpCache", cacheSettings);
40-
services.AddSingleton(x => cacheSettings);
41-
services.AddSingleton<ICacheService, RedisCacheService>();
42-
4338
AddRedisEvents(services, config);
44-
45-
services.AddMemoryCache();
39+
// Register cache service
40+
AddCacheServices(services, config);
4641

4742
RegisterPlugins(services, config);
4843
AddBotSharpOptions(services, configOptions);
4944

5045
return services;
5146
}
5247

48+
private static void AddCacheServices(IServiceCollection services, IConfiguration config)
49+
{
50+
services.AddMemoryCache();
51+
var cacheSettings = new SharpCacheSettings();
52+
config.Bind("SharpCache", cacheSettings);
53+
services.AddSingleton(x => cacheSettings);
54+
services.AddSingleton<MemoryCacheService>();
55+
services.AddSingleton<RedisCacheService>();
56+
services.AddSingleton<HybridCacheService>();
57+
services.AddSingleton<ICacheService>(sp =>
58+
cacheSettings.CacheType switch
59+
{
60+
CacheType.HybridCache => sp.GetRequiredService<HybridCacheService>(),
61+
CacheType.RedisCache => sp.GetRequiredService<RedisCacheService>(),
62+
_ => sp.GetRequiredService<MemoryCacheService>(),
63+
});
64+
}
65+
5366
public static IServiceCollection UsingSqlServer(this IServiceCollection services, IConfiguration config)
5467
{
5568
services.AddScoped<IBotSharpRepository>(sp =>
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
using BotSharp.Abstraction.Infrastructures;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
8+
namespace BotSharp.Core.Infrastructures
9+
{
10+
public class HybridCacheService : ICacheService
11+
{
12+
private readonly MemoryCacheService _memoryCache;
13+
private readonly RedisCacheService _redisCache;
14+
15+
public HybridCacheService(
16+
MemoryCacheService memoryCache,
17+
RedisCacheService redisCache)
18+
{
19+
_memoryCache = memoryCache;
20+
_redisCache = redisCache;
21+
}
22+
23+
public async Task<T?> GetAsync<T>(string key)
24+
{
25+
var result = await _memoryCache.GetAsync<T>(key);
26+
if (result != null)
27+
{
28+
return result;
29+
}
30+
result = await _redisCache.GetAsync<T>(key);
31+
if (result != null)
32+
{
33+
await SetAsync(key, result, TimeSpan.FromMinutes(5));
34+
}
35+
return result;
36+
}
37+
38+
public async Task<object> GetAsync(string key, Type type)
39+
{
40+
var result = await _memoryCache.GetAsync(key, type);
41+
if (result != null)
42+
{
43+
return result;
44+
}
45+
result = await _redisCache.GetAsync(key, type);
46+
if (result != null)
47+
{
48+
await SetAsync(key, result, TimeSpan.FromMinutes(5));
49+
}
50+
51+
return result;
52+
}
53+
54+
public async Task SetAsync<T>(string key, T value, TimeSpan? expiry)
55+
{
56+
await _memoryCache.SetAsync(key, value, expiry);
57+
await _redisCache.SetAsync(key, value, expiry);
58+
}
59+
60+
public async Task RemoveAsync(string key)
61+
{
62+
await _memoryCache.RemoveAsync(key);
63+
await _redisCache.RemoveAsync(key);
64+
}
65+
}
66+
}

src/Infrastructure/BotSharp.Core/Infrastructures/MemoryCacheService.cs renamed to src/Infrastructure/BotSharp.Core/Infrastructures/Cache/MemoryCacheService.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,11 @@ namespace BotSharp.Core.Infrastructures;
55

66
public class MemoryCacheService : ICacheService
77
{
8-
private static IMemoryCache _cache = new MemoryCache(new MemoryCacheOptions
9-
{
10-
});
11-
private readonly BotSharpDatabaseSettings _settings;
8+
private readonly IMemoryCache _cache;
129

13-
public MemoryCacheService(BotSharpDatabaseSettings settings)
10+
public MemoryCacheService(IMemoryCache memoryCache)
1411
{
15-
_settings = settings;
12+
this._cache = memoryCache;
1613
}
1714

1815
public async Task<T?> GetAsync<T>(string key)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
using BotSharp.Abstraction.Infrastructures;
2+
using Newtonsoft.Json;
3+
using StackExchange.Redis;
4+
5+
namespace BotSharp.Core.Infrastructures;
6+
7+
public class RedisCacheService : ICacheService
8+
{
9+
private IConnectionMultiplexer _redis = null!;
10+
11+
public RedisCacheService(IConnectionMultiplexer redis)
12+
{
13+
_redis = redis;
14+
}
15+
16+
public async Task<T?> GetAsync<T>(string key)
17+
{
18+
var db = _redis.GetDatabase();
19+
var value = await db.StringGetAsync(key);
20+
21+
if (value.HasValue)
22+
{
23+
return JsonConvert.DeserializeObject<T>(value);
24+
}
25+
26+
return default;
27+
}
28+
29+
public async Task<object> GetAsync(string key, Type type)
30+
{
31+
var db = _redis.GetDatabase();
32+
var value = await db.StringGetAsync(key);
33+
34+
if (value.HasValue)
35+
{
36+
return JsonConvert.DeserializeObject(value, type);
37+
}
38+
39+
return default;
40+
}
41+
42+
43+
public async Task SetAsync<T>(string key, T value, TimeSpan? expiry)
44+
{
45+
var db = _redis.GetDatabase();
46+
await db.StringSetAsync(key, JsonConvert.SerializeObject(value), expiry);
47+
}
48+
49+
public async Task RemoveAsync(string key)
50+
{
51+
var db = _redis.GetDatabase();
52+
await db.KeyDeleteAsync(key);
53+
}
54+
}

0 commit comments

Comments
 (0)