-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Description
It seems like AsyncLocal
does not keep its state until the scope created for executing a controller is finished disposed. This means that a value set in AsyncLocal
during a controller call is not available when the injected dependencies are disposed - instead the value is always null.
Not sure if this is intended or not, but it'd be nice to document this behavior somewhere and maybe provide a work around. Using AsyncLocal
to store some information that's required during a call is not unheard of and if you store resources that have to be cleaned up in it, this is rather cumbersome currently.
Example. Put the following into the default asp.net core Web API project template for netcoreapp2.1 and register TestRepo as a transient dependency. When executing GET api/values
we get an exception during TestRepo::Dispose
because the asynclocal value is null instead of 5.
[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()
{
return _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 :(");
}
}
}
Current behavior: Dispose throws a InvalidOperationException.
Expected behavior: GetValue()
returns 5 inside TestRepo::Dispose