In my Asp.Net WebApi controller (framework version 4.6.1) I have the following code:
[Route("async_test_2")]
public async Task<IHttpActionResult> AsyncTest2()
{
TelemetryDebugWriter.IsTracingDisabled = true;
var aspNetContext = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext()); //set context while calling AsyncMethod
var task = AsyncMethod();
SynchronizationContext.SetSynchronizationContext(aspNetContext); //Restore AspNet context before awaiting
DebugContext("Before outer await");
await Task.WhenAll(new Task[] { task });
DebugContext("After outer await");
return Ok();
}
private async Task AsyncMethod()
{
DebugContext("Before inner await");
await Task.Delay(2000);
DebugContext("After inner await");
}
private void DebugContext(string location)
{
System.Diagnostics.Debug.WriteLine(location + " --- SyncContext: " + (SynchronizationContext.Current?.ToString() ?? "null") + "; ManagedThreadId: " + Thread.CurrentThread.ManagedThreadId);
}
The debug output is:
Before inner await --- SyncContext: System.Threading.SynchronizationContext; ManagedThreadId: 6
Before outer await --- SyncContext: System.Web.AspNetSynchronizationContext; ManagedThreadId: 6
After inner await --- SyncContext: null; ManagedThreadId: 5
After outer await --- SyncContext: System.Web.AspNetSynchronizationContext; ManagedThreadId: 6
Why would the continuation 'After inner await' have a null SynchronizationContext? If I simply remove the call to SetSynchronizationContext and the call to restore it (i.e. don't modify the context, leaving the default AspNetSynchronizationContext), then the context is not null in any debug output.
Before inner await --- SyncContext: System.Web.AspNetSynchronizationContext; ManagedThreadId: 7
Before outer await --- SyncContext: System.Web.AspNetSynchronizationContext; ManagedThreadId: 7
After inner await --- SyncContext: System.Web.AspNetSynchronizationContext; ManagedThreadId: 8
After outer await --- SyncContext: System.Web.AspNetSynchronizationContext; ManagedThreadId: 8
Adding 'ConfigureAwait(false)' after the inner await will cause the context to be null in the continuation as expected.
Before inner await --- SyncContext: System.Web.AspNetSynchronizationContext; ManagedThreadId: 7
Before outer await --- SyncContext: System.Web.AspNetSynchronizationContext; ManagedThreadId: 7
After inner await --- SyncContext: null; ManagedThreadId: 7
After outer await --- SyncContext: System.Web.AspNetSynchronizationContext; ManagedThreadId: 8
So it works as expected when the AspNetSynchronizationContext is active, but not when a default SynchronizationContext is active. In that case, it's always null in the continuation regardless of whether ConfigureAwait(false) is called.