web-dev-qa-db-fra.com

"Cette opération SqlTransaction est terminée; elle n'est plus utilisable." ... erreur de configuration?

Je travaille là-dessus depuis environ un jour et demi et j'ai consulté de nombreux blogs et articles d’aide sur le Web. J'ai trouvé plusieurs questions sur SO liées à cette erreur, mais je ne pensais pas qu'elles s'appliquaient parfaitement à ma situation (ou, dans certains cas, malheureusement, je ne les comprenais pas assez pour les implémenter: P). Je ne suis pas sûr de pouvoir décrire cela assez bien pour obtenir de l'aide ... mais voici:

Nous avons une application .NET pour suivre nos ressources. Il existe une fonction d'exportation permettant de copier une ressource dans le système de suivi du temps et le système de facturation; cela accède à une procédure stockée qui relie aux bases de données de temps et de facturation.

J'ai récemment déplacé la base de données du système de facturation sur un nouveau serveur (serveur d'origine: Server 2003 SP2, SQL 2005; nouveau serveur: Server 2008 R2, SQL 2008 R2). J'ai un serveur lié configuré qui pointe vers les bases de données 2008. J'ai mis à jour la procédure stockée pour qu'elle pointe vers le serveur 2008, puis un message d'erreur concernant MSDTC et RPC (http://www.safnet.com/writing/tech/archives/2007/06/server_myserver.html). J'ai activé 'rpc/rpc out' sur le serveur lié et paramétré MSDTC pour autoriser l'accès au réseau (quelque chose comme ceci: http://www.sqlwebpedia.com/content/msdtc-troubleshooting ).

Maintenant, je reçois ce qui précède lorsque j'essaie d'exécuter la fonction d'exportation: "Cette opération SqlTransaction est terminée; elle n'est plus utilisable." Ce qui me semble étrange, c’est que lorsque je viens d’exécuter la procédure stockée (à partir de SSMS), cela signifie qu’elle se termine avec succès. 

Quelqu'un a-t-il déjà vu cela avant? Ai-je oublié quelque chose dans la configuration? Je ne cesse de parcourir les mêmes pages et la seule chose que j'ai constatée est que je n'ai pas redémarré après avoir apporté les modifications MSDTC (mentionnées ici: http://social.msdn.Microsoft.com/forums/en-US/adodotnetdataproviders/thread/7172223f-acbe-4472-8cdf-feec80fd2e64/ ).

Je peux publier une partie ou la totalité de la procédure stockée, si cela peut vous aider ... veuillez me le faire savoir.

41
White Island

Je crois que ce message d'erreur est dû à une "transaction zombie".

Recherchez les zones possibles dans lesquelles le transacton est engagé deux fois (ou annulé deux fois, ou annulé et engagé, etc.). Le code .Net valide-t-il la transaction après que le SP l'ait déjà validée? Le code .Net l'annule-t-il lorsqu'il rencontre une erreur, puis tente-t-il de l'annuler à nouveau dans une clause catch (ou finally)?

Il est possible qu'une condition d'erreur n'ait jamais été rencontrée sur l'ancien serveur. Le code "double rollback" défectueux n'a donc jamais été touché. Peut-être que vous avez maintenant une situation où est une erreur de configuration sur le nouveau serveur, et maintenant le code défectueux est touché via la gestion des exceptions.

Pouvez-vous déboguer dans le code d'erreur? Avez-vous une trace de pile?

40
Phil Sandler

J'ai eu cela récemment après refactoring dans un nouveau gestionnaire de connexion. Une nouvelle routine a accepté une transaction afin qu'elle puisse être exécutée dans le cadre d'un lot. Le problème était avec un bloc using:

public IEnumerable<T> Query<T>(IDbTransaction transaction, string command, dynamic param = null)
{
  using (transaction.Connection)
  {
    using (transaction)
    {
      return transaction.Connection.Query<T>(command, new DynamicParameters(param), transaction, commandType: CommandType.StoredProcedure);
    }
  }
}

Il semble que l'utilisateur externe ferme la connexion sous-jacente, donc toute tentative de validation ou d'annulation de la transaction a généré le message "This SqlTransaction has completed; it is no longer usable."

J'ai enlevé les utilisateurs, ajouté un test de couverture et le problème a disparu.

public IEnumerable<T> Query<T>(IDbTransaction transaction, string command, dynamic param = null)
{
  return transaction.Connection.Query<T>(command, new DynamicParameters(param), transaction, commandType: CommandType.StoredProcedure);
}

Recherchez tout élément susceptible de fermer la connexion dans le contexte d'une transaction.

6
Phil Cooper

J'ai récemment rencontré une situation similaire. Pour déboguer dans n'importe quelle version de VS IDE, ouvrez des exceptions à partir de Debug (Ctrl + D, E) - cochez toutes les cases à cocher avec la colonne "Lancé" et exécutez l'application en mode débogage. J'ai réalisé que l'une des tables n'avait pas été importée correctement dans la nouvelle base de données. Par conséquent, Sql Exception interne supprimait la connexion et aboutissait à cette erreur. 

En résumé, si le code précédemment utilisé retourne cette erreur sur une nouvelle base de données, il pourrait s'agir d'un problème manquant dans le schéma de base de données. 

J'espère que cela vous aidera HydTechie

3
HydPhani

Dans mon cas, le problème était que l'une des requêtes incluses dans la transaction soulevait une exception, et même si l'exception était gérée "avec élégance", il était toujours en mesure d'annuler la transaction dans son intégralité.

Mon pseudo-code était comme:

var transaction = connection.BeginTransaction();
for(all the lines in a file)
{
     try{
         InsertLineInTable(); // INSERT statement might fail and throw an exception
     }
     catch {
         // notify the user about the error on line x and continue
     }
}

// Commit and Rollback will fail if one of the queries 
// in InsertLineInTable threw an exception
if(CheckTableForErrors())
{
    transaction.Commit();
}
else
{
    transaction.Rollback();
}
3
Loris

J'ai le même problème. Cette erreur se produit en raison de la mise en commun des connexions. Lorsqu'il existe deux utilisateurs ou plus qui accèdent au système, le regroupement de connexions réutilise également la connexion et la transaction. Si le premier utilisateur exécute commit ou rollback, la transaction n'est plus utilisable.

2
Ademilso Peres

Vérifiez également que les bases de données exécutées depuis votre application .NET ne sont pas longues Par exemple, vous appelez peut-être une procédure stockée ou une requête qui ne dispose pas de suffisamment de temps pour terminer et qui peut apparaître dans vos journaux sous la forme:

  • Le délai d'exécution a expiré. Le délai d'attente s'est écoulé avant la fin de l'opération ou le serveur ne répond pas.

    • Cette transaction SqlTransaction est terminée. il n'est plus utilisable.

Vérifiez les paramètres de délai d'expiration de la commande Essayez de lancer une trace (profileur) et voyez ce qui se passe du côté de la base de données ...

1
Milan

Voici un moyen de détecter une transaction Zombie

SqlTransaction trans = connection.BeginTransaction();

//some db calls here

if (trans.Connection != null) //Detecting zombie transaction
{
  trans.Commit();
}

Décompiler la classe SqlTransaction, vous verrez ce qui suit

public SqlConnection Connection
{
  get
  {
    if (this.IsZombied)
      return (SqlConnection) null;
    return this._connection;
  }
}

Je remarque que si la connexion est fermée, la transop deviendra zombie, donc ne peut pas Commit. Pour mon cas, c’est parce que j’ai la Commit() dans un bloc finally alors que la connexion était dans le bloc try Cette disposition entraîne l’élimination de la connexion et la collecte des ordures. La solution consistait à placer Commit dans le bloc try à la place.

0
Jeson Martajaya