I have a UserScope
class which functions similarly to TransactionScope
, i.e., it stores the current state in a thread local. This of course doesn't work across calls to await
, and neither did TransactionScope
until TransactionScopeAsyncFlowOption
was added in .NET 4.5.1.
What alternative to thread local can I use so that UserScope
can be used the same in single-threaded and multi-threaded scenarios? (If I had 4.5.1 installed I'd decompile to see how TransactionScope
does it.) This is a simplified version of what I have:
class User {
readonly string name;
public User(string name) {
this.name = name;
}
public string Name {
get { return this.name; }
}
}
class UserScope : IDisposable {
readonly User user;
[ThreadStatic]
static UserScope currentScope;
public UserScope(User user) {
this.user = user;
currentScope = this;
}
public static User User {
get { return currentScope != null ? currentScope.user : null; }
}
public void Dispose() {
///...
}
}
And this is a test I'd expect to work:
static async Task Test() {
var user = new User("Thread Flintstone");
using (new UserScope(user)) {
await Task.Run(delegate {
Console.WriteLine("Crashing with NRE...");
Console.WriteLine("The current user is: {0}", UserScope.User.Name);
});
}
}
static void Main(string[] args) {
Test().Wait();
Console.ReadLine();
}
In .NET 4.5 full framework, you can use the logical call context for this:
static async Task Test()
{
CallContext.LogicalSetData("Name", "Thread Flintstone");
await Task.Run(delegate
{
//Console.WriteLine("Crashing with NRE...");
Console.WriteLine("The current user is: {0}", CallContext.LogicalGetData("Name"));
});
}
static void Main(string[] args)
{
Test().Wait();
Console.ReadLine();
}
However, you should only store immutable data in the logical call context. I have more details on my blog. I've been meaning to wrap this up into an AsyncLocal<T>
library, but haven't (yet) found the time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With