Was bedeutet es für eine SqlConnection, in eine Transaktion "eingetragen" zu werden? Bedeutet dies einfach, dass Befehle, die ich für die Verbindung ausführe, an der Transaktion teilnehmen?
Wenn ja, unter welchen Umständen wird eine SqlConnection automatisch in eine Umgebungs-TransactionScope-Transaktion aufgenommen?
Siehe Fragen in Codekommentaren. Meine Vermutung zur Antwort jeder Frage folgt jeder Frage in Klammern.
Szenario 1: Öffnen von Verbindungen INNERHALB eines Transaktionsbereichs
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?)
}
Szenario 2: Verwenden von Verbindungen INNERHALB eines Transaktionsbereichs, der AUSSERHALB davon geöffnet wurde
//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?)
}
Gute Arbeit Triynko, Ihre Antworten sehen für mich alle sehr genau und vollständig aus. Einige andere Dinge, auf die ich hinweisen möchte:
(1) Manuelle Eintragung
In Ihrem obigen Code zeigen Sie (korrekt) die manuelle Registrierung wie folgt:
Es ist jedoch auch möglich, dies mit Enlist = false in der Verbindungszeichenfolge zu tun.
Hier ist noch etwas zu beachten. Wenn conn2 geöffnet wird, weiß der Verbindungspoolcode nicht, dass Sie ihn später in derselben Transaktion wie conn1 eintragen möchten. Dies bedeutet, dass conn2 eine andere interne Verbindung als conn1 erhält. Wenn dann conn2 eingetragen ist, sind jetzt 2 Verbindungen eingetragen, sodass die Transaktion zu MSDTC heraufgestuft werden muss. Diese Aktion kann nur durch die automatische Registrierung vermieden werden.
(2) Vor .Net 4.0 empfehle ich dringend, in der Verbindungszeichenfolge "Transaktionsbindung = Explizite Aufhebung der Bindung" festzulegen . Dieses Problem wurde in .Net 4.0 behoben, sodass Explicit Unbind völlig unnötig wird.
(3) Sich selbst zu rollen
CommittableTransaction
und sich darauf einzustellenTransaction.Current
, ist im Wesentlichen dasselbe wie das, wasTransactionScope
tut. Dies ist selten wirklich nützlich, nur zu Ihrer Information.(4)
Transaction.Current
ist fadenstatisch. Dies bedeutet, dass diesTransaction.Current
nur für den Thread festgelegt ist, der das erstellt hatTransactionScope
. Daher sind mehrere Threads, die dasselbe ausführenTransactionScope
(möglicherweise mitTask
), nicht möglich.quelle
Eine andere bizarre Situation, die wir gesehen haben, ist, dass wenn Sie eine konstruieren,
EntityConnectionStringBuilder
diese mitTransactionScope.Current
der Transaktion in Konflikt gerät und (wir denken) dazu beiträgt. Wir haben dies im Debugger beobachtet, woTransactionScope.Current
‚scurrent.TransactionInformation.internalTransaction
zeigtenlistmentCount == 1
vor der Errichtung undenlistmentCount == 2
danach.Um dies zu vermeiden, konstruieren Sie es im Inneren
using (new TransactionScope(TransactionScopeOption.Suppress))
und möglicherweise außerhalb des Bereichs Ihres Betriebs (wir haben es jedes Mal erstellt, wenn wir eine Verbindung benötigten).
quelle