Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to wrap IDbTransactions in a TransactionScope

I have several code methods that look like this:

using (var connection = this.connectionFactory.GetConnection())
{
    connection.Open();
    using (var transaction = connection.BeginTransaction())
    {
        using (var command = connection.CreateCommand())
        {
            command.Transaction = transaction;
            command.CommandText = "foo";
            command.ExecuteNonQuery();
            transaction.Commit();
        }
    }
}

I now need to call several of these methods together inside an outer transaction, So I did this:

using (var transactionScope = new TransactionScope(TransactionScopeOption.RequiresNew))
{
    method1();
    method2();
    method3();
}

but its doing:

The operation is not valid for the state of the transaction.
   at System.Transactions.TransactionState.EnlistPromotableSinglePhase(InternalTransaction tx, IPromotableSinglePhaseNotification promotableSinglePhaseNotification, Transaction atomicTransaction)
   at System.Transactions.Transaction.EnlistPromotableSinglePhase(IPromotableSinglePhaseNotification promotableSinglePhaseNotification)
   at System.Data.SqlClient.SqlInternalConnection.EnlistNonNull(Transaction tx)
   at System.Data.SqlClient.SqlInternalConnection.Enlist(Transaction tx)
   at System.Data.SqlClient.SqlInternalConnectionTds.Activate(Transaction transaction)
   at System.Data.ProviderBase.DbConnectionInternal.ActivateConnection(Transaction transaction)
   at System.Data.ProviderBase.DbConnectionPool.GetConnection(DbConnection owningObject)
   at System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection)
   at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory)
   at System.Data.SqlClient.SqlConnection.Open()

Do I need to replace the IDbTransactions with TransactionScopes?

What TransactionScopeOption should I use for the outer an inner scopes? Im guessing i want RequiresNew for the outer and Required for the inners?

The methods will still be called individually (i.e. without an outer TransactionScope as well as together, so I still need them to be transactionally safe.

Thanks

like image 986
Andrew Bullock Avatar asked Jun 08 '11 10:06

Andrew Bullock


1 Answers

I believe you are mixing technologies here and should avoid using TransactionScope and DbTransaction together because TransactionScope creates an implicit transaction.

So I would recommend to have your methods similar to:

using (var connection = this.connectionFactory.GetConnection())
{
    connection.Open();
    using (TransactionScope scope = new TransactionScope())
    {
        using (var command = connection.CreateCommand())
        {
            command.CommandText = "foo";
            command.ExecuteNonQuery();
        }
        scope.Complete();
    }
}

Then you can call them together:

using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew))
{
    method1();
    method2();
    method3();

    scope.Complete();
}

and the methods you called will share the same transaction.

like image 108
Regent Avatar answered Sep 30 '22 21:09

Regent