2010-05-26 13 views
31

J'ai un problème et tous les articles ou exemples que j'ai trouvés ne semblent pas s'y intéresser.Impossible d'accéder à l'objet SqlTransaction pour l'annuler dans le bloc catch

Je souhaite effectuer des actions de base de données dans une transaction. Ce que je veux faire est très similaire à la plupart des exemples:

using (SqlConnection Conn = new SqlConnection(_ConnectionString)) 
{ 
    try 
    { 
     Conn.Open(); 
     SqlTransaction Trans = Conn.BeginTransaction(); 

     using (SqlCommand Com = new SqlCommand(ComText, Conn)) 
     { 
      /* DB work */ 
     } 
    } 
    catch (Exception Ex) 
    { 
     Trans.Rollback(); 
     return -1; 
    } 
} 

Mais le problème est que la SqlTransaction Trans est déclarée à l'intérieur du bloc try. Il n'est donc pas accessible dans le bloc catch(). La plupart des exemples font Conn.Open() et Conn.BeginTransaction() avant le bloc try, mais je pense que c'est un peu risqué, car les deux peuvent lancer plusieurs exceptions.

Ai-je tort, ou la plupart des gens ignorent-ils ce risque? Quelle est la meilleure solution pour pouvoir revenir en arrière, si une exception se produit?

+2

P.S. Êtes-vous sûr de vouloir retourner -1 (un code d'erreur) au lieu de lancer une exception? –

Répondre

55
using (var Conn = new SqlConnection(_ConnectionString)) 
{ 
    SqlTransaction trans = null; 
    try 
    { 
     Conn.Open(); 
     trans = Conn.BeginTransaction(); 

     using (SqlCommand Com = new SqlCommand(ComText, Conn, trans)) 
     { 
      /* DB work */ 
     } 
     trans.Commit(); 
    } 
    catch (Exception Ex) 
    { 
     if (trans != null) trans.Rollback(); 
     return -1; 
    } 
} 

ou vous pouvez aller encore plus propre et plus facile et utiliser ceci:

using (var Conn = new SqlConnection(_ConnectionString)) 
{ 
    try 
    { 
     Conn.Open(); 
     using (var ts = new System.Transactions.TransactionScope()) 
     { 
      using (SqlCommand Com = new SqlCommand(ComText, Conn)) 
      { 
       /* DB work */ 
      } 
      ts.Complete(); 
     } 
    } 
    catch (Exception Ex) 
    {  
     return -1; 
    } 
} 
+0

La deuxième version effectue-t-elle réellement une restauration lorsqu'une exception est générée? Edit: OK, après avoir lu la documentation, je l'ai vu. – Marks

+1

Dans votre premier exemple, n'avez-vous pas besoin de spécifier que la commande sql est associée à la transaction? comme 'using (SqlCommand Com = nouvelle SqlCommand (ComText, Conn, ** trans **))'? Ou est-ce inutile? est-il implicitement associé? –

+0

Oui, merci. Avec TransactionScope, ce n'est pas le cas, mais je l'ai omis de mon premier exemple. Edité en conséquence. –

6

utilisent ce

using (SqlConnection Conn = new SqlConnection(_ConnectionString)) 
{ 
    SqlTransaction Trans = null; 
    try 
    { 
     Conn.Open(); 
     Trans = Conn.BeginTransaction(); 

     using (SqlCommand Com = new SqlCommand(ComText, Conn)) 
     { 
      /* DB work */ 
     } 
    } 
    catch (Exception Ex) 
    { 
     if (Trans != null) 
      Trans.Rollback(); 
     return -1; 
    } 
} 

BTW - Vous n'avez pas commis en cas de traitement réussi

1

Échantillons Microsoft, placez le début trans à l'extérieur du try/catch see this msdn link. Je suppose que la méthode BeginTransaction devrait soit lancer une exception OU commencer une transaction mais jamais les deux (bien que la documentation ne dise pas que c'est impossible).

Cependant, vous pouvez être mieux d'utiliser TransactionScope qui gère beaucoup de (pas) de levage lourd pour vous: this link

3
using (SqlConnection Conn = new SqlConnection(_ConnectionString)) 
{ 
    try 
    { 
     Conn.Open(); 
     SqlTransaction Trans = Conn.BeginTransaction(); 

     try 
     { 
      using (SqlCommand Com = new SqlCommand(ComText, Conn)) 
      { 
       /* DB work */ 
      } 
     } 
     catch (Exception TransEx) 
     { 
      Trans.Rollback(); 
      return -1; 
     } 
    } 
    catch (Exception Ex) 
    { 
     return -1; 
    } 
} 
+0

Bien qu'il y ait plus à coder, cela fournit la meilleure granularité pour pouvoir déterminer pourquoi chaque étape échouerait. Cependant, notez que le SqlCommand doit être associé à la transaction. – JWilliams

8

Je n'aime pas taper les types et la définition des variables à NULL, donc :

try 
{ 
    using (var conn = new SqlConnection(/* connection string or whatever */)) 
    { 
     conn.Open(); 

     using (var trans = conn.BeginTransaction()) 
     { 
      try 
      { 
       using (var cmd = conn.CreateCommand()) 
       { 
        cmd.Transaction = trans; 
        /* setup command type, text */ 
        /* execute command */ 
       } 

       trans.Commit(); 
      } 
      catch (Exception ex) 
      { 
       trans.Rollback(); 
       /* log exception and the fact that rollback succeeded */ 
      } 
     } 
    } 
} 
catch (Exception ex) 
{ 
    /* log or whatever */ 
} 

Et si vous vouliez passer à MySQL ou à un autre fournisseur, il vous suffirait de modifier 1 ligne.

0
SqlConnection conn = null; 
SqlTransaction trans = null; 

try 
{ 
    conn = new SqlConnection(_ConnectionString); 
    conn.Open(); 
    trans = conn.BeginTransaction(); 
    /* 
    * DB WORK 
    */ 
    trans.Commit(); 
} 
catch (Exception ex) 
{ 
    if (trans != null) 
    { 
     trans.Rollback(); 
    } 
    return -1; 
} 
finally 
{ 
    if (conn != null) 
    { 
     conn.Close(); 
    } 
}