52 votes

Pourquoi utiliser une instruction using avec une SqlTransaction ?

J'ai rencontré quelques problèmes concernant une SqlTransaction que j'utilise dans mon code. En cherchant sur Google, j'ai vu que de nombreuses personnes utilisaient une instruction using avec une SqlTransaction.

Quel est l'avantage et/ou la différence d'utiliser ce type d'instruction avec une SqlTransaction ?

using (SqlConnection cn = new SqlConnection())
{
     using (SqlTransaction tr = cn.BeginTransaction())
     {
      //some code
      tr.Commit();
     }
}

Actuellement, mon code ressemble à ceci :

SqlConnection cn = new SqlConnection(ConfigurationManager.AppSettings["T3"]);
cn.Open();
SqlTransaction tr = cn.BeginTransaction();

try
{
     //some code
     tr.Commit();
     cn.Close();
}
catch(Exception ex)
{
      tr.Rollback();
      cn.Close();
      throw ex;
}

Quel est l'avantage d'une méthode sur l'autre ?

6 votes

Pour "moins" d'imbrication, je crois que vous pouvez sauter la première paire de crochets. (ou est-ce des accolades...)

62voto

John Saunders Points 118808

A using devrait être utilisée chaque fois que vous créez une instance d'une classe qui implémente IDisposable dans le cadre d'un bloc . Il garantit que le Dispose() sera appelée sur cette instance, qu'une exception soit levée ou non.

En particulier, votre code n'attrape que les exceptions gérées, puis détruit le cadre de la pile en lançant une nouvelle exception au lieu de relancer celle qui existe.

La façon correcte de procéder est la suivante :

using (SqlConnection cn = new SqlConnection(ConfigurationManager.AppSettings["T3"])) {
    cn.Open();
    using (SqlTransaction tr = cn.BeginTransaction()) {
        //some code
        tr.Commit();
    }
}

Notez que si votre classe possède des membres d'instance de types qui mettent en œuvre l'option IDisposable alors votre classe doit implémenter IDisposable lui-même, et dispose de ces membres pendant sa propre période d'activité. Dispose() appeler.

3 votes

....parce qu'elle garantit que la méthode "Dispose" de l'interface IDisposble mise en œuvre sera appelée - quoi qu'il arrive dans votre code.

3 votes

+1, généralement correct, mais vous ne pouvez pas toujours utiliser une instruction using, il faut parfois implémenter soi-même IDisposable. Je dirais plutôt : "Chaque fois que possible", ce qui signifie "chaque fois que vous créez une instance d'IDisposable utilisée uniquement au sein d'un bloc" ou quelque chose comme ça.

1 votes

C'est logique. J'ai également corrigé mon exception. Merci de me l'avoir signalé.

30voto

Ken Keenan Points 5173

La raison en est que l'objet SqlTransaction revient en arrière dans sa méthode Dispose() s'il n'a pas été explicitement validé (par exemple, si une exception est levée). En d'autres termes, cela a le même effet que votre code, juste un peu plus propre.

3 votes

Confirmé par la décompilation. Appelle this.Rollback() sur this.Dispose().

7 votes

En fait, le fait que Rollback soit appelé ou non à l'intérieur de Dispose() dépend de l'implémentation du pilote que vous utilisez (cf. msdn.microsoft.com/fr/us/library/bf2cw321(v=vs.110).aspx ). Les implémenteurs de pilotes sont censés appeler Rollback, mais Microsoft recommande de ne pas compter dessus. Donc, si vous savez que le seul pilote que vous utiliserez appelle Rollback dans Dispose(), vous êtes en sécurité. Sinon, il est plus sûr de l'appeler explicitement.

16voto

heavyd Points 8845

L'utilisation fait essentiellement la même chose que vous, à l'exception d'un bloc final au lieu d'attraper toutes les exceptions :

using (SqlConnection cn = new SqlConnection())
{
     using (SqlTransaction tr = cn.BeginTransaction())
     {
      //some code
      tr.Commit();
     }
}

est la même chose que, juste beaucoup moins de code :)

{
    SqlConnection cn = null;
    try
    {
       cn = new SqlConnection();
       {
           SqlTransaction tr = null;
           try
           {
               tr = cn.BeginTransaction())

               //some code
               tr.Commit();
            }
            finally
            {
                if(tr != null && tr is IDisposable)
                {
                    tr.Dispose();
                }
            }
        }
    }
    finally
    {
        if(cn != null && cn is IDisposable)
        {
            cn.Dispose();
        }
    }
}

0 votes

Vous avez en fait un jeu d'accolades supplémentaire dans votre premier exemple : vous pouvez imbriquer des déclarations d'utilisation sans créer un nouveau bloc, par exemple using (x = new X) using (y = new Y) { }

3 votes

Pour être explicite, c'est pour la même raison que nous mettons toujours des accolades autour de toutes nos instructions if, même si elles sont d'une seule ligne.

2 votes

Vous devez également faire cn.Open(); avant using (SqlTransaction tr ... . Sinon, vous recevrez une InvalidOperationException. Ou est-ce que quelque chose m'échappe ?

9voto

Joel Coehoorn Points 190579

En fin de compte, using est juste un raccourci pour un motif. Mais c'est un très utile et un raccourci utile, car il permet de s'assurer que vous implémentez le modèle correctement et de le faire avec moins de code.

Dans ce cas, vous n'avez pas mis en œuvre le modèle correctement. Que se passe-t-il dans votre code si l'appel à tr.RollBack() lance également une exception ?

4voto

Scott Ivey Points 19577

Le site en utilisant l'énoncé ferme et dispose de votre connexion et de votre transaction pour vous. C'est l'équivalent d'avoir un bloc final sur votre try/catch qui fait la disposition.

Vous pourriez aussi condenser les blocs d'utilisation un peu comme ceci...

using (SqlConnection cn = new SqlConnection())
using (SqlTransaction tr = cn.BeginTransaction())     
{
      //some code
      tr.Commit();
}

ce qui revient à peu près au même que :

SqlConnection cn = null;
SqlTransaction tr = null;
try
{
    cn = new SqlConnection());
    tr = cn.BeginTransaction());

    //some code
    tr.Commit();
}
finally
{
    if (cn != null)
        cn.Dispose();
    if (tr != null)    
        tr.Dispose();
}

0 votes

Très proche, mais il y a aussi un bloc supplémentaire de portée anonyme là-dedans. Le code tel que vous l'avez ne compilera pas, car cn et tr sont hors de portée dans le bloc finally.

2 votes

@ZoHas, il y aura automatiquement un Rollback à Dispose(); s'il n'y a pas eu d'appel de Commit(); voir aussi

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