2010-04-05 3 views
2

Quelle est la meilleure façon d'implémenter la gestion des erreurs pour un Rollback SqlTransaction déjà existant dans une clause catch? Mon code est à peu près comme ceci:Comment intercepter l'exception sur RollBack

using (SqlConnection objSqlConn = new SqlConnection(connStr)) { 
    objSqlConn.Open(); 

    using (SqlTransaction objSqlTrans = objSqlConn.BeginTransaction()) { 
    try { 
     // code 
     // more code 
     // and more code 
    } 
    catch (Exception ex) { 
     // What happens if RollBack() has an exception? 
     objSqlTrans.Rollback(); 
     throw ex; 
    } 
    } 
} 

Je crois que ma demande avait une exception dans le bloc d'essai, ce qui a été pris dans le bloc catch, puis la baisse de prix a été tentée. Cependant, l'erreur que je vois indique quelque chose au sujet d'un SqlTransaction.ZombieCheck(), qui me fait me demander si le RollBack() a jeté une exception aussi bien. Alors, ai-je besoin d'implémenter un type de gestion des erreurs sur le RollBack()? Comment est-ce que je fais cela et réussis à conserver l'exception qui a placé l'exécution dans le bloc catch en premier lieu?

EDIT - Tout mon code:

using (SqlConnection objSqlConn = new SqlConnection(connStr)) { 

    objSqlConn.Open(); 

    // Begin Transaction 
    using (SqlTransaction objSqlTrans = objSqlConn.BeginTransaction()) { 

     try { 
      // Create file in db (which in turn creates it on disk according to where the 
      // ...FileStream points) 
      SqlCommand objSqlCmd = new SqlCommand("usp_FileAdd", objSqlConn, objSqlTrans); 
      objSqlCmd.CommandType = CommandType.StoredProcedure; 

      // Sql parameter - report name 
      SqlParameter objSqlParam1 = new SqlParameter("@ObjectID", SqlDbType.Int); 
      objSqlParam1.Value = objID; 

      // Sql out parameter - returns the file path 
      SqlParameter objSqlParamOutput = new SqlParameter("@filepath", SqlDbType.VarChar, -1); 
      objSqlParamOutput.Direction = ParameterDirection.Output; 

      // Add Sql parameters to command obj 
      objSqlCmd.Parameters.Add(objSqlParam1); 
      objSqlCmd.Parameters.Add(objSqlParamOutput); 

      // Execute command object 
      objSqlCmd.ExecuteNonQuery(); 

      // Path to the FileStream 
      string path = objSqlCmd.Parameters["@filepath"].Value.ToString(); 

      // Reset command object to get FileStream 
      objSqlCmd = new SqlCommand(
       "SELECT GET_FILESTREAM_TRANSACTION_CONTEXT()", 
       objSqlConn, 
       objSqlTrans); 

      // Execute command object 
      Object obj = objSqlCmd.ExecuteScalar(); 

      if (obj != DBNull.Value) { 
       // Byte array representing the FileStream 
       byte[] fsBytes = (byte[])obj; 

       SqlFileStream sqlFS = new SqlFileStream(path, fsBytes, FileAccess.Write); 

       using (FileStream fs = fi.OpenRead()) { 
        //byte[] b = new byte[1024]; 
        byte[] b = new byte[4096]; 
        int read; 

        fs.Seek(0, SeekOrigin.Begin); 

        while ((read = fs.Read(b, 0, b.Length)) > 0) { 
         sqlFS.Write(b, 0, read); 
        } 
       } 

       sqlFS.Close(); 
      } 

      // Commit the transaction 
      objSqlTrans.Commit(); 
     } 
     catch (Exception ex) { 
      objSqlTrans.Rollback(); 
      throw ex; 
     } 
    } 
} 

Répondre

1

Vous avez déjà

using (SqlTransaction objSqlTrans = objSqlConn.BeginTransaction()) 

Cela entraînera l'opération à laminer en arrière quand il le bloc à l'aide se termine si elle n'a pas été engagé. Donc, je retirerais complètement le bloc d'arrêt. Pour ce qui se passe quand la restauration échoue, je commencerais par reconnaître que c'est une très mauvaise situation et suivre les conseils d'Eric Lippert sur un problème similaire. here

+0

Je pense que vous l'avez peut-être cloué. J'ai trouvé une autre question sur SO qui traitait de l'utilisation de blocs pour SqlTransaction, et j'ai remarqué que dans leur exemple ils n'utilisent pas non plus le Rollback. Je ne pense pas que ma question soit un double de celui-là. http://stackoverflow.com/questions/1127830/why-use-a-using-statement-with-a-sqltransaction – Jagd

+0

ok la restauration va tenter sans l'explicite, mais conservera-t-elle l'exception interne au lieu de lancer une exception n'a pas la cause la plus probable? – Maslow

+0

@Maslow ce qui est dans l'exception ne sera pas affecté par l'instruction using. Donc, s'il y a une exception interne fournie par SqlCommand.ExecuteScalar, elle sera conservée. –

1

Ce fragment doit se lire comme ceci:

using (SqlConnection objSqlConn = new SqlConnection(connStr)) { 
objSqlConn.Open(); 

using (SqlTransaction objSqlTrans = objSqlConn.BeginTransaction()) { 
    try { 
    // code 
    // more code 
    // and more code 
    } 
    catch (Exception ex) { 
    // What happens if RollBack() has an exception? 
    try { 
     objSqlTrans.Rollback(); 
    } catch (Exception ex2) { 
     /* can't roll back -- db gone? db will do it for us since we didn't commit. */ 
    } 
    throw; 
    } 
} 
} 

EDIT: Venez à penser qu'il n'y a pas besoin de l'ensemble try/catch Dans ce cas particulier, la fermeture d'une connexion avec une transaction non validée annule la transaction, de sorte que le bloc peut ressembler à ceci:

using (SqlConnection objSqlConn = new SqlConnection(connStr)) { 
objSqlConn.Open(); 

using (SqlTransaction objSqlTrans = objSqlConn.BeginTransaction()) { 
    // code 
    // more code 
    // and more code 
} 
} 
+0

Je pense que vous avez raté le mot-clé catch dans ce bloc de 2e essai. Une chose que je suis confus à propos de - si Rollback() fonctionne très bien et n'a pas d'erreur, alors je ne verrai jamais l'exception originale puisque le jet existe seulement dans le 2ème bloc catch, non? – Jagd

+0

Correction des sauts de ligne pour rendre évident que le lancer est à la fin du premier bloc de capture. – Joshua