Как обернуть IDbTransactions в TransactionScope - PullRequest
4 голосов
/ 08 июня 2011

У меня есть несколько методов кода, которые выглядят так:

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();
        }
    }
}

Теперь мне нужно вызвать несколько из этих методов вместе во внешней транзакции, поэтому я сделал это:

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

но делает:

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

Нужно ли заменить IDbTransactions на TransactionScopes?

Что TransactionScopeOption я должен использовать для внешних и внутренних областей? Я предполагаю, что я хочу RequiresNew для внешнего и Required для внутренних?

Методы по-прежнему будут вызываться по отдельности (т. Е. Без внешнего TransactionScope, а также вместе, так что мне все еще нужно, чтобы они были транзакционно безопасными.

Спасибо

Ответы [ 3 ]

6 голосов
/ 08 июня 2011

Я полагаю, что вы смешиваете технологии здесь, и вам следует избегать совместного использования TransactionScope и DbTransaction, потому что TransactionScope создает неявную транзакцию.

Поэтому я бы рекомендовал, чтобы ваши методы были похожи на:

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();
    }
}

Затем вы можете вызывать их вместе:

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

    scope.Complete();
}

, и вызванные вами методы будут использовать одну и ту же транзакцию.

0 голосов
/ 08 июня 2011

Я не уверен, что скажу, но прочитав это: http://msdn.microsoft.com/en-us/library/ms172152(v=vs.80).aspx#sectionSection3

Я думаю, вы должны использовать TransactionScope в вашем методе sub, но у нас есть опция Required. Поэтому, когда они вызываются по отдельности, у вас все еще есть транзакция, а когда вы вызываете их в верхнем методе, транзакция будет интегрирована в внешнюю транзакцию.

0 голосов
/ 08 июня 2011

Полагаю, вы все делаете правильно.Внутренняя транзакция регистрируется в той же области видимости, что и внешняя, и все будет откатываться.Чтобы иметь новую область транзакций, вы должны указать «requirenew».http://web.archive.org/web/20091012162649/http://www.pluralsight.com/community/blogs/jimjohn/archive/2005/06/18/11451.aspx

...