2010-06-07 41 views
2

par exemple. Lorsqu'un blocage se produit, les commandes SQL suivantes sont exécutées avec succès, même si elles ont affecté une transaction SQL qui est après l'annulation. Il semble que cela soit dû à une nouvelle transaction implicite créée sur SQL Server.Bogue de transaction zombie ADO.NET? Comment s'assurer que les commandes ne seront pas exécutées sur une transaction implicite?

Quelqu'un pouvait s'attendre à ce que ADO.NET lève une exception que les commandes sont en cours d'exécution sur une transaction zombie. Cependant, une telle exception n'est pas levée. (Je pense que c'est un bug dans ASP.NET.) En outre, à cause de la transaction zombie, le dernier Dispose() ignore silencieusement la restauration.

Des idées, comment puis-je m'assurer que personne ne peut exécuter des commandes sur une transaction implicite? Ou, comment vérifier cette transaction est zombie? J'ai trouvé que Commit() et Rollback() chèque de transaction zombie, mais je peux les appeler pour un test :)

J'ai aussi trouvé que la lecture aussi IsolationLevel fera le chèque, mais je ne sais pas si simple, appelant transaction.IsolationLevel.ToString(); ne seront pas supprimés par un futur optimiseur. Ou connaissez-vous un autre moyen sûr d'invoquer un getter (sans utiliser de réflexion ou d'émission IL)?

EDIT: Remus Rusanu a souligné que cette situation ne devrait normalement pas se produire. Oui c'est vrai. Cela se produit généralement lorsqu'il y a un bug dans un code. Dans notre cas, il y avait une routine de journalisation dans une instruction finally qui essayait de stocker l'échec dans la base de données. Maintenant, j'essaie de trouver une solution pour détecter de tels bugs dans le futur. Depuis ces bugs sont difficiles à tester. Si ADO.NET vérifie que la transaction fournie est zombie, ce bogue se trouvera beaucoup plus facilement. J'ai trouvé deux possibilités:

  1. désactiver la création de transactions implicites - Je ne suis pas sûr que ce soit possible.
  2. Assurez-vous qu'avant d'exécuter des commandes, vérifiez que la transaction zombie est exécutée.

Répondre

2

Ce que vous décrivez n'existe pas. Une annulation de transaction lancera une exception très visible dans votre application. pour une raison quelconque, je crois plutôt que votre code capture l'exception et l'ignore silencieusement, continuant à exécuter des instructions après la fin de la transaction.

+0

Oui, vous avez raison. Il y avait un bug dans un code. Le bug est corrigé. Maintenant, j'essaie juste de trouver une solution pour éviter de tels bugs. (Cela a été causé par un code de journalisation dans l'instruction finally, qui essayait de consigner le problème dans la base de données.) –

0

Probablement pas directement lié à votre problème, car il a été causé par un bogue, mais pourrait être intéressant. Toutes les erreurs ne provoquent pas le retour en arrière d'une transaction, donc parfois une transaction peut être "partiellement réussie" - certaines instructions sont erronées tandis que d'autres sont compelées correctement.
Il existe une option SET XACT_ABORT ON qui permet au serveur d'annuler une transaction en cas d'erreur. Compte tenu de votre question, vous ne pouvez pas désactiver les transactions implicites (si vous exécutez une instruction SQL, une transaction implicite sera créée sauf si une autre transaction est déjà active). Il vous suffit donc de gérer les erreurs correctement pour vous assurer qu'une transaction est là quand vous en avez besoin. Regardez la classe TransactionScope, vous pouvez l'utiliser pour éviter de gérer ces transactions dans votre code.

+0

Thx, je connais TransactionScope. (En fait, j'utilise ma version allégée.) Donc, il n'y a pas d'option pour interdire la création d'une transaction implicite (par exemple, les commandes de manipulation de données échoueront, s'il n'y a pas de transaction explicite)? –

0

// Sur la base de votre description Je devine que votre code fait effectivement ce

 SqlConnection conn = new SqlConnection("ConnectionString"); 
     SqlCommand cmd = new SqlCommand("insert into ...."); 

     cmd.Connection = conn; 

     conn.Open(); 

     SqlTransaction tran = conn.BeginTransaction(); 


     cmd.Transaction = tran; 

     tran.Rollback(); //or tran.Dispose(); 

     cmd.ExecuteNonQuery(); 

Cela provoque le cmd à exécuter en dehors du cadre d'une transaction.

Suppression de la ligne cmd.Connection = conn; atteindra le comportement que je pense que vous recherchez (par exemple, la commande échouera car la transaction n'est plus valide.)

SqlConnection conn = new SqlConnection("ConnectionString"); 
    SqlCommand cmd = new SqlCommand("insert into ...."); 

    //cmd.Connection = conn; 

    conn.Open(); 

    SqlTransaction tran = conn.BeginTransaction(); 
    cmd.Connection = tran.Connection; 

    cmd.Transaction = tran; 

    tran.Rollback(); //or tran.Dispose(); 

    cmd.ExecuteNonQuery(); 
+0

En fait non, je n'appelle pas rollback. –

+0

La restauration se produira si la transaction est éliminée. –

+0

Non, dans mon cas, la restauration a été provoquée par un blocage sur un serveur. Cependant, les ADO.NET n'ont pas vérifié cela et exécutaient une commande sur une transaction zombie. –