Skip to content

Commit db6517b

Browse files
committed
add CacheKeyAccessor implementation
1 parent 5d59cc4 commit db6517b

File tree

14 files changed

+273
-6
lines changed

14 files changed

+273
-6
lines changed

package.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<!--Package Info-->
88
<PropertyGroup>
9-
<VersionPrefix>2.1.0</VersionPrefix>
9+
<VersionPrefix>2.2.0</VersionPrefix>
1010
<!--<VersionSuffix>beta-05</VersionSuffix>-->
1111

1212
<Description>The `asp.net core` server-side caching component implemented based on `ResourceFilter` and `ActionFilter`; 基于`ResourceFilter`和`ActionFilter`实现的`asp.net core`服务端缓存组件</Description>

readme.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,3 +324,13 @@ app.EnableResponseCachingDiagnosticLogger();
324324
```
325325

326326
如上配置后,内部将订阅相关`Diagnostic`,并将事件信息使用`ILogger`输出。
327+
328+
-------
329+
330+
## 其它
331+
332+
### 1. 启用 `CacheKeyAccessor` 在代码中访问当前请求的 `cache key`
333+
```C#
334+
services.AddCaching()
335+
.EnableCacheKeyAccessor(); //启用 CacheKeyAccessor ,从DI中获取 ICacheKeyAccessor 以访问当前请求的 `cache key`
336+
```

src/Cuture.AspNetCore.ResponseCaching/CacheKey/Builders/QueryKeysCacheKeyBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public override ValueTask<string> BuildAsync(FilterContext filterContext, String
4949
char[]? buffer = null;
5050
try
5151
{
52-
buffer = ArrayPool<char>.Shared.Rent(queryString.Value!.Length);
52+
buffer = ArrayPool<char>.Shared.Rent(queryString.Value!.Length + 1);
5353
var length = QueryStringOrderUtil.Order(queryString, buffer);
5454
keyBuilder.Append(CombineChar);
5555
keyBuilder.Append(buffer, 0, length);

src/Cuture.AspNetCore.ResponseCaching/CacheKey/Generators/FullPathAndQueryCacheKeyGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public ValueTask<string> GenerateKeyAsync(FilterContext filterContext)
3434
try
3535
{
3636
var path = pathValue.Value;
37-
buffer = ArrayPool<char>.Shared.Rent(method.Length + path.Length + queryString.Value!.Length);
37+
buffer = ArrayPool<char>.Shared.Rent(method.Length + path.Length + queryString.Value!.Length + 1);
3838

3939
var span = buffer.AsSpan();
4040
method.CopyTo(span);
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
namespace Cuture.AspNetCore.ResponseCaching;
2+
3+
/// <summary>
4+
/// 缓存Key访问器
5+
/// </summary>
6+
public interface ICacheKeyAccessor
7+
{
8+
#region Public 属性
9+
10+
/// <summary>
11+
/// 缓存key
12+
/// </summary>
13+
string? Key { get; }
14+
15+
#endregion Public 属性
16+
}
17+
18+
internal sealed class CacheKeyAccessor : ICacheKeyAccessor
19+
{
20+
#region Private 字段
21+
22+
private static readonly AsyncLocal<string?> s_asyncLocalCacheKey = new();
23+
24+
#endregion Private 字段
25+
26+
#region Public 属性
27+
28+
public string? Key { get => s_asyncLocalCacheKey.Value; set => s_asyncLocalCacheKey.Value = value; }
29+
30+
#endregion Public 属性
31+
}

src/Cuture.AspNetCore.ResponseCaching/Context/ResponseCachingContext.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ public class ResponseCachingContext : IDisposable
8181
/// </summary>
8282
public IResponseCache ResponseCache { get; }
8383

84+
/// <summary>
85+
/// 构建时使用的 <see cref="IServiceProvider"/>
86+
/// </summary>
87+
public IServiceProvider ServiceProvider { get; }
88+
8489
#endregion Public 属性
8590

8691
#region Protected 构造函数
@@ -107,13 +112,15 @@ protected ResponseCachingContext()
107112
/// <param name="options"></param>
108113
/// <param name="interceptorAggregator"></param>
109114
/// <param name="dumpStreamCapacity"></param>
115+
/// <param name="serviceProvider"></param>
110116
public ResponseCachingContext(EndpointMetadataCollection metadatas,
111117
ICacheKeyGenerator cacheKeyGenerator,
112118
IResponseCache responseCache,
113119
IResponseCacheDeterminer cacheDeterminer,
114120
ResponseCachingOptions options,
115121
InterceptorAggregator interceptorAggregator,
116-
int dumpStreamCapacity)
122+
int dumpStreamCapacity,
123+
IServiceProvider serviceProvider)
117124
{
118125
ArgumentNullException.ThrowIfNull(metadatas);
119126

@@ -131,13 +138,16 @@ public ResponseCachingContext(EndpointMetadataCollection metadatas,
131138
LockMillisecondsTimeout = Checks.ThrowIfLockMillisecondsTimeoutInvalid(executingLockMetadata?.LockMillisecondsTimeout ?? options.DefaultLockMillisecondsTimeout).Value;
132139

133140
Interceptors = interceptorAggregator;
141+
134142
DumpStreamCapacity = Checks.ThrowIfDumpStreamInitialCapacityTooSmall(dumpStreamCapacity, nameof(dumpStreamCapacity));
135143

136144
OnCannotExecutionThroughLock = options.OnCannotExecutionThroughLock ?? DefaultExecutionLockTimeoutFallback.SetStatus429;
137145
OnExecutionLockTimeout = executingLockMetadata?.OnExecutionLockTimeout
138146
?? options.OnExecutionLockTimeoutFallback
139147
?? DefaultExecutionLockTimeoutFallback.SetStatus429;
140148

149+
ServiceProvider = serviceProvider;
150+
141151
TMetadata? Metadata<TMetadata>() where TMetadata : class => metadatas.GetMetadata<TMetadata>();
142152
TMetadata RequiredMetadata<TMetadata>() where TMetadata : class => metadatas.RequiredMetadata<TMetadata>();
143153
}

src/Cuture.AspNetCore.ResponseCaching/DefaultResponseCachingFilterBuilder.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ public IFilterMetadata CreateFilter(IServiceProvider serviceProvider)
6868
cacheDeterminer: cacheDeterminer,
6969
options: buildContext.Options,
7070
interceptorAggregator: interceptorAggregator,
71-
dumpStreamCapacity: dumpStreamCapacity);
71+
dumpStreamCapacity: dumpStreamCapacity,
72+
serviceProvider: serviceProvider);
7273
return filterType switch
7374
{
7475
FilterType.Resource => executingLockPool is null

src/Cuture.AspNetCore.ResponseCaching/Filters/CacheFilterBase.cs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
using System.Runtime.CompilerServices;
1+
using System.Diagnostics.CodeAnalysis;
2+
using System.Runtime.CompilerServices;
23

34
using Cuture.AspNetCore.ResponseCaching.Diagnostics;
45
using Cuture.AspNetCore.ResponseCaching.Interceptors;
56
using Cuture.AspNetCore.ResponseCaching.ResponseCaches;
67

78
using Microsoft.AspNetCore.Mvc;
89
using Microsoft.AspNetCore.Mvc.Filters;
10+
using Microsoft.Extensions.DependencyInjection;
911
using Microsoft.Extensions.Logging;
1012

1113
namespace Cuture.AspNetCore.ResponseCaching.Filters;
@@ -19,6 +21,8 @@ public abstract class CacheFilterBase<TFilterExecutingContext>
1921
{
2022
#region Private 字段
2123

24+
private readonly CacheKeyAccessor? _cacheKeyAccessor;
25+
2226
private readonly CachingDiagnosticsAccessor _cachingDiagnosticsAccessor;
2327

2428
private readonly OnCacheStoringDelegate<ActionContext> _onCacheStoringDelegate;
@@ -27,6 +31,13 @@ public abstract class CacheFilterBase<TFilterExecutingContext>
2731

2832
#endregion Private 字段
2933

34+
#region Private 属性
35+
36+
[MemberNotNullWhen(true, nameof(_cacheKeyAccessor))]
37+
private bool NeedSetCacheKeyAccessor { get; }
38+
39+
#endregion Private 属性
40+
3041
#region Protected 属性
3142

3243
/// <inheritdoc cref="Diagnostics.CachingDiagnostics"/>
@@ -61,6 +72,11 @@ public CacheFilterBase(ResponseCachingContext context, CachingDiagnosticsAccesso
6172
CachingDiagnostics = _cachingDiagnosticsAccessor.CachingDiagnostics;
6273
Logger = CachingDiagnostics.Logger;
6374
ResponseCache = Context.ResponseCache;
75+
76+
_cacheKeyAccessor = context.ServiceProvider.GetService<CacheKeyAccessor>();
77+
78+
NeedSetCacheKeyAccessor = _cacheKeyAccessor is not null;
79+
6480
_onCacheStoringDelegate = InternalStoreCacheAsync;
6581
}
6682

@@ -169,6 +185,18 @@ protected Task<bool> WriteCacheToResponseWithInterceptorAsync(ActionContext cont
169185

170186
#endregion Response with cache
171187

188+
/// <summary>
189+
/// 设置 <see cref="ICacheKeyAccessor"/>
190+
/// </summary>
191+
/// <param name="cacheKey"></param>
192+
protected void SetCacheKeyAccessor(string cacheKey)
193+
{
194+
if (NeedSetCacheKeyAccessor)
195+
{
196+
_cacheKeyAccessor.Key = cacheKey;
197+
}
198+
}
199+
172200
#endregion Protected 方法
173201

174202
#region Dispose

src/Cuture.AspNetCore.ResponseCaching/Filters/DefaultActionCacheFilter.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ public async Task OnActionExecutionAsync(ActionExecutingContext executingContext
4545
var key = (await Context.KeyGenerator.GenerateKeyAsync(executingContext)).ToLowerInvariant();
4646
CachingDiagnostics.CacheKeyGenerated(executingContext, key, Context.KeyGenerator, Context);
4747

48+
SetCacheKeyAccessor(key);
49+
4850
if (key.Length > Context.MaxCacheKeyLength)
4951
{
5052
CachingDiagnostics.CacheKeyTooLong(key, Context.MaxCacheKeyLength, executingContext, Context);

src/Cuture.AspNetCore.ResponseCaching/Filters/DefaultResourceCacheFilter.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ private async Task InternalOnResourceExecutionAsync(ResourceExecutingContext exe
4949
var key = (await Context.KeyGenerator.GenerateKeyAsync(executingContext)).ToLowerInvariant();
5050
cachingDiagnostics.CacheKeyGenerated(executingContext, key, Context.KeyGenerator, Context);
5151

52+
SetCacheKeyAccessor(key);
53+
5254
if (key.Length > Context.MaxCacheKeyLength)
5355
{
5456
cachingDiagnostics.CacheKeyTooLong(key, Context.MaxCacheKeyLength, executingContext, Context);

0 commit comments

Comments
 (0)