Why does AsyncLocal<T> return different results when code is refactored slightly?
Follow this link AsyncLocal Class on MSDN
AsyncLocal<T>
represents ambient data that is local to a given asynchronous control flow, such as an asynchronous method
It means that your code uses different values when it's accesses from another async
method such as WrapperAsync
and your main thread contains another value
[UPDATE]
Not obvious thing to understand, but here is explanation. Control Flow in Async Programs. This is how your thread is changed when you do not expect this.
This is how Control Flow working with async
public class Program
{
private static readonly AsyncLocal<string> AsyncLocalContext = new AsyncLocal<string>();
public static void Main(string[] args)
{
AsyncLocalContext.Value = "No surprise";
WrapperAsync("surprise!");
Console.WriteLine("Main: " + AsyncLocalContext.Value);
}
private static async void WrapperAsync(string text)
{
Console.WriteLine("WrapperAsync before: " + AsyncLocalContext.Value);
AsyncLocalContext.Value = text;
Console.WriteLine("WrapperAsync after: " + AsyncLocalContext.Value);
}
}
Output is:
WrapperAsync before: No surprise
WrapperAsync after: surprise!
Main: No surprise
[/UPDATE]
AsyncLocal<T>
is ambient data stored on the ExecutionContext
of the current thread. ExecutionContext
is flowed across threads automagically in async/await call chains (see Stephen Toub's blog for details). When the app starts, the default ExecutionContext
is used, but once data is stored via AsyncLocal<T>.Value
, a new ExecutionContext
is created for the current async call chain (see here) and the ambient data is added to it. This new context is propagated to downstream calls.
Stephen Cleary discusses this behavior here (scroll down to the AsyncLocal section) and makes the point:
[AsyncLocal] provides a way for contextual information to flow “down” asynchronous calls. Note that the value does not flow “up”.
This is why AsyncLocal<T>
updates down the call chain are not reflected in upstream methods.