I use a similar pattern as HttpContextAccessor
The simplified version is as follows, Console.WriteLine(SimpleStringHolder.StringValue)
is not supposed to be null.
public class SimpleStringHolder
{
private static readonly AsyncLocal<ValueHolder> CurrentHolder = new AsyncLocal<ValueHolder>();
public static string StringValue
{
get => CurrentHolder.Value?.StringValue;
set
{
var holder = CurrentHolder.Value;
if (holder != null)
{
holder.StringValue = null;
}
if (value != null)
{
CurrentHolder.Value = new ValueHolder() { StringValue = value };
}
}
}
private class ValueHolder
{
public string StringValue;
}
}
class Program
{
private static readonly AsyncLocal<string> currentValue = new AsyncLocal<string>();
public static void Main(string[] args)
{
var task = Task.Run(async () => await OutterAsync());
task.Wait();
}
public static async Task OutterAsync()
{
SimpleStringHolder.StringValue = "1";
await InnerAsync();
Console.WriteLine(SimpleStringHolder.StringValue); //##### the value is gone ######
}
public static async Task InnerAsync()
{
var lastValue = SimpleStringHolder.StringValue;
await Task.Delay(1).ConfigureAwait(false);
SimpleStringHolder.StringValue = lastValue; // comment this line will make it work
Console.WriteLine(SimpleStringHolder.StringValue); //the value is still here
}
}
In the above code, OutterAsync
invokes an async method InnerAsync
, in InnerAsync
the StringValue
is set, which makes AsyncLocal loses its context in OutterAsync
Console.WriteLine(SimpleStringHolder.StringValue);
is null.
I think the magic is in the SimpleStringHolder's property set, removing the following code will make things right.
if (holder != null)
{
holder.StringValue = null;
}
The above code works as expected.
Please help me understand what sorcery is this?