Awaiting something in TransactionScope
I like asynchronous programming. The Asynchronous Programming Model and later Tasks with ContinueWith offer great performance especially if no waiting and similar is used.
Although the async/await makes this very simple for 99% of cases, there’s always 1% where you might hit the wall. With callbacks it was little bit obvious, because you wrote the code. Now the compiler is doing the hard work.
If you do something like:
lock (SyncRoot)
{
await FooBar.DoAsync();
}
You’ll get nice error from compiler saying The 'await' operator cannot be used in the body of a lock statement
. And it really makes sense. The lock will very likely provide wrong results under default rewriting work the compiler is doing (and doing it properly for Monitor class needs a lot of knowledge of what you’re trying to achieve. What’s not so clear is that with TransactionScope block (or similar construct) you’re basically doing same stuff, just probably somewhere else in database.
So the compiler is completely OK with:
using (TransactionScope ts = new TransactionScope())
{
await FooBar.DoAsync();
}
But that’s not what you might had in mind. Consider code like:
static void Main(string[] args)
{
Test();
}
static async void Test()
{
Task t = FooAsync();
Console.WriteLine("Other stuff");
await t;
}
static async Task FooAsync()
{
using (Test t = new Test())
{
Console.WriteLine("Before");
await Task.Yield();
Console.WriteLine("After");
}
}
class Test : IDisposable
{
public void Dispose()
{
Console.WriteLine("Dispose");
}
}
The result is correct (and expected, if you look at it closely):
Before
Other stuff
After
Dispose
But considering, that the Other stuff might have some dependency in data being done in that transaction you might get wrong result.
So it’s not always wrong, nor some gotcha in compiler. But think it through before using this construct.