关于“我应该如何解决这个问题”,您已经有了一个很好的答案。这里有更多关于它为什么会这样表现的描述。
AsyncLocal<T> 与logging scopes 具有相同的语义。因为它具有相同的语义,所以我总是更喜欢将它与IDisposable 一起使用,以便范围清晰明确,并且没有关于方法是否标记为async 的奇怪规则。
有关奇怪规则的详细信息,请参阅this。总结:
- 将新值写入
AsyncLocal<T> 会将该值设置为在当前范围内。
- 标记为
async 的方法将在第一次写入时将其范围复制到新范围(并且修改的是新范围)。
我已经使它类似于 IHttpContextAccessor 的工作方式,但还差得远。
我不建议抄袭IHttpContextAccessor的设计。它适用于那个非常具体的用例。如果你想使用AsyncLocal<T>,那么使用这样的设计:
static class MyImplicitValue
{
private static readonly AsyncLocal<T> Value = new();
public static T Get() => Value.Value;
public static IDisposable Set(T newValue)
{
var oldValue = Value.Value;
Value.Value = newValue;
return new Disposable(() => Value.Value = oldValue);
}
}
用法:
using (MyImplicitValue.Set(myValue))
{
// Code in here can get myValue from MyImplicitValue.Get().
}
如果需要,您可以将其包装到 IMyImplicitValueAccessor 中,但请注意,任何“设置器”逻辑都应使用 IDisposable 模式,如图所示。
AsyncLocal 实例设置在 AuthenticationHandler 中的某个点,但没有到达控制器
这是因为您的 AuthenticationHandler 设置了该值,但在设置该值后不调用控制器(它不应该)。
但是,如果我从中间件设置 AsyncLocal,它会到达控制器。
这是因为中间件会调用下一个中间件(最终到达控制器)。即,中间件的结构如下:
public async Task InvokeAsync(HttpContext context)
{
using (implicitValue.Set(myValue))
{
await _next(context);
}
}
因此,控制器在设置 AsyncLocal<T> 值时的范围内。
HttpContext如何能够一直保留Items属性内容
Items 只是一个属性包。它与AsyncLocal<T> 没有任何关系。它之所以存在是因为它是 HttpContext 上的一个属性,它之所以存在是因为在整个请求中使用了同一个 HttpContext 实例。
ASP.NET 运行时是否因为某些安全原因而将捕获的 DomainContextAccessor 的 ExecutionContext 设置在何处?
不完全是。 AsyncLocal<T> 设置得很好;只是在设置的AsyncLocal<T> 范围内没有调用控制器。