Qu'est-ce que cela signifie pour une SqlConnection d'être «enrôlé» dans une transaction? Cela signifie-t-il simplement que les commandes que j'exécute sur la connexion participeront à la transaction?
Si tel est le cas, dans quelles circonstances un SqlConnection est-il automatiquement enrôlé dans une transaction TransactionScope ambiante?
Voir les questions dans les commentaires de code. Je suppose que la réponse à chaque question suit chaque question entre parenthèses.
Scénario 1: Ouverture de connexions DANS UNE étendue de transaction
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?)
}
Scénario 2: Utilisation de connexions à l'intérieur d'une étendue de transaction qui a été ouverte à l'extérieur de celle-ci
//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?)
}
Beau travail Triynko, vos réponses me semblent toutes très précises et complètes. Je voudrais souligner d'autres choses:
(1) Inscription manuelle
Dans votre code ci-dessus, vous affichez (correctement) l'inscription manuelle comme ceci:
Cependant, il est également possible de le faire comme ceci, en utilisant Enlist = false dans la chaîne de connexion.
Il y a autre chose à noter ici. Lorsque conn2 est ouvert, le code du pool de connexions ne sait pas que vous souhaitez l'enrôler ultérieurement dans la même transaction que conn1, ce qui signifie que conn2 reçoit une connexion interne différente de conn1. Ensuite, lorsque conn2 est inscrit, il y a maintenant 2 connexions inscrites, de sorte que la transaction doit être promue en MSDTC. Cette promotion ne peut être évitée qu'en utilisant l'inscription automatique.
(2) Avant .Net 4.0, je recommande vivement de définir "Transaction Binding = Explicit Unbind" dans la chaîne de connexion . Ce problème est résolu dans .Net 4.0, rendant Explicit Unbind totalement inutile.
(3) Rouler le vôtre
CommittableTransaction
et le réglerTransaction.Current
est essentiellement la même chose que ce qui seTransactionScope
passe. C'est rarement réellement utile, juste pour info.(4)
Transaction.Current
est statique. Cela signifie que ceTransaction.Current
paramètre est uniquement défini sur le thread qui a créé le fichierTransactionScope
. Ainsi, plusieurs threads exécutant la même choseTransactionScope
(éventuellement en utilisantTask
) ne sont pas possibles.la source
Une autre situation bizarre que nous avons vue est que si vous construisez un,
EntityConnectionStringBuilder
il va bougerTransactionScope.Current
et (nous pensons) vous engager dans la transaction. Nous avons observé cela dans le débogueur, oùTransactionScope.Current
« scurrent.TransactionInformation.internalTransaction
montreenlistmentCount == 1
avant de construire, et par laenlistmentCount == 2
suite.Pour éviter cela, construisez-le à l'intérieur
using (new TransactionScope(TransactionScopeOption.Suppress))
et peut-être en dehors de la portée de votre opération (nous la construisions chaque fois que nous avions besoin d'une connexion).
la source