Skip to content

Commit 90e9c6b

Browse files
committed
feat: 新增 取消令牌相关方法
1 parent 139b0e0 commit 90e9c6b

File tree

8 files changed

+294
-3
lines changed

8 files changed

+294
-3
lines changed

framework/src/Bing.AspNetCore/Bing/AspNetCore/Threading/HttpContextCancellationTokenProvider.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using System.Threading;
2-
using Bing.DependencyInjection;
1+
using Bing.DependencyInjection;
32
using Bing.Threading;
43
using Microsoft.AspNetCore.Http;
54
using Microsoft.Extensions.DependencyInjection;
@@ -27,4 +26,4 @@ public class HttpContextCancellationTokenProvider : ICancellationTokenProvider
2726
/// 异步任务取消令牌
2827
/// </summary>
2928
public CancellationToken Token => _httpContextAccessor.HttpContext?.RequestAborted ?? CancellationToken.None;
30-
}
29+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
using System.Collections.Concurrent;
2+
using Bing.Collections;
3+
using Microsoft.Extensions.Logging;
4+
using Microsoft.Extensions.Logging.Abstractions;
5+
6+
namespace Bing.Threading;
7+
8+
/// <summary>
9+
/// 环境数据上下文环境范围提供程序
10+
/// </summary>
11+
/// <typeparam name="T">泛型类型</typeparam>
12+
public class AmbientDataContextAmbientScopeProvider<T> : IAmbientScopeProvider<T>
13+
{
14+
/// <summary>
15+
/// 日志
16+
/// </summary>
17+
public ILogger<AmbientDataContextAmbientScopeProvider<T>> Logger { get; set; }
18+
19+
/// <summary>
20+
/// 范围字典
21+
/// </summary>
22+
// ReSharper disable once InconsistentNaming
23+
private static readonly ConcurrentDictionary<string, ScopeItem> ScopeDictionary = new();
24+
25+
/// <summary>
26+
/// 环境数据上下文
27+
/// </summary>
28+
private readonly IAmbientDataContext _dataContext;
29+
30+
/// <summary>
31+
/// 初始化一个<see cref="AmbientDataContextAmbientScopeProvider{T}"/>类型的实例
32+
/// </summary>
33+
/// <param name="dataContext">环境数据上下文</param>
34+
public AmbientDataContextAmbientScopeProvider(IAmbientDataContext dataContext)
35+
{
36+
_dataContext = dataContext ?? throw new ArgumentNullException(nameof(dataContext));
37+
Logger = NullLogger<AmbientDataContextAmbientScopeProvider<T>>.Instance;
38+
}
39+
40+
/// <summary>
41+
/// 获取值
42+
/// </summary>
43+
/// <param name="contextKey">上下文键名</param>
44+
/// <returns>对象值</returns>
45+
public T GetValue(string contextKey)
46+
{
47+
var item = GetCurrentItem(contextKey);
48+
if (item is null)
49+
return default;
50+
return item.Value;
51+
}
52+
53+
/// <summary>
54+
/// 开始范围
55+
/// </summary>
56+
/// <param name="contextKey">上下文键名</param>
57+
/// <param name="value">对象值</param>
58+
public IDisposable BeginScope(string contextKey, T value)
59+
{
60+
var item = new ScopeItem(value, GetCurrentItem(contextKey));
61+
if (!ScopeDictionary.TryAdd(item.Id, item))
62+
throw new BingFrameworkException($"Can not add item! ScopeDictionary.TryAdd returns false!");
63+
_dataContext.SetData(contextKey, item.Id);
64+
65+
return new DisposeAction(() =>
66+
{
67+
ScopeDictionary.TryRemove(item.Id, out item);
68+
if (item.Outer == null)
69+
{
70+
_dataContext.SetData(contextKey, null);
71+
return;
72+
}
73+
74+
_dataContext.SetData(contextKey, item.Outer.Id);
75+
});
76+
}
77+
78+
/// <summary>
79+
/// 获取当前项
80+
/// </summary>
81+
/// <param name="contextKey">上下文键名</param>
82+
private ScopeItem GetCurrentItem(string contextKey)
83+
{
84+
return _dataContext.GetData(contextKey) is string objKey ? ScopeDictionary.GetOrDefault(objKey) : null;
85+
}
86+
87+
/// <summary>
88+
/// 范围项
89+
/// </summary>
90+
private class ScopeItem
91+
{
92+
/// <summary>
93+
/// 标识
94+
/// </summary>
95+
public string Id { get; }
96+
97+
/// <summary>
98+
/// 外部范围项
99+
/// </summary>
100+
public ScopeItem Outer { get; }
101+
102+
/// <summary>
103+
/// 值
104+
/// </summary>
105+
public T Value { get; }
106+
107+
/// <summary>
108+
/// 初始化一个<see cref="ScopeItem"/>类型的实例
109+
/// </summary>
110+
/// <param name="value">值</param>
111+
/// <param name="outer">外部范围项</param>
112+
public ScopeItem(T value, ScopeItem outer = null)
113+
{
114+
Id = Guid.NewGuid().ToString();
115+
Value = value;
116+
Outer = outer;
117+
}
118+
}
119+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System.Collections.Concurrent;
2+
using Bing.DependencyInjection;
3+
4+
namespace Bing.Threading;
5+
6+
/// <summary>
7+
/// 基于 <see cref="AsyncLocal{T}"/> 的环境数据上下文
8+
/// </summary>
9+
public class AsyncLocalAmbientDataContext : IAmbientDataContext, ISingletonDependency
10+
{
11+
/// <summary>
12+
/// 异步线程本地存储字典
13+
/// </summary>
14+
// ReSharper disable once InconsistentNaming
15+
private static readonly ConcurrentDictionary<string, AsyncLocal<object>> AsyncLocalDictionary = new();
16+
17+
/// <summary>
18+
/// 设置数据
19+
/// </summary>
20+
/// <param name="key">键名</param>
21+
/// <param name="value">对象值</param>
22+
public void SetData(string key, object value)
23+
{
24+
var asyncLocal = AsyncLocalDictionary.GetOrAdd(key, (k) => new AsyncLocal<object>());
25+
asyncLocal.Value = value;
26+
}
27+
28+
/// <summary>
29+
/// 获取数据
30+
/// </summary>
31+
/// <param name="key">键名</param>
32+
/// <returns>对象值</returns>
33+
public object GetData(string key)
34+
{
35+
var asyncLocal = AsyncLocalDictionary.GetOrAdd(key, (k) => new AsyncLocal<object>());
36+
return asyncLocal.Value;
37+
}
38+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
namespace Bing.Threading;
2+
3+
/// <summary>
4+
/// <see cref="AsyncLocal{T}"/> 扩展
5+
/// </summary>
6+
public static class AsyncLocalExtensions
7+
{
8+
/// <summary>
9+
/// 设置范围
10+
/// </summary>
11+
/// <typeparam name="T">类型</typeparam>
12+
/// <param name="asyncLocal">异步本地存储</param>
13+
/// <param name="value">值</param>
14+
public static IDisposable SetScoped<T>(this AsyncLocal<T> asyncLocal, T value)
15+
{
16+
var previousValue = asyncLocal.Value;
17+
asyncLocal.Value = value;
18+
return new DisposeAction(() =>
19+
{
20+
asyncLocal.Value = previousValue;
21+
});
22+
}
23+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
namespace Bing.Threading;
2+
3+
/// <summary>
4+
/// 取消令牌覆盖
5+
/// </summary>
6+
public class CancellationTokenOverride
7+
{
8+
/// <summary>
9+
/// 取消令牌
10+
/// </summary>
11+
public CancellationToken CancellationToken { get; }
12+
13+
/// <summary>
14+
/// 初始化一个<see cref="CancellationTokenOverride"/>类型的实例
15+
/// </summary>
16+
/// <param name="cancellationToken">取消令牌</param>
17+
public CancellationTokenOverride(CancellationToken cancellationToken)
18+
{
19+
CancellationToken = cancellationToken;
20+
}
21+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
namespace Bing.Threading;
2+
3+
/// <summary>
4+
/// 异步任务取消令牌提供程序基类
5+
/// </summary>
6+
public abstract class CancellationTokenProviderBase : ICancellationTokenProvider
7+
{
8+
/// <summary>
9+
/// 取消令牌覆盖上下文键名
10+
/// </summary>
11+
public const string CancellationTokenOverrideContextKey = "Bing.Threading.CancellationToken.Override";
12+
13+
/// <summary>
14+
/// 异步任务取消令牌
15+
/// </summary>
16+
public abstract CancellationToken Token { get; }
17+
18+
/// <summary>
19+
/// 取消令牌覆盖-环境范围提供程序
20+
/// </summary>
21+
protected IAmbientScopeProvider<CancellationTokenOverride> CancellationTokenOverrideScopeProvider { get; }
22+
23+
/// <summary>
24+
/// 取消令牌覆盖值
25+
/// </summary>
26+
protected CancellationTokenOverride OverrideValue => CancellationTokenOverrideScopeProvider.GetValue(CancellationTokenOverrideContextKey);
27+
28+
/// <summary>
29+
/// 初始化一个<see cref="CancellationTokenProviderBase"/>类型的实例
30+
/// </summary>
31+
/// <param name="cancellationTokenOverrideScopeProvider">取消令牌覆盖-环境范围提供程序</param>
32+
protected CancellationTokenProviderBase(IAmbientScopeProvider<CancellationTokenOverride> cancellationTokenOverrideScopeProvider)
33+
{
34+
CancellationTokenOverrideScopeProvider = cancellationTokenOverrideScopeProvider;
35+
}
36+
37+
/// <summary>
38+
/// 自动释放
39+
/// </summary>
40+
/// <param name="cancellationToken">取消令牌</param>
41+
public IDisposable Use(CancellationToken cancellationToken)
42+
{
43+
return CancellationTokenOverrideScopeProvider.BeginScope(CancellationTokenOverrideContextKey, new CancellationTokenOverride(cancellationToken));
44+
}
45+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
namespace Bing.Threading;
2+
3+
/// <summary>
4+
/// 环境数据上下文
5+
/// </summary>
6+
/// <remarks>
7+
/// 应用程序中共享上下文信息的模式,允许从一个地方设置和存储上下文,在整个应用程序中使用该上下文,而无需显示地将其传递给每个方法和类。
8+
/// </remarks>
9+
public interface IAmbientDataContext
10+
{
11+
/// <summary>
12+
/// 设置数据
13+
/// </summary>
14+
/// <param name="key">键名</param>
15+
/// <param name="value">对象值</param>
16+
void SetData(string key, object value);
17+
18+
/// <summary>
19+
/// 获取数据
20+
/// </summary>
21+
/// <param name="key">键名</param>
22+
/// <returns>对象值</returns>
23+
object GetData(string key);
24+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
namespace Bing.Threading;
2+
3+
/// <summary>
4+
/// 环境范围提供程序
5+
/// </summary>
6+
/// <typeparam name="T">泛型类型</typeparam>
7+
public interface IAmbientScopeProvider<T>
8+
{
9+
/// <summary>
10+
/// 获取值
11+
/// </summary>
12+
/// <param name="contextKey">上下文键名</param>
13+
/// <returns>对象值</returns>
14+
T GetValue(string contextKey);
15+
16+
/// <summary>
17+
/// 开始范围
18+
/// </summary>
19+
/// <param name="contextKey">上下文键名</param>
20+
/// <param name="value">对象值</param>
21+
IDisposable BeginScope(string contextKey, T value);
22+
}

0 commit comments

Comments
 (0)