For some weird reason, both the System.Runtime.Remoting.Messaging.CallContext and AsyncLocal classes are only available using the full CLR. This makes it really hard to do asynchronous scoping while working with Portable Class Libraries or Windows Phone applications, especially since Windows Phone APIs are becoming async-only; so we don't really have an option in not using async/await.
What this practically means is that in WPF or WinForms, we can write methods like this:
private async void button_Click(object sender, EventArgs e)
{
CallContext.LogicalSetData("x", new object());
await Something();
var context = CallContext.LogicalGetData("x");
}
In WPF and WinForms, the framework ensures that each click to the same button get their own context and with that can run in isolation. It's hard to achieve the same using ThreadLocal<T>
and [ThreadStatic]
since every click will get executed on the UI thread.
My question is, how would we solve this problem in Windows Phone, Store and other application types that don't support CallContext
and AsyncLocal<T>
?
Some background information:
Very often we want (business) logic to run in some sort of context. This context can contain information that the business logic can use throughout the operation. In a server environment this is really easy to imagine, because you need to run requests in a database transaction, need to access the current user, tenant id, etc. But even in a client application, operations might need to access contextual information such as a correlation id for the current running operation or context for logging. During such operation (like an click event) we might need to resolve additional services (from our Composition Root). For the operation to work successfully, we might need to reuse the same component throughout the entire client operation and that means that our Composition Root needs to be aware of the context it is running.
Although all this information can be passed on through the entire system using method calls of the services' public API, this would not only force us to pollute the API of the services in our application with implementation details, it would lead to severe maintenance issues, because we would have to pass through all this information throughout the system, and a simple internal change to one of our components would propagate up the call stack through all the methods calls. And when it comes to our Composition Root, we definitely don't want to pass though some cache/scope object of our DI library through the application, because that would tightly couple our business logic to an external library. Obviously, neither do we want to pass on some sort of service locator.
So implementing scoping using something like CallContext
or AsyncLocal<T>
is very important in client applications.
There is no easy solution for this today, sorry. When Windows Phone (eventually) becomes "Windows 10 on a phone" (i.e., with AsyncLocal<T>
), then this will be possible. But for now...
The easiest way to do this is to pass the context, either explicitly (as a parameter) or implicitly (as a member variable on this
).
It may also be possible to achieve a limited version of this with custom awaiters. But in addition to being horribly complex, that solution would require you to modify every await
in your entire app (or use a post-compilation IL rewriting to achieve the same effect).
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