【发布时间】:2018-11-30 18:07:08
【问题描述】:
在控制器范围内解析的元素上调用Dispose 之前,似乎不会保留执行上下文。这可能是因为 asp.net 核心必须在本机代码和托管代码之间跳转,并在每次跳转时重置执行上下文。似乎在释放范围之前不再恢复正确的上下文。
下面演示了这个问题 - 只需将它放在默认的 asp.net 核心示例项目中,并将 TestRepo 注册为临时依赖项。
当调用GET api/values/ 时,我们在调用开始时在静态 AsyncLocal 中将当前任务的值设置为 5。该值按预期通过 await 流动,没有任何问题。但是,当控制器及其依赖项在调用后被释放时,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 :(
}
}
}
这是故意的吗?如果是,是否有解决此问题的方法?
【问题讨论】:
-
在瞬态范围的注入上绑定一个静态使我的大脑受伤。你到底想做什么?对于 Web 应用程序,静态几乎总是错误的方法,尤其是现在在 ASP.NET Core 的世界中,everything 现在都是依赖注入的。
-
我不同意@ChrisPratt 对此的看法。使用静态
AsyncLocal<T>没有任何问题,只要将这个值封装在内部即Composition Root中。 -
@Steven 是的 AsyncLocal 只是一个非常特殊的类,其中天真的“哦,永远不要使用静态!”根本没有意义。它的全部意义在于存储全局状态 - 所以确保您可以避免将其设为静态,而是将类设为单例,但这只不过是装点门面而已。地狱的 asp.net 核心人员同意,因为他们以完全相同的方式使用该类。反对票通过复杂的问题证明了问题——人们没有花时间去理解问题,或者根本没有经验来理解问题所在。..
-
您看到的行为是 ASP.NET Core 工作方式中的一个不幸的怪癖(Web API 具有相同的行为)。处理是在请求结束时完成的,由于某种原因,异步上下文在该点之前已经被清除。
-
@Steven Yes 很害怕。我没有想到您想到的解决方法吗?
标签: c# .net asp.net-core dependency-injection .net-core