How to dispose TransactionScope in cancelable async/await? How to dispose TransactionScope in cancelable async/await? database database

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 scopeusing (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