Reputation: 3304
Consider the following code that sets a ClaimsPrincipal on a single, main thread of execution and then runs a task and tries to access the ClaimsPrincipal:
public class Program
{
public static void Main(string[] args)
{
//Setting the CurrentPrincipal on the main thread
Thread.CurrentPrincipal = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim("name", "bob"), new Claim("role", "admin") }, "CUSTOM", "name", "role"));
Console.WriteLine("Thread.CurrentThread.ManagedThreadId " + Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("Thread.CurrentPrincipal?.Identity?.Name " + (Thread.CurrentPrincipal?.Identity?.Name ?? "null"));
AsyncHelper.RunSync(
async () =>
{
Console.WriteLine("\tInside Async Method - Thread.CurrentThread.ManagedThreadId " + Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("\tInside Async Method - Thread.CurrentPrincipal?.Identity?.Name " + (Thread.CurrentPrincipal?.Identity?.Name ?? "null"));
//Simulate long(er) running work
await Task.Delay(2000);
Console.WriteLine("\tInside Async Method - Thread.CurrentThread.ManagedThreadId " + Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("\tInside Async Method - Thread.CurrentPrincipal?.Identity?.Name " + (Thread.CurrentPrincipal?.Identity?.Name ?? "null"));
});
Console.WriteLine("Thread.CurrentThread.ManagedThreadId " + Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("Thread.CurrentPrincipal?.Identity?.Name " + (Thread.CurrentPrincipal?.Identity?.Name ?? "null"));
}
}
and
internal static class AsyncHelper
{
private static readonly TaskFactory MyTaskFactory = new
TaskFactory(CancellationToken.None,
TaskCreationOptions.None,
TaskContinuationOptions.None,
TaskScheduler.Default);
public static void RunSync(Func<Task> func)
{
AsyncHelper.MyTaskFactory
.StartNew<Task>(func)
.Unwrap()
.GetAwaiter()
.GetResult();
}
}
The output of the program is
Thread.CurrentThread.ManagedThreadId 2
Thread.CurrentPrincipal?.Identity?.Name bob
Inside Async Method - Thread.CurrentThread.ManagedThreadId 3
Inside Async Method - Thread.CurrentPrincipal?.Identity?.Name null
Inside Async Method - Thread.CurrentThread.ManagedThreadId 3
Inside Async Method - Thread.CurrentPrincipal?.Identity?.Name null
Thread.CurrentThread.ManagedThreadId 2
Thread.CurrentPrincipal?.Identity?.Name bob
Press any key to exit
What I thought I would see is this:
Thread.CurrentThread.ManagedThreadId 2
Thread.CurrentPrincipal?.Identity?.Name bob
Inside Async Method - Thread.CurrentThread.ManagedThreadId 3
Inside Async Method - Thread.CurrentPrincipal?.Identity?.Name bob <--- Notice here
Inside Async Method - Thread.CurrentThread.ManagedThreadId 3
Inside Async Method - Thread.CurrentPrincipal?.Identity?.Name bob <--- Notice here
Thread.CurrentThread.ManagedThreadId 2
Thread.CurrentPrincipal?.Identity?.Name bob
Press any key to exit
What happened to the ClaimsPrincipal
that was set on the main thread (in the case of this specific output ManagedThreadId 2)? Why did the ClaimsPrincipal not get copied to the other thread when the ExecutionContext was copied?
Update: The .Net target framework is .NET Core 2.0.
Update 2: This issue seems to be specific to .NET Core. Using the same code but instead targeting the .NET 4.6.1 framework I get the output I expect.
Upvotes: 1
Views: 1113
Reputation: 9776
You just can't rely on thread properties, since such details as threads assignment to the task depends on concrete ThreadScheduler ( https://msdn.microsoft.com/en-us/library/system.threading.tasks.taskscheduler(v=vs.110).aspx ) . There are many different ThreadScheduler implementations.
There is an option to implement your own custom TaskScheduler where you will set Thread.CurrentPrincipal to your values, but better just do not rely on all threading properties at all staying on "task level".
Upvotes: 1