I have a method similar to:
public async Task SaveItemsAsync(IEnumerable<MyItem> items)
{
using (var ts = new TransactionScope())
{
foreach (var item in items)
{
await _repository.SaveItemAsync(item);
}
await _repository.DoSomethingElse();
ts.Complete();
}
}
This of course has issues because TransactionScope
doesn't play nice with async/await.
It fails with an InvalidOperationException
with the message:
"A TransactionScope must be disposed on the same thread that it was created."
I read about TransactionScopeAsyncFlowOption
in this answer, which appears to be exactly what I need.
However, for this particular project, I have a hard requirement to support .Net 4.0 and cannot upgrade to 4.5 or 4.5.1. Thus the async/await behavior in my project is provided by the Microsoft.Bcl.Async NuGet Package.
I can't seem to find TransactionScopeAsyncFlowOption
in this or any other OOB package. Am I just missing it somewhere?
If it is not available, is there an alternative for achieving the same result? That is - I would like the transaction scope to properly complete or rollback, despite crossing threads with continuations.
I added DoSomethingElse
in the example above to illustrate that there may be multiple calls to make within the transaction scope, so simply passing all items to the database in one call is not a viable option.
In case it matters, the repository uses direct ADO.Net (SqlConnection
, SqlCommand
, etc) to write to a SQL Server.
UPDATE 1
I thought I had a solution which involved taking System.Transactions.dll from .Net 4.5.1 and including it in my project. However, I found that this worked only on my dev box because it already had 4.5.1 installed. It did not work when deploying to a machine with only .Net 4.0. It just gave a MissingMethodException
. I'm looking for a solution that will work on a .Net 4.0 installation.
UPDATE 2
I originally asked this question in July 2014. .NET Framework 4.0, 4.5, and 4.5.1 reached end of life in January 2016. The question thus is no longer applicable and is here only for historical reference.
It is not possible to achieve this in .NET Framework 4.0. Additionally, .NET Framework 4.0 reached end of life on 2016-01-12, and thus is no longer relevant.
To support transaction scope in async methods in .NET going forward (since .NET Framework 4.5.1), use TransactionScopeAsyncFlowOption.Enabled
public static TransactionScope CreateAsyncTransactionScope(IsolationLevel isolationLevel = IsolationLevel.ReadCommitted)
{
var transactionOptions = new TransactionOptions
{
IsolationLevel = isolationLevel,
Timeout = TransactionManager.MaximumTimeout
};
return new TransactionScope(TransactionScopeOption.Required, transactionOptions, TransactionScopeAsyncFlowOption.Enabled);
}