AsyncLocal<T> 无法到达控制器

Ost*_*ati 3 asp.net-web-api asp.net-core

我不太完全理解这种情况,其中 AsyncLocal实例是在AuthenticationHandler中的某个点设置的,但当它被注入到constructor中时却没有到达控制器。

我已经使它类似于IHttpContextAccessor 的工作方式,但仍然相距甚远。但是,如果我从Middleware设置 AsyncLocal ,它就会到达控制器。另外,从AuthenticationHandler设置 HttpContext.Items 属性也可以正常工作。

问题: HttpContext 如何能够一直保留 Items 属性内容,并且 ASP.NET 运行时是否出于某种安全原因处理我的 DomainContextAccessor 捕获的 ExecutionContext(因为它的设置位置)?

我制作了一个示例应用程序来演示此用例。我真的很感激有人能阐明这个问题。

Ste*_*ary 6

对于“我应该如何解决这个问题?”您已经有了一个很好的答案。这里有更多关于它为什么会这样的描述。

AsyncLocal<T>与日志记录范围具有相同的语义。因为它具有相同的语义,所以我总是更喜欢将它与 an 一起使用IDisposable,以便范围清晰明确,并且对于是否标记方法没有奇怪的规则async

有关奇怪规则的具体信息,请参阅。总之:

  • 将新值写入到当前范围内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);
  }
}
Run Code Online (Sandbox Code Playgroud)

用法:

using (MyImplicitValue.Set(myValue))
{
  // Code in here can get myValue from MyImplicitValue.Get().
}
Run Code Online (Sandbox Code Playgroud)

如果需要,您可以将其包装到 a 中IMyImplicitValueAccessor,但请注意,任何“setter”逻辑都应使用IDisposable所示的模式。

AsyncLocal 实例在 AuthenticationHandler 中的某个点设置,但未到达控制器

这是因为您的 AuthenticationHandler 设置了该值,但在设置该值后不会调用控制器(也不应该)。

但是,如果我从中间件设置 AsyncLocal,它就会到达控制器。

这是因为中间件会调用下一个中间件(最终到达控制器)。即,中间件的结构如下:

public async Task InvokeAsync(HttpContext context)
{
  using (implicitValue.Set(myValue))
  {
    await _next(context);
  }
}
Run Code Online (Sandbox Code Playgroud)

因此控制器处于AsyncLocal<T>设置该值时的范围内。

HttpContext如何能够一直保留Items属性内容

Items只是一个财产袋。和它没有任何关系AsyncLocal<T>。它存在是因为它是 上的一个属性,并且它持续存在是因为在整个请求过程中使用了HttpContext同一个实例。HttpContext

ASP.NET 运行时是否出于某种安全原因处理我的 DomainContextAccessor 捕获的 ExecutionContext(因为它的设置位置)?

不完全是。正在AsyncLocal<T>设置得很好;只是控制器不会在所AsyncLocal<T>设置的范围内被调用。