繁体   English   中英

AsyncLocal 与 ASP.NET Core Controller/ServiceProviderScope

[英]AsyncLocal with ASP.NET Core Controller/ServiceProviderScope

似乎在控制器作用域中解析的元素上调用Dispose之前,不会保留执行上下文。 这可能是因为 asp.net core 必须在本机代码和托管代码之间跳转,并在每次跳转时重置执行上下文。 在处理范围之前,似乎不再恢复正确的上下文。

下面演示了这个问题 - 只需将它放在默认的 asp.net 核心示例项目中,并将TestRepo注册为瞬态依赖项。

当调用GET api/values/我们在调用开始时在静态 AsyncLocal 中将当前任务的值设置为 5。 该值按预期流过等待,没有任何问题。 但是当控制器及其依赖项在调用后被释放时,AsyncLocal 上下文已经被重置。

[Route("api/[controller]")]
public class ValuesController : Controller
{
    private readonly TestRepo _testRepo;

    public ValuesController(TestRepo testRepo) => _testRepo = testRepo;

    [HttpGet()]
    public async Task<IActionResult> Get()
    {
        _testRepo.SetValue(5);
        await Task.Delay(100);
        var val = _testRepo.GetValue(); // val here has correctly 5.
        return Ok();
    }
}

public class TestRepo : IDisposable
{
    private static readonly AsyncLocal<int?> _asyncLocal = new AsyncLocal<int?>();

    public int? GetValue() => _asyncLocal.Value;

    public void SetValue(int x) => _asyncLocal.Value = x;

    public void Foo() => SetValue(5);

    public void Dispose()
    {
        if (GetValue() == null)
        {
            throw new InvalidOperationException(); //GetValue() should be 5 here :(
        }
    }
}

这是故意的吗? 如果是,是否有解决此问题的任何解决方法?

您看到的行为是 ASP.NET Core 工作方式的一个不幸的怪癖。 我不清楚为什么微软选择这种行为,但它似乎是从 Web API 的工作方式中复制的,它具有确切的行为。 处理显然是在请求结束时完成的,但由于某种原因,异步上下文在此之前已经被清除,因此无法在单个异步上下文中运行完整的请求。

你基本上有两个选择:

  1. 不是使用环境状态来共享状态,而是通过对象图流动状态而不是使用环境状态。 换句话说,使TestRepo Scoped,并将value存储在私有字段中。
  2. 将使用该值的操作移至请求中的较早阶段。 例如,您可以定义一些中间件来包装请求并在最后调用该操作。 在那个阶段,异步上下文仍然存在。

一些 DI 容器实际上应用了第二种技术。 例如,Simple Injector 使用基于环境状态的范围界定,在AsyncLocal<T>使用AsyncLocal<T> 当集成到 ASP.NET Core 中时,它会将请求包装在一个应用此范围的中间件中。 这意味着从 Simple Injector 解析的任何 Scoped 组件都将在 ASP.NET Core 管道处理其服务之前被处理,并且在异步上下文仍然可用时发生这种情况。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM