在什么情况下SqlConnection会自动登记在环境TransactionScope事务中?

时间:2010-05-21 19:13:10

标签: c# ado.net transactionscope sqlconnection sqlcommand

在事务中“登记”SqlConnection是什么意思?它只是意味着我在连接上执行的命令将参与事务吗?

如果是这样,在什么情况下SqlConnection 会自动在环境TransactionScope事务中登记?

查看代码评论中的问题。我对每个问题答案的猜测都在括号内的每个问题之后。

场景1:在事务范围内打开连接

using (TransactionScope scope = new TransactionScope())
using (SqlConnection conn = ConnectToDB())
{   
    // Q1: Is connection automatically enlisted in transaction? (Yes?)
    //
    // Q2: If I open (and run commands on) a second connection now,
    // with an identical connection string,
    // what, if any, is the relationship of this second connection to the first?
    //
    // Q3: Will this second connection's automatic enlistment
    // in the current transaction scope cause the transaction to be
    // escalated to a distributed transaction? (Yes?)
}

场景2:在内部打开的事务范围内使用连接

//Assume no ambient transaction active now
SqlConnection new_or_existing_connection = ConnectToDB(); //or passed in as method parameter
using (TransactionScope scope = new TransactionScope())
{
    // Connection was opened before transaction scope was created
    // Q4: If I start executing commands on the connection now,
    // will it automatically become enlisted in the current transaction scope? (No?)
    //
    // Q5: If not enlisted, will commands I execute on the connection now
    // participate in the ambient transaction? (No?)
    //
    // Q6: If commands on this connection are
    // not participating in the current transaction, will they be committed
    // even if rollback the current transaction scope? (Yes?)
    //
    // If my thoughts are correct, all of the above is disturbing,
    // because it would look like I'm executing commands
    // in a transaction scope, when in fact I'm not at all, 
    // until I do the following...
    //
    // Now enlisting existing connection in current transaction
    conn.EnlistTransaction( Transaction.Current );
    //
    // Q7: Does the above method explicitly enlist the pre-existing connection
    // in the current ambient transaction, so that commands I
    // execute on the connection now participate in the
    // ambient transaction? (Yes?)
    //
    // Q8: If the existing connection was already enlisted in a transaction
    // when I called the above method, what would happen?  Might an error be thrown? (Probably?)
    //
    // Q9: If the existing connection was already enlisted in a transaction
    // and I did NOT call the above method to enlist it, would any commands
    // I execute on it participate in it's existing transaction rather than
    // the current transaction scope. (Yes?)
}

3 个答案:

答案 0 :(得分:179)

答案 1 :(得分:19)

好工作Triynko,你的答案对我来说都很准确和完整。我想指出的其他一些事情:

(1)手动登记

在上面的代码中,您(正确)显示了这样的手动登记:

using (SqlConnection conn = new SqlConnection(connStr))
{
    conn.Open();
    using (TransactionScope ts = new TransactionScope())
    {
        conn.EnlistTransaction(Transaction.Current);
    }
}

但是,也可以这样做,在连接字符串中使用Enlist = false。

string connStr = "...; Enlist = false";
using (TransactionScope ts = new TransactionScope())
{
    using (SqlConnection conn1 = new SqlConnection(connStr))
    {
        conn1.Open();
        conn1.EnlistTransaction(Transaction.Current);
    }

    using (SqlConnection conn2 = new SqlConnection(connStr))
    {
        conn2.Open();
        conn2.EnlistTransaction(Transaction.Current);
    }
}

这里还有另外一点需要注意。当conn2打开时,连接池代码不知道您以后要在与conn1相同的事务中登记它,这意味着conn2被赋予与conn1不同的内部连接。然后,当conn2被登记时,现在有2个连接登记,因此必须将事务提升为MSDTC。只有使用自动登记才能避免此促销。

(2)在.Net 4.0之前,我强烈建议您设置"Transaction Binding=Explicit Unbind" in the connection string。这个问题在.Net 4.0中得到修复,使得Explicit Unbind完全不必要。

(3)滚动您自己的CommittableTransaction并将Transaction.Current设置为TransactionScopeTransaction.Current的内容基本相同。这实际上很少有用,仅供参考。

(4) Transaction.Current是线程静态的。这意味着TransactionScope仅在创建TransactionScope的线程上设置。因此,执行相同Task(可能使用{{1}})的多个线程是不可能的。

答案 2 :(得分:0)

我们看到的另一个奇怪的情况是,如果你构建一个EntityConnectionStringBuilder,它将与TransactionScope.Current混淆,并且(我们认为)在事务中登记。我们在调试器中发现了这一点,其中TransactionScope.Current的{​​{1}}在构建前显示current.TransactionInformation.internalTransaction,之后显示enlistmentCount == 1

要避免这种情况,请在

中构建它

enlistmentCount == 2

并且可能超出了您的操作范围(我们每次需要连接时都在构建它)。