25 votes

SqlTransaction a terminé

J'ai une application qui effectue potentiellement des milliers d'insertions dans une base de données SQL Server 2005. Si une insertion échoue pour une raison quelconque (contrainte de clé étrangère, longueur de champ, etc.), l'application est conçue pour enregistrer l'erreur d'insertion et continuer.

Chaque insertion étant indépendante des autres, les transactions ne sont pas nécessaires pour l'intégrité de la base de données. Cependant, nous souhaitons les utiliser pour le gain de performance. Lorsque j'utilise les transactions, nous obtenons l'erreur suivante sur environ un commit sur cent.

This SqlTransaction has completed; it is no longer usable.
   at System.Data.SqlClient.SqlTransaction.ZombieCheck()
   at System.Data.SqlClient.SqlTransaction.Commit()

Pour essayer de trouver la cause, j'ai placé des instructions de suivi à chaque opération de transaction afin de m'assurer que la transaction n'était pas fermée avant d'appeler le commit. J'ai confirmé que mon application ne fermait pas la transaction. J'ai ensuite relancé l'application en utilisant exactement les mêmes données d'entrée et elle a réussi.

Si j'éteins la connexion, l'échec persiste. Si je l'active à nouveau, il réussit. Cette activation/désactivation s'effectue via le fichier app.config sans qu'il soit nécessaire de recompiler.

Il est évident que l'acte d'enregistrement modifie le timing et le fait fonctionner. Cela indiquerait un problème de threading. Cependant, mon application n'est pas multithreadée.

J'ai vu une entrée dans la base de données MS indiquant qu'un bogue dans le cadre de .Net 2.0 pouvait causer des problèmes similaires ( http://support.microsoft.com/kb/912732 ). Cependant, la solution qu'ils ont fournie ne résout pas ce problème.

11voto

aef123 Points 193

Merci pour tous ces commentaires. J'ai travaillé avec quelqu'un de MSFT sur les forums MSDN pour comprendre ce qui se passe. Il s'avère que le problème est dû à l'échec de l'une des insertions en raison d'un problème de conversion de la date et de l'heure.

Le problème majeur est le fait que cette erreur apparaît s'il s'agit d'une erreur de conversion de date. En revanche, s'il s'agit d'une autre erreur, par exemple un champ trop long, le problème ne se pose pas. Dans les deux cas, je m'attendrais à ce que la transaction existe toujours pour que je puisse l'appeler en retour.

J'ai un exemple complet de programme pour reproduire ce problème. Si quelqu'un souhaite le voir ou voir l'échange avec MSFT, vous pouvez trouver le fil de discussion sur les newsgroups de MSFT dans microsoft.public.dotnet.framework.adonet sous le fil de discussion de l'erreur SqlTransaction.ZombieCheck.

7voto

Joe Points 60749

Difficile d'aider sans voir le code. Je suppose d'après votre description que vous utilisez une transaction pour commiter après chaque N insertions, ce qui améliorera les performances par rapport à commiter chaque insertion si N n'est pas trop grand.

Mais l'inconvénient est le suivant : si une insertion échoue, toutes les autres insertions dans le lot actuel de N seront annulées lorsque vous annulerez la transaction.

En général, vous devez disposer d'une transaction avant de fermer la connexion (qui annulera la transaction si elle n'a pas été validée). Le modèle habituel ressemble à ce qui suit :

using(SqlConnection connection = ...)
{
    connection.Open();
    using(SqlTransaction transaction = connection.BeginTransaction())
    {
        ... do stuff ...
        transaction.Commit(); // commit if all is successful
    } // transaction.Dispose will be called here and will rollback if not committed
} // connection.Dispose called here

Veuillez poster le code si vous avez besoin d'aide supplémentaire.

6voto

Bevan Points 20976

N'oubliez pas que votre application n'est pas le seul participant à la transaction : le serveur SQL est également impliqué.

L'erreur que vous citez :

Cette SqlTransaction s'est terminée ; elle n'est plus utilisable. at System.Data.SqlClient.SqlTransaction.ZombieCheck() à l'adresse System.Data.SqlClient.SqlTransaction.Commit()

n'indique pas que la transaction a été effectuée, mais seulement qu'elle est terminée.

Ma première suggestion est que votre serveur a tué la transaction parce qu'elle a pris trop de temps (temps de paroi écoulé) ou qu'elle est devenue trop importante (trop de changements ou trop de verrous).

Ma deuxième suggestion est de vérifier que vous nettoyez les connexions et les transactions de manière appropriée. Il est possible que vous rencontriez des problèmes parce que vous épuisez occasionnellement un pool de ressources avant que les choses ne soient automatiquement recyclées.

Par exemple, DbConnection met en œuvre IDisposable Vous devez donc vous assurer que vous nettoyez de manière appropriée, avec un en utilisant si vous le pouvez, ou en appelant Dispose() directement si vous ne pouvez pas. DbCommand est similaire, car il met également en œuvre IDisposable .

3voto

Alex Yakunin Points 3523

Cette exception est levée parce que la transaction de la base de données a déjà été annulée et que l'objet .NET qui la représente du côté client est déjà un "zombie".

Une explication plus détaillée est aquí . Ce poste explique comment écrire un code de retour de transaction correct pour de tels scénarios.

2voto

Charles Points 76

Selon ce post : http://blogs.msdn.com/b/dataaccesstechnologies/archive/2010/08/24/zombie-check-on-transaction-error-this-sqltransaction-has-completed-it-is-no-longer-usable.aspx

Une solution temporaire pourrait être d'essayer de rattraper l'opération de rollback ou de commit. Ce code sera donc suffisant pour arrêter le bogue à lancer :

    public static void TryRollback(this System.Data.IDbTransaction t)
    {
        try
        {
            t.Rollback();
        }
        catch (Exception ex)
        {
            // log error in my case
        }
    }

    public static void TryCommit(this System.Data.IDbTransaction t)
    {
        try
        {
            t.Commit();
        }
        catch (Exception ex)
        {
            // log error in my case
        }
    }

Vérifiez cet exemple à partir du site web msdn : http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqltransaction.aspx

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X