如何在TransactionScope中包装IDbTransactions

5

我有几个代码方法长这样:

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个回答

7

我认为你在混淆技术,应该避免同时使用TransactionScopeDbTransaction,因为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();
}

你调用的方法将共享相同的事务。


这是我的计划,这样做是否可以使顶部代码在事务上安全?我想我只能写一些测试代码来验证一下! - Andrew Bullock
关于代码行 command.Transaction = transaction; - transaction 是从哪里来的? - Christoffer Lette
1
@ChristofferLette:我的错!实际上你不需要显式地指定它,因为TransactionScope会处理它 - 已更新。 - Regent

0

链接未找到,很遗憾。 - Kiquenet

0

我不确定我要说什么,但是在阅读了这篇文章之后:http://msdn.microsoft.com/en-us/library/ms172152(v=vs.80).aspx#sectionSection3

我认为你应该在你的子方法中使用TransactionScope,但我们需要选项Required。因此,当它们被单独调用时,您仍然拥有一个事务,并且当您在上层方法中调用它们时,该事务将集成到环境事务中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接