Quelle est la raison du “contexte de Transaction en cours d'utilisation par une autre session”
Je suis à la recherche d'une description de la racine de cette erreur: "le contexte de Transaction en cours d'utilisation par une autre session".
Je reçois parfois à l'un de mes unittests donc je ne peux pas le fournisseur de repro code. Mais je me demande ce qu'est "by design" raison de l'erreur.
Mise à JOUR: l'erreur renvoie comme SqlException à partir de SQL Server 2008. Un endroit où je reçois le message d'erreur semble être mono-thread. Mais j'ai probablement unittests interaction que je reçois l'erreur où exécuter plusieurs tests à la fois (MSTest dans VS2008sp1).
Mais le test en échec ressemble:
- créer un objet et de l'enregistrer à l'intérieur de DB-transaction (commit)
- créer TransactionScope
- en essayant d'ouvrir une connexion, ici, je reçois SqlException avec de tels stacktrace:
.
System.Data.SqlClient.SqlException: Transaction context in use by another session.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)
at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
at System.Data.SqlClient.TdsParser.TdsExecuteTransactionManagerRequest(Byte[] buffer, TransactionManagerRequestType request, String transactionName, TransactionManagerIsolationLevel isoLevel, Int32 timeout, SqlInternalTransaction transaction, TdsParserStateObject stateObj, Boolean isDelegateControlRequest)
at System.Data.SqlClient.SqlInternalConnectionTds.PropagateTransactionCookie(Byte[] cookie)
at System.Data.SqlClient.SqlInternalConnection.EnlistNonNull(Transaction tx)
at System.Data.SqlClient.SqlInternalConnection.Enlist(Transaction tx)
at System.Data.SqlClient.SqlInternalConnectionTds.Activate(Transaction transaction)
at System.Data.ProviderBase.DbConnectionInternal.ActivateConnection(Transaction transaction)
at System.Data.ProviderBase.DbConnectionPool.GetConnection(DbConnection owningObject)
at System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection)
at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory)
at System.Data.SqlClient.SqlConnection.Open()
J'ai trouvé ces postes:
- http://blogs.msdn.com/asiatech/archive/2009/08/10/system-transaction-may-fail-in-multiple-thread-environment.aspx
- http://msdn.microsoft.com/en-us/library/ff649002.aspx
Mais je ne peux pas comprendre ce que "Plusieurs threads partage la même opération dans une étendue de la transaction sera la cause de l'exception suivante:" les Transactions contexte en cours d'utilisation par une autre session.'" les moyens. Tous les mots sont compréhensibles, mais pas le point.
Fait, je peux partager un système de transaction entre les threads. Et il y a même mécanisme spécial pour ce - DependentTransaction de classe et de Transaction.DependentClone méthode.
Je suis en train de reproduire un cas d'utilisation à partir du premier post:
- Thread principal crée transaction DTC, reçoit DependentTransaction (créé à l'aide de la Transaction.Actuel.DependentClone sur le thread principal
- Enfant thread 1 enrôle dans cette transaction DTC par la création d'une étendue de transaction basée sur la transaction dépendante (adoptée par le constructeur)
- Enfant thread 1 ouvre une connexion
- Enfant thread 2 enrôle dans la transaction DTC par la création d'une étendue de transaction basée sur la transaction dépendante (adoptée par le constructeur)
- Enfant thread 2 ouvre une connexion
avec ce code:
using System;
using System.Threading;
using System.Transactions;
using System.Data;
using System.Data.SqlClient;
public class Program
{
private static string ConnectionString = "Initial Catalog=DB;Data Source=.;User ID=user;PWD=pwd;";
public static void Main()
{
int MAX = 100;
for(int i =0; i< MAX;i++)
{
using(var ctx = new TransactionScope())
{
var tx = Transaction.Current;
// make the transaction distributed
using (SqlConnection con1 = new SqlConnection(ConnectionString))
using (SqlConnection con2 = new SqlConnection(ConnectionString))
{
con1.Open();
con2.Open();
}
showSysTranStatus();
DependentTransaction dtx = Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
Thread t1 = new Thread(o => workCallback(dtx));
Thread t2 = new Thread(o => workCallback(dtx));
t1.Start();
t2.Start();
t1.Join();
t2.Join();
ctx.Complete();
}
trace("root transaction completes");
}
}
private static void workCallback(DependentTransaction dtx)
{
using(var txScope1 = new TransactionScope(dtx))
{
using (SqlConnection con2 = new SqlConnection(ConnectionString))
{
con2.Open();
trace("connection opened");
showDbTranStatus(con2);
}
txScope1.Complete();
}
trace("dependant tran completes");
}
private static void trace(string msg)
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " : " + msg);
}
private static void showSysTranStatus()
{
string msg;
if (Transaction.Current != null)
msg = Transaction.Current.TransactionInformation.DistributedIdentifier.ToString();
else
msg = "no sys tran";
trace( msg );
}
private static void showDbTranStatus(SqlConnection con)
{
var cmd = con.CreateCommand();
cmd.CommandText = "SELECT 1";
var c = cmd.ExecuteScalar();
trace("@@TRANCOUNT = " + c);
}
}
Il ne parvient pas à Terminer l'appel de la racine TransactionScope. Mais l'erreur est différente:
Exception Non Gérée: System.Des Transactions.TransactionInDoubtException: La transaction est mise en doute. --->
pired. Le délai écoulé avant la fin de l'opération ou le serveur ne répond pas.
Pour résumer: je veux comprendre ce "contexte de Transaction en cours d'utilisation par une autre session" signifie et comment le reproduire.
Non, l'ouverture de deux connexions simultanées conduit toujours à une transaction distribuée. Il n'a pas d'importance, ils ont les mêmes chaînes de connexion ou pas
mon erreur. Désolé.
mais vous avez raison dans le cas où si la même connexion instance s'ouvre et se ferme en série puis en sql2008 nous les tran mais dans sql2005 distibuted tran.
OriginalL'auteur Shrike | 2010-05-18
Vous devez vous connecter pour publier un commentaire.
C'est un peu tard pour la réponse 🙂 mais espérons qu'il sera utile pour les autres.
Réponse contient trois parties:
1. Que signifie le "contexte de Transaction en cours d'utilisation par une autre session."
Avis Important: le contexte de Transaction verrou est acquis juste avant et immédiatement libéré après que l'interaction entre
SqlConnection
et SQL Server.Lorsque vous exécuter une Requête SQL,
SqlConnection
"regards" est-il une opération d'emballage. Il peut êtreSqlTransaction
("native" pour SqlConnection) ouTransaction
deSystem.Transactions
de l'assemblée.Lors de la transaction de trouvé
SqlConnection
l'utilise pour communiquer avec SQL Server et au moment où ils communiquerTransaction
contexte est exclusivement verrouillé.Ce n'
TransactionScope
? Il créeTransaction
et fournit .NET Framework de Composants informations à ce sujet, donc tout le monde y compris SqlConnection peut (et doit) utiliser.Donc déclarer
TransactionScope
nous sommes en train de créer de nouvelles Transactions qui sont disponibles pour tous "transactable" objets instanciés dans le courantThread
.Décrit erreur signifie que le suivant:
SqlConnections
sous le mêmeTransactionContext
(ce qui signifie qu'elles ont trait à la même transaction)SqlConnection
pour communiquer avec SQL Server simultanémentTransaction
contexte et la prochaine avaient envoyé erreur2. Comment reproduire l'erreur "contexte de Transaction en cours d'utilisation par une autre session."
Tout d'abord, le contexte de transaction est utilisé ("verrouillé") au moment même de l'exécution d'une commande sql. Il est donc difficile à reproduire ce comportement sûr.
Mais nous pouvons essayer de le faire par le démarrage de plusieurs threads d'exécution relativement long SQL opérations effectuées en vertu de la transaction unique.
Nous allons préparer la table
[dbo].[Persons]
dans[tests]
Base de données:Et de se reproduire "contexte de Transaction en cours d'utilisation par une autre session." erreur avec le code C# basé sur la Pie-grièche exemple de code
Et en guise de conclusion, quelques mots sur la mise en œuvre de l'appui de transaction dans votre application:
SELECT
/UPDATE
/etc... les demandes dans une seule file d'attente et à les servir avec un seul thread travailleur;TransactionScope
. Par défaut estSerializable
mais dans la plupart des casReadCommitted
est assez;TransactionScope
etDependentTransaction
OriginalL'auteur Ilya Chidyakin
Semble assez simple. Si vous demandez deux connexions différentes pour la même opération, puis essayez d'envoyer des commandes sur chacun des deux connexions, simultanément, à partir de différents threads, un conflit peut se produire.
En d'autres termes, un thread est une commande sur une seule connexion et est titulaire d'une sorte de verrou sur le contexte de transaction. L'autre fil, à l'aide de la connexion aux autres, essaie d'exécuter des commandes en même temps, et ne peut pas verrouiller le même contexte de transaction, qui est utilisé par l'autre thread.
Et si SysTrans ne supporte pas l'environnement multithread pourquoi avons-nous besoin DependantTransaction type.
OriginalL'auteur Triynko
Prendre du recul et de se concentrer plus sur votre code et moins dans plusieurs threads info flottant autour.
Si votre scénario n'implique pas de filetage, il pourrait se rapportent à des œuvres qui ne sont pas fermés comme vous l'attendez.
Peut-être que le code sql que vous appelez n'a pas atteint la transaction de validation de l'instruction. Ou il y a autre chose à ce niveau. Peut-être que vous avez utilisé une occurrence de SqlConnection paramètre de la transaction dans le .net code, et réutilisez la même instance de l'autre code qui utilise la TransactionScope. Essayez de les ajouter à l'aide de() instructions le cas échéant, assurez-vous que tout est fermé, comme vous l'attendez.
OriginalL'auteur eglasius
Comment je voudrais traiter de cette question lors de la construction de Linq états avec mutlipe objets est d'avoir un constructeur pour chaque classe qui prend dans un contexte de données et un correspondant GetDataContext() la méthode dans chaque classe. lors de la combinaison des classes, j'avais nouveau jusqu'les instances de classe en passant dans la première classe de la GetContext()
OriginalL'auteur James Fleming
Vous devez créer un
DependentTransaction
pour chaque thread, puis à l'intérieur de la thread créer & ouvrir la connexion db à l'intérieur d'unTransacctionScope
à l'aide de ladependentTransaction
dans le ctor.OriginalL'auteur Tochas
J'ai une application multi-thread qui fait de la manipulation de données et stocke les résultats dans la base de données. Parce que les différents threads de travail sur les différents types de données, à l'écriture de code de collecter les résultats et le rincer à la base de données dans un thread est de plus en plus pénible que d'avoir à chaque thread écrire les résultats lui-même quand il est fait.
Je voulais courir ce dans une transaction, de sorte que j'ai la possibilité de revenir tous les travaux dans le cas où une erreur se produit dans tout l'un de l'enfant threads. L'ajout de transactions a commencé à causer des problèmes, ce qui m'a conduit à ce message, mais j'ai été en mesure de travailler à travers eux. Multi-thread accès de base de données en une seule opération est possible. Je suis même en utilisant à la fois LINQ-to-SQL et SqlBulkCopy ensemble dans la même transaction.
J'ai trouvé Ilya Chidyakin réponse très utile. Vous devez passer une DependentTransaction à chaque thread, et l'utiliser pour créer un nouveau TransactionScope. Et, vous devez vous rappeler de commettre TransactionScope et la DependentTransaction dans chaque thread. Enfin, vous devez attendre pour valider vos "originaux" opération jusqu'à ce que tous les enfants du travail est fait. (DependentTransaction devrait s'occuper de cela, en fait, mais j'étais déjà à l'aide de Fil.Joindre à attendre pour l'ensemble du travail à faire, avant que j'ai ajoutée des opérations à ce projet.)
La clé, c'est, qu'un seul thread peut accéder à la base de données à un moment donné. J'ai juste utilisé un sémaphore pour bloquer l'accès à la base de données à un seul thread à la fois. Depuis que mon fils passe la plupart du temps de calcul et seulement un peu de temps à écrire à la base de données, je n'ai pas vraiment d'encourir une pénalité sur les performances à cause de cela... Cependant, si vos fils sont à l'aide de la base de données fréquemment, cette exigence peut essentiellement retirer l'avantage de performance de multi-threading, si vous voulez tout ce qui est contenu dans une seule transaction.
Si vous avez plusieurs threads accèdent à la base de données à la fois, vous obtiendrez une Exception avec le message "contexte de Transaction en cours d'utilisation par une autre session." Si vous oubliez d'engager toutes les transactions dans chaque thread, vous aurez une Exception avec le message "L'opération est mise en doute" lorsque vous essayez de valider l'extérieur-la plupart des transactions.
OriginalL'auteur Aaron