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

C# 在什么情况下,SqlConnection会自动登记到环境TransactionScope事务中?,c#,ado.net,transactionscope,sqlconnection,sqlcommand,C#,Ado.net,Transactionscope,Sqlconnection,Sqlcommand,在事务中“登记”SqlConnection意味着什么?这是否仅仅意味着我在连接上执行的命令将参与事务 如果是,在什么情况下SqlConnection会自动登记到环境TransactionScope事务中 请参阅代码注释中的问题。我猜每个问题的答案都跟在括号里的每个问题后面 场景1:在事务范围内打开连接 场景2:在事务范围内使用在其外部打开的连接 自从提出这个问题以来,我做了一些测试,发现大部分答案(如果不是全部的话)都是我自己的,因为没有其他人回答。如果我遗漏了什么,请告诉我 Q1。是,除非在连

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

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

请参阅代码注释中的问题。我猜每个问题的答案都跟在括号里的每个问题后面

场景1:在事务范围内打开连接 场景2:在事务范围内使用在其外部打开的连接
自从提出这个问题以来,我做了一些测试,发现大部分答案(如果不是全部的话)都是我自己的,因为没有其他人回答。如果我遗漏了什么,请告诉我

Q1。是,除非在连接字符串中指定了“enlist=false”。连接池找到一个可用的连接。可用连接是未登记在事务中的连接或登记在同一事务中的连接

Q2.第二个连接是一个独立的连接,参与同一事务。我不确定这两个连接上的命令的交互,因为它们运行在同一个数据库上,但我认为如果同时在这两个连接上发出命令,可能会发生错误:错误如下

Q3.是的,它会升级为分布式事务,因此登记多个连接(即使使用相同的连接字符串)会导致它成为分布式事务,可以通过在transaction.Current.TransactionInformation.DistributedIdentifier检查非空GUID来确认。 *更新:我在某个地方读到,SQL Server 2008中修复了这一问题,因此当两个连接使用相同的连接字符串时(只要两个连接没有同时打开),就不会使用MSDTC。这允许您在一个事务中多次打开和关闭连接,通过尽可能晚地打开连接并尽快关闭连接,可以更好地利用连接池

Q4.否。在没有事务作用域处于活动状态时打开的连接不会自动登记到新创建的事务作用域中

Q5.否。除非在事务作用域中打开连接,或在该作用域中登记现有连接,否则基本上没有事务。必须在事务作用域中自动或手动登记您的连接,以便您的命令参与事务

Q6.是,即使代码恰好在回滚的事务作用域块中执行,但未参与事务的连接上的命令也会按发出的方式提交。如果连接未登记在当前事务作用域中,则它不参与事务,因此提交或回滚事务将不会影响在未登记在事务作用域中的连接上发出的命令。。。作为。除非您了解自动登记过程,否则很难发现这一点:只有在活动事务范围内打开连接时才会发生

Q7.是。可以通过调用EnclestTransaction(transaction.current)在当前事务作用域中显式登记现有连接。您还可以通过使用DependentTransaction在事务中的单独线程上登记连接,但与以前一样,我不确定同一事务中涉及的同一数据库的两个连接如何交互。。。并且可能会发生错误,当然,第二个登记的连接会导致事务升级为分布式事务

Q8。可能会抛出错误。如果使用了TransactionScopeOption.Required,并且该连接已登记在事务范围事务中,则不存在错误;事实上,没有为作用域创建新的事务,并且事务计数(@@trancount)没有增加。但是,如果使用TransactionScopeOption.RequiresNew,则在尝试在新事务作用域事务中登记连接时会收到一条有用的错误消息:“连接当前已登记事务。请完成当前事务并重试。”是的,如果完成事务,则会登记连接,您可以在新事务中安全地登记连接。 更新:如果您以前在连接上调用过BeginTransaction,则在尝试登记到新事务作用域事务时会引发一个稍有不同的错误:“无法登记到事务中,因为连接上正在进行本地事务。请完成本地事务,然后重试。”另一方面,当SqlConnection在事务作用域事务中登记时,您可以安全地在SqlConnection上调用BeginTransaction,这实际上会将@trancount增加1,这与使用嵌套事务作用域的必需选项不同,后者不会导致它增加。有趣的是,如果您接着使用必需的选项创建另一个嵌套的事务作用域,则不会出现错误,因为已经有一个活动的事务作用域事务不会导致任何更改(请记住,当事务作用域事务已经活动且使用了必需的选项时,@@trancount不会增加)


Q9.是。命令参与连接所登记的任何事务,而不管C代码中的活动事务范围是什么。

干得好Triynko,你的答案在我看来都非常准确和完整。我还想指出其他一些事情:

(1)手动登记

在上面的代码中,您(正确地)显示如下手动登记:

using (SqlConnection conn = new SqlConnection(connStr))
{
    conn.Open();
    using (TransactionScope ts = new TransactionScope())
    {
        conn.EnlistTransaction(Transaction.Current);
    }
}
然而,也可以这样做,使用E
//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?)
}
using (SqlConnection conn = new SqlConnection(connStr))
{
    conn.Open();
    using (TransactionScope ts = new TransactionScope())
    {
        conn.EnlistTransaction(Transaction.Current);
    }
}
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);
    }
}