Asp.net core 异步本地<;T>;不';t到达控制器

Asp.net core 异步本地<;T>;不';t到达控制器,asp.net-core,asp.net-web-api,Asp.net Core,Asp.net Web Api,我不完全理解这种情况,其中AsyncLocal设置在中的某个点,但在将其注入到中时未到达控制器 我已经使它与工作原理相似,但仍然不太接近。但是,如果我从设置AsyncLocal,它将到达控制器。另外,设置HttpContext.Items属性也可以 问题:HttpContext如何能够始终保留Items属性内容,并且ASP.NET运行时是否出于某些安全原因处理my DomainContextAccessor捕获的ExecutionContext,因为它设置在何处 我已经做了一个演示来演示这个用例

我不完全理解这种情况,其中AsyncLocal设置在中的某个点,但在将其注入到中时未到达控制器

我已经使它与工作原理相似,但仍然不太接近。但是,如果我从设置AsyncLocal,它将到达控制器。另外,设置HttpContext.Items属性也可以

问题:HttpContext如何能够始终保留Items属性内容,并且ASP.NET运行时是否出于某些安全原因处理my DomainContextAccessor捕获的ExecutionContext,因为它设置在何处


我已经做了一个演示来演示这个用例。我真的很感激有人能告诉我这个问题。

所以必须发生的是,执行上下文的改变会抹去这个值。它在中间件中工作,因为控制器与中间件处于相同的执行上下文中

将代码更改为:

私有静态void DomainContextChangeHandler(AsyncLocalValueChangedArgs args)
{
Trace.WriteLine($“ThreadContextChanged:{args.ThreadContextChanged}”);
Trace.WriteLine($“Current:{args.CurrentValue?.GetHashCode()}”);
Trace.WriteLine($“Previous:{args.PreviousValue?.GetHashCode()}”);
Trace.WriteLine($“线程Id:{Thread.CurrentThread.ManagedThreadId}”);
}
现在您可以看到上下文何时更改

以下是您可以做的事情:

私有静态void DomainContextChangeHandler(AsyncLocalValueChangedArgs args)
{
如果(args.ThreadContextChanged&&(args.PreviousValue!=null)&&(args.CurrentValue==null))
{
Trace.WriteLine(
“****检测到具有以前值但设置为当前值的上下文更改”+
“值为空。将值重置为上一个。”);
_domainContextCurrent.Value=args.PreviousValue;
返回;
}
Trace.WriteLine($“ThreadContextChanged:{args.ThreadContextChanged}”);
Trace.WriteLine($“Current:{args.CurrentValue?.GetHashCode()}”);
Trace.WriteLine($“Previous:{args.PreviousValue?.GetHashCode()}”);
Trace.WriteLine($“线程Id:{Thread.CurrentThread.ManagedThreadId}”);
}
但是,这有点违背了使用
asynchlocal
作为备份存储的目的

我的建议是删除
AsyncLocal
,使用普通的类作用域存储:

名称空间WebApp.Models
{
公共接口IDomainContextAccessor
{
DomainContext DomainContext{get;set;}
}
公共密封类DomainContextAccessor:IDomainContextAccessor
{
公共域上下文域上下文{get;set;}
}
}
并将其作为作用域而不是单例注入:

services.AddScoped<IDomainContextAccessor, DomainContextAccessor>();
services.addScope();
它会做你想要的事情,而不会有任何混乱——而且,未来你(或开发人员)将完全理解正在发生的事情以及为什么会是这样


没有中间件,没有
AsyncLocal
有趣的业务。它只是起作用。

所以必须发生的是执行上下文的更改会清除该值。它在中间件中工作,因为控制器与中间件处于相同的执行上下文中

将代码更改为:

私有静态void DomainContextChangeHandler(AsyncLocalValueChangedArgs args)
{
Trace.WriteLine($“ThreadContextChanged:{args.ThreadContextChanged}”);
Trace.WriteLine($“Current:{args.CurrentValue?.GetHashCode()}”);
Trace.WriteLine($“Previous:{args.PreviousValue?.GetHashCode()}”);
Trace.WriteLine($“线程Id:{Thread.CurrentThread.ManagedThreadId}”);
}
现在您可以看到上下文何时更改

以下是您可以做的事情:

私有静态void DomainContextChangeHandler(AsyncLocalValueChangedArgs args)
{
如果(args.ThreadContextChanged&&(args.PreviousValue!=null)&&(args.CurrentValue==null))
{
Trace.WriteLine(
“****检测到具有以前值但设置为当前值的上下文更改”+
“值为空。将值重置为上一个。”);
_domainContextCurrent.Value=args.PreviousValue;
返回;
}
Trace.WriteLine($“ThreadContextChanged:{args.ThreadContextChanged}”);
Trace.WriteLine($“Current:{args.CurrentValue?.GetHashCode()}”);
Trace.WriteLine($“Previous:{args.PreviousValue?.GetHashCode()}”);
Trace.WriteLine($“线程Id:{Thread.CurrentThread.ManagedThreadId}”);
}
但是,这有点违背了使用
asynchlocal
作为备份存储的目的

我的建议是删除
AsyncLocal
,使用普通的类作用域存储:

名称空间WebApp.Models
{
公共接口IDomainContextAccessor
{
DomainContext DomainContext{get;set;}
}
公共密封类DomainContextAccessor:IDomainContextAccessor
{
公共域上下文域上下文{get;set;}
}
}
并将其作为作用域而不是单例注入:

services.AddScoped<IDomainContextAccessor, DomainContextAccessor>();
services.addScope();
它会做你想要的事情,而不会有任何混乱——而且,未来你(或开发人员)将完全理解正在发生的事情以及为什么会是这样


没有中间件,没有
AsyncLocal
有趣的业务。它只是起作用。

关于“我应该如何解决这个问题?”您已经有了一个很好的答案,下面是对它为什么会出现这种情况的更多描述

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

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

  • 将新值写入
    AsyncLocal
    将在当前范围内设置该值
  • 标记为
    async
    的方法将在第一个ti时将其作用域复制到新作用域
    using (MyImplicitValue.Set(myValue))
    {
      // Code in here can get myValue from MyImplicitValue.Get().
    }
    
    public async Task InvokeAsync(HttpContext context)
    {
      using (implicitValue.Set(myValue))
      {
        await _next(context);
      }
    }