Skip to content

Commit 6dbbf94

Browse files
authored
Merge pull request #28 from sj-distributor/enhance-MultiSourceEvictable-logic
MultiSourceEvictable 允许根据多个规则清除缓存
2 parents 7490097 + 3a5ee24 commit 6dbbf94

File tree

8 files changed

+92
-14
lines changed

8 files changed

+92
-14
lines changed

FastCache.MultiSource/Attributes/MultiSourceEvictableAttribute.cs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System;
2+
using System.Collections.Concurrent;
23
using System.Collections.Generic;
4+
using System.Linq;
35
using System.Threading.Tasks;
46
using AspectCore.DynamicProxy;
57
using FastCache.Core.Driver;
@@ -12,12 +14,14 @@ namespace FastCache.MultiSource.Attributes
1214
public class MultiSourceEvictableAttribute : AbstractInterceptorAttribute
1315
{
1416
private readonly string[] _keys;
15-
private readonly string _expression;
17+
private readonly string[] _expression;
1618
private readonly Target _target;
1719

1820
public sealed override int Order { get; set; }
1921

20-
public MultiSourceEvictableAttribute(string[] keys, string expression, Target target)
22+
public override bool AllowMultiple { get; } = true;
23+
24+
public MultiSourceEvictableAttribute(string[] keys, string[] expression, Target target)
2125
{
2226
_keys = keys;
2327
_expression = expression;
@@ -43,18 +47,30 @@ public override async Task Invoke(AspectContext context, AspectDelegate next)
4347
throw new ArgumentOutOfRangeException();
4448
}
4549

50+
var tasks = new ConcurrentBag<Task>();
51+
4652
var dictionary = new Dictionary<string, object>();
4753
var parameterInfos = context.ImplementationMethod.GetParameters();
4854
for (var i = 0; i < context.Parameters.Length; i++)
4955
{
5056
dictionary.Add(parameterInfos[i].Name, context.Parameters[i]);
5157
}
5258

53-
foreach (var s in _keys)
59+
var keys = _keys.Select(x => x.Trim()).Distinct().ToList();
60+
var expressions = _expression.Select(x => x.Trim()).Distinct().ToList();
61+
62+
foreach (var key in keys)
5463
{
55-
var key = KeyGenerateHelper.GetKey(_expression, dictionary);
64+
foreach (var expression in expressions)
65+
{
66+
var deleteKey = KeyGenerateHelper.GetKey(expression, dictionary);
67+
tasks.Add(cacheClient.Delete(deleteKey, key));
68+
}
69+
}
5670

57-
await cacheClient.Delete(key, s);
71+
if (tasks.Count > 0)
72+
{
73+
await Task.WhenAll(tasks);
5874
}
5975
}
6076
}

IntegrationTests/MultiSourceApiRequestCacheTests.cs

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
using System.Net.Http;
77
using System.Net.Http.Json;
88
using System.Threading.Tasks;
9+
using FastCache.Core.Driver;
10+
using FastCache.Redis.Driver;
911
using Microsoft.AspNetCore.Mvc.Testing;
1012
using Microsoft.Extensions.DependencyInjection;
1113
using TestApi.DB;
@@ -31,7 +33,8 @@ public MultiSourceApiRequestCacheTests(WebApplicationFactory<Program> factory)
3133
new()
3234
{
3335
Id = "1",
34-
Name = "anson1"
36+
Name = "anson1",
37+
Age = 10
3538
},
3639
new()
3740
{
@@ -148,7 +151,7 @@ public async void CacheAndEvictOther(string baseUrl)
148151
var timeResult = end - start;
149152
Assert.True(timeResult < 500000);
150153
}
151-
154+
152155
[Theory]
153156
[InlineData("/MultiSource")]
154157
public async Task TestUpdated(string baseUrl)
@@ -186,4 +189,46 @@ public async void CacheResultTaskNull(string baseUrl)
186189
var result1 = await resp1.Content.ReadAsStringAsync();
187190
Assert.Equal("", result1);
188191
}
192+
193+
[Theory]
194+
[InlineData("/MultiSource")]
195+
public async void TestMultiSourceEvictableAllowsEvictionByMultipleRules(string baseUrl)
196+
{
197+
var responseMessage = await _httpClient.GetAsync($"{baseUrl}?id=1");
198+
Assert.Equal(responseMessage.StatusCode, HttpStatusCode.OK);
199+
200+
var user = await responseMessage.Content.ReadFromJsonAsync<User>();
201+
Assert.Equal(user.Name, "anson1");
202+
Assert.Equal(user.Age, 10);
203+
204+
var responseMessageBySearchName = await _httpClient.GetAsync($"{baseUrl}/get/name?name=anson1");
205+
Assert.Equal(responseMessageBySearchName.StatusCode, HttpStatusCode.OK);
206+
207+
var userBySearchName = await responseMessageBySearchName.Content.ReadFromJsonAsync<User>();
208+
209+
Assert.Equal(userBySearchName.Name, "anson1");
210+
Assert.Equal(userBySearchName.Age, 10);
211+
212+
user.Age = 1;
213+
214+
var updateAfter = await _httpClient.PutAsJsonAsync(baseUrl, user);
215+
Assert.Equal(updateAfter.StatusCode, HttpStatusCode.OK);
216+
217+
var responseMessageBySearchNameUpdated = await _httpClient.GetAsync($"{baseUrl}/get/name?name=anson1");
218+
Assert.Equal(responseMessageBySearchNameUpdated.StatusCode, HttpStatusCode.OK);
219+
220+
var userUpdated = await responseMessageBySearchNameUpdated.Content.ReadFromJsonAsync<User>();
221+
Assert.Equal("anson1", userUpdated.Name);
222+
Assert.Equal(1, userUpdated.Age);
223+
224+
var deleted = await _httpClient.DeleteAsync($"{baseUrl}?id=1");
225+
Assert.True(deleted.StatusCode == HttpStatusCode.OK);
226+
227+
var responseMessageBySearchNameDeleted = await _httpClient.GetAsync($"{baseUrl}/get/name?name=anson1");
228+
Assert.Equal(responseMessageBySearchNameDeleted.StatusCode, HttpStatusCode.OK);
229+
230+
var userDeleted = await responseMessageBySearchNameDeleted.Content.ReadFromJsonAsync<User>();
231+
Assert.Equal("anson1", userDeleted.Name);
232+
Assert.Equal(1, userDeleted.Age);
233+
}
189234
}

TestApi/Controllers/MultiSourceController.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@ public User Add(User user)
3232

3333
[HttpPut]
3434
[MultiSourceCacheable("MultiSource-single", "{user:id}", Target.Redis, 5)]
35-
[MultiSourceEvictable(new[] { "MultiSource-single", "MultiSources" }, "{user:id}", Target.Redis)]
35+
[MultiSourceEvictable(new[] { "MultiSource-single", "MultiSources" }, ["{user:id}", "{user:name}"], Target.Redis)]
3636
public virtual async Task<User> Update(User user)
3737
{
3838
return await _userService.Update(user);
3939
}
4040

4141
[HttpDelete]
42-
[MultiSourceEvictable(new[] { "MultiSource-single", "MultiSources" }, "{id}", Target.Redis)]
42+
[MultiSourceEvictable(new[] { "MultiSource-single", "MultiSources" }, ["{id}"], Target.Redis)]
4343
public virtual bool Delete(string id)
4444
{
4545
return _userService.Delete(id);
@@ -57,4 +57,10 @@ public virtual IEnumerable<User> Users(string page)
5757
{
5858
return await _userService.SingleOrDefault(id);
5959
}
60+
61+
[HttpGet("get/name")]
62+
public virtual async Task<User?> SearchName(string name)
63+
{
64+
return await _userService.SingleOrDefaultByName(name);
65+
}
6066
}

TestApi/Controllers/MultiSourceInMemoryController.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@ public User Add(User user)
3232

3333
[HttpPut]
3434
[MultiSourceCacheable("MultiSource-single", "{user:id}", Target.InMemory, 5)]
35-
[MultiSourceEvictable(new[] { "MultiSource-single", "MultiSources" }, "{user:id}", Target.InMemory)]
35+
[MultiSourceEvictable(new[] { "MultiSource-single", "MultiSources" }, ["{user:id}"], Target.InMemory)]
3636
public virtual async Task<User> Update(User user)
3737
{
3838
return await _userService.Update(user);
3939
}
4040

4141
[HttpDelete]
42-
[MultiSourceEvictable(new[] { "MultiSource-single", "MultiSources" }, "{id}", Target.InMemory)]
42+
[MultiSourceEvictable(new[] { "MultiSource-single", "MultiSources" }, ["{id}"], Target.InMemory)]
4343
public virtual bool Delete(string id)
4444
{
4545
return _userService.Delete(id);

TestApi/Controllers/UseLockController.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class UseLockController(ILockUserService lockUserService)
1313
[HttpPost("add-with-cache")]
1414
[DistributedLock("user-add")]
1515
[MultiSourceCacheable("MultiSource-single", "{user:id}", Target.Redis, 5)]
16-
[MultiSourceEvictable(new[] { "MultiSource-single", "MultiSources" }, "{user:id}", Target.Redis)]
16+
[MultiSourceEvictable(new[] { "MultiSource-single", "MultiSources" }, ["{user:id}"], Target.Redis)]
1717
public virtual async Task<User> AddWithCache(User user, int delayMs = 0)
1818
{
1919
return await lockUserService.Add(user, delayMs);
@@ -32,7 +32,7 @@ public virtual async Task<User> Get(string id)
3232
{
3333
return await lockUserService.Single(id);
3434
}
35-
35+
3636
[HttpGet("users")]
3737
[MultiSourceCacheable("MultiSource-single", "{id}", Target.Redis, 5)]
3838
public virtual IEnumerable<User> Users(string page)
@@ -41,7 +41,7 @@ public virtual IEnumerable<User> Users(string page)
4141
}
4242

4343
[HttpDelete]
44-
[MultiSourceEvictable(new[] { "MultiSource-single", "MultiSources" }, "*{id}*", Target.Redis)]
44+
[MultiSourceEvictable(new[] { "MultiSource-single", "MultiSources" }, ["*{id}*"], Target.Redis)]
4545
public virtual bool Delete(string id)
4646
{
4747
return lockUserService.Delete(id);

TestApi/Entity/User.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,7 @@ public record User : IEntity
77
public string Id { get; set; }
88
public string Name { get; set; }
99

10+
public int Age { get; set; }
11+
1012
[NotMapped]public List<long>? ThirdPartyIds { get; set; }
1113
}

TestApi/Service/IMultiSourceService.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ public interface IMultiSourceService
1010

1111
Task<User?> SingleOrDefault(string id);
1212

13+
Task<User?> SingleOrDefaultByName(string name);
14+
1315
Task<User> Update(User user);
1416

1517
bool Delete(string id);

TestApi/Service/MultiSourceService.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public virtual async Task<User> Update(User user)
2626
{
2727
var first = await dbContext.Set<User>().FirstAsync(x => x.Id == user.Id);
2828
first.Name = user.Name;
29+
first.Age = user.Age;
2930
dbContext.Set<User>().Update(first);
3031
await dbContext.SaveChangesAsync();
3132
return first;
@@ -52,6 +53,12 @@ public virtual IEnumerable<User> List(string page)
5253
return await dbContext.Set<User>().SingleOrDefaultAsync(x => x.Id == id);
5354
}
5455

56+
[MultiSourceCacheable("MultiSource-single", "{name}", Target.Redis, 60)]
57+
public async Task<User?> SingleOrDefaultByName(string name)
58+
{
59+
return await dbContext.Set<User>().SingleOrDefaultAsync(x => x.Name == name);
60+
}
61+
5562
public virtual Task<string?> TestReturnNull()
5663
{
5764
return null;

0 commit comments

Comments
 (0)