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