如何理清TransactionScope连接绑定不能像MSDN那样工作的事实?

时间:2014-12-11 16:43:37

标签: c# .net transactions transactionscope sqlconnection

Here你可以阅读:

  

隐式取消绑定默认值。导致连接断开连接   交易结束时。分离后,还有其他要求   连接在自动提交模式下执行。 Current属性是   在事务处于活动状态时执行请求时未检查。   事务结束后,将执行其他请求   自动提交模式。

我的所有连接都使用 Implicit Unbind ,默认设置。

我在创建 TransactionScope 实例后打开连接,因此所有打开的连接都是同一事务的一部分。到现在为止还挺好。当我在调用transactionScope.Complete()transactionScope.Dispose()方法后尝试使用其中一个连接时出现问题,我得到以下异常:

An unhandled exception of type 'System.Data.SqlClient.SqlException' occurred in System.Data.dll

Additional information: Distributed transaction completed. Either enlist this session in a new transaction or the NULL transaction.

在调用方法transactionScope.Complete()transactionScope.Dispose()之后可以避免关闭和重新打开连接的异常,但我的问题是我在代码中无法知道使用连接之后的连接如果创建了交易范围并且这些连接是否参与其中,则调用transactionScope.Complete()transactionScope.Dispose()

基本上不是 Implicit Unbind默认的事实。导致连接在事务结束时从事务中分离。:必须在事务结束后关闭并重新打开连接,然后才能为连接提交新命令以实际分离。

TransactionScope transactionScope = new TransactionScope();

SqlConnection connection1 = new SqlConnection(connectionString);
connection1.Open();

// Use connection1

SqlConnection connection2 = new SqlConnection(connectionString);
connection2.Open();

// Use connection1

transactionScope.Complete();
transactionScope.Dispose();

// The use of connection1 without close it and reopened it raise an exception

当然这是我的实际代码的简化,{SC}使用的connection1不属于{SC}使用connection1的同一类,我想避免必须添加依赖关系或全局变量来解决这个技术问题。基本上我的连接容器类没有意识到它包含的连接作为其实时循环的一部分参与了事务,我希望以这种方式保留在我的设计中。

1 个答案:

答案 0 :(得分:1)

Implicit Unbind仅在事务非分布时执行它所说的内容 - 也就是说,当只有一个连接使用该事务且服务器支持可升级事务(IIRC是SQL Server 2005或更高版本)时。

在您的代码示例中,当第二个连接打开并自动登记到事务中时,事务将提升为分布式事务。从那时起,“Implicit Unbind”不再适用,并且连接将不再隐式地取消与事务的绑定。我想你可能能够使用EnlistTransaction(null)明确取消绑定,抱歉我现在无法检查。

抽象中的这种泄漏通常是不可见的,因为建议的做法是使用“使用”块,如下例所示,这将导致在事务完成之前处理连接(因此没有机会在交易完成后使用连接。)

using (TransactionScope transactionScope = new TransactionScope())
{
    using (SqlConnection connection1 = GetConnection())
    {
        connection1.Open();

        // Use connection1
        ExecuteCommand(connection1);

        using (SqlConnection connection2 = GetConnection())
        {
            connection2.Open();
        }

        // Use connection1
        ExecuteCommand(connection1);
    }

    transactionScope.Complete();
}