[英]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 的工作方式中复制的,它具有确切的行为。 处理显然是在请求结束时完成的,但由于某种原因,异步上下文在此之前已经被清除,因此无法在单个异步上下文中运行完整的请求。
你基本上有两个选择:
TestRepo
Scoped,并将value
存储在私有字段中。 一些 DI 容器实际上应用了第二种技术。 例如,Simple Injector 使用基于环境状态的范围界定,在AsyncLocal<T>
使用AsyncLocal<T>
。 当集成到 ASP.NET Core 中时,它会将请求包装在一个应用此范围的中间件中。 这意味着从 Simple Injector 解析的任何 Scoped 组件都将在 ASP.NET Core 管道处理其服务之前被处理,并且在异步上下文仍然可用时发生这种情况。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.