How to dispose TransactionScope in cancelable async/await?
In .NET Framework 4.5.1, there is a set of new constructors for TransactionScope that take a TransactionScopeAsyncFlowOption parameter.
According to the MSDN, it enables transaction flow across thread continuations.
My understanding is that it is meant to allow you to write code like this:
// transaction scope
using (var scope = new TransactionScope(... ,
TransactionScopeAsyncFlowOption.Enabled))
{
// connection
using (var connection = new SqlConnection(_connectionString))
{
// open connection asynchronously
await connection.OpenAsync();
using (var command = connection.CreateCommand())
{
command.CommandText = ...;
// run command asynchronously
using (var dataReader = await command.ExecuteReaderAsync())
{
while (dataReader.Read())
{
...
}
}
}
}
scope.Complete();
}
I have not tried it yet, so I don't know if it will work.
I know this is an old thread, but if anyone has run into the problem System.InvalidOperationException : A TransactionScope must be disposed on the same thread that it was created.
The solution is to upgrade to .net 4.5.1 at a minimum and use a transaction like the following:
using (var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
//Run some code here, like calling an async method
await someAsnycMethod();
transaction.Complete();
}
Now the transaction is shared between methods. Take a look at the link below. It provide a simple example and more detail
For complete details, take a look at This
The problem stems from the fact that I was prototyping the code in a console application, which I did not reflect in the question.
The way async/await continues to execute the code after await
is dependent on the presence of SynchronizationContext.Current
, and console application don't have one by default, which means the continuation gets executed using the current TaskScheduler
, which is a ThreadPool
, so it (potentially?) executes on a different thread.
Thus one simply needs to have a SynchronizationContext
that will ensure TransactionScope
is disposed on the same thread it was created. WinForms and WPF applications will have it by default, while console applications can either use a custom one, or borrow DispatcherSynchronizationContext
from WPF.
Here are two great blog posts that explain the mechanics in detail:
Await, SynchronizationContext, and Console Apps
Await, SynchronizationContext, and Console Apps: Part 2