2008-12-04 17 views
9

Aquí está la arquitectura actual de mi código fuente de ámbito de transacción. El tercer inserto arroja una excepción .NET (No es una Excepción de SQL) y no está retrotrayendo las dos instrucciones de inserción anteriores. ¿Que estoy haciendo mal?TransactionScope no retrotraer transacción

EDIT: Eliminé el try/catch de insert2 e insert3. También eliminé la utilidad de manejo de excepciones de insert1 try/catch y puse "throw ex". Todavía no revierte la transacción.

EDIT 2: He añadido el try/catch en el método Insert3 y simplemente pongo un "tiro" en la instrucción catch. Todavía no revierte la transacción.

ACTUALIZACIÓN: Basándose en la información que he recibido, la clase "SqlHelper" está utilizando el objeto SqlConnection para establecer una conexión con la base de datos, a continuación, crea un objeto SqlCommand, establezca la propiedad CommandType a "StoredProcedure" y llama a la Método ExecuteNonQuery de SqlCommand.

Tampoco agregué el Enlace de transacción = Desvincular explícitamente a la cadena de conexión actual. Añadiré eso durante mi próxima prueba.

public void InsertStuff() 
{ 
    try 
    { 
     using(TransactionScope ts = new TransactionScope()) 
     { 
      //perform insert 1 
      using(SqlHelper sh = new SqlHelper()) 
      { 
       SqlParameter[] sp = { /* create parameters for first insert */ }; 

       sh.Insert("MyInsert1", sp); 
      } 

      //perform insert 2 
      this.Insert2(); 

      //perform insert 3 - breaks here!!!!! 
      this.Insert3(); 

      ts.Complete();    
     } 
    } 
    catch(Exception ex) 
    { 
     throw ex; 
    } 
} 

public void Insert2() 
{ 
    //perform insert 2 
    using(SqlHelper sh = new SqlHelper()) 
    { 
     SqlParameter[] sp = { /* create parameters for second insert */ }; 

     sh.Insert("MyInsert2", sp); 
    } 
} 

public void Insert3() 
{ 
    //perform insert 3 
    using(SqlHelper sh = new SqlHelper()) 
    { 
     SqlParameter[] sp = { /*create parameters for third insert */ }; 

     sh.Insert("MyInsert3", sp); 
    } 
} 
+0

No quiero poner en duda sus habilidades de desarrollo, etc. pero, ¿cómo está probando que la transacción se ha revertido? ¿Es posible que la transacción funcione correctamente pero está malinterpretando los resultados? ¿Tal vez está pasando algo más y nosotros/ustedes están ladrando el árbol equivocado? –

+0

Espero que esto ayude: http://stackoverflow.com/questions/28191333/error-en-ambient-transaction-doesnt-rollback-the-transaction/28258935#28258935 –

Respuesta

6

Parece que está capturando la excepción en Insert3() por lo que su código continúa después de la llamada. Si desea que se retrotraiga, deberá permitir que la excepción suba al bloque try/catch en la rutina principal para que nunca se llame a la instrucción ts.Complete().

+0

Por lo tanto, debería eliminar la declaración try/catch de la inserción 2 y 3? –

+0

Sí. O vuelva a lanzar la excepción u otra excepción. – tvanfosson

+0

sí, no está manejando la excepción que la parte llamante continúa ... también ... ¿están sus transacciones declaradas en el sqlHelper? ... entrego un problema una vez donde entregué una transacción declarada en mi ayudante y tuve para eliminarlo –

1

Una reversión implícita solo se producirá si se cierra el uso sin llamar a ts.complete. Debido a que está manejando la excepción en Insert3(), la excepción nunca causa una instrucción using para salir.

Cualquiera de volver a lanzar la excepción o notificar a la persona que llama que se necesita un rollback (hacer cambiar la firma de Insert3() a bool Insert3()?)

+0

Eso explicaría por qué cuando se realiza la depuración y toca detener antes de .Complete() se llama la transacción no se retrotrae (porque mientras .Complete() no se llama ni es .Disponer()). Leí varias respuestas y usted fue la única persona que mencionó esto. +1 –

1

(basado en la versión editada que no tragar excepciones)

¿Cuánto tardan las operaciones? Si alguno de ellos tiene una ejecución muy larga, es posible que la característica Transaction Binding error le haya picado, es decir, que la conexión se haya desconectado. Intente agregar Transaction Binding=Explicit Unbind a la cadena de conexión.

+0

He actualizado la pregunta según su respuesta –

+0

Sin dados. Agregué Explicit Unbind a la cadena de conexión y todavía no se revierte –

23

También me he encontrado con un problema similar. Mi problema ocurrió porque la SqlConnection que utilicé en mis SqlCommands ya estaba abierta antes de que se creara TransactionScope, por lo que nunca se enlistó en TransactionScope como transacción.

¿Es posible que la clase SqlHelper está reutilizando una instancia de SQLConnection que está abierta antes de introducir el bloque de TransactionScope?

+0

Gracias - resolvió mi problema. ;) – MojoDK

0

No veo su clase de ayuda, pero retrocede en el alcance de la transacción si no llama a la instrucción completa incluso si obtiene un error del código .NET. Copié un ejemplo para ti. Puede estar haciendo algo mal en la depuración. Este ejemplo tiene un error en el código .net y un bloque catch similar al tuyo.

private static readonly string _connectionString = ConnectionString.GetDbConnection(); 

    private const string inserttStr = @"INSERT INTO dbo.testTable (col1) VALUES(@test);"; 

     /// <summary> 
     /// Execute command on DBMS. 
     /// </summary> 
     /// <param name="command">Command to execute.</param> 
     private void ExecuteNonQuery(IDbCommand command) 
     { 
      if (command == null) 
       throw new ArgumentNullException("Parameter 'command' can't be null!"); 

      using (IDbConnection connection = new SqlConnection(_connectionString)) 
      { 
       command.Connection = connection; 
       connection.Open(); 
       command.ExecuteNonQuery(); 
      } 
     } 

     public void FirstMethod() 
     { 
      IDbCommand command = new SqlCommand(inserttStr); 
      command.Parameters.Add(new SqlParameter("@test", "Hello1")); 


       ExecuteNonQuery(command); 

     } 

     public void SecondMethod() 
     { 
      IDbCommand command = new SqlCommand(inserttStr); 
      command.Parameters.Add(new SqlParameter("@test", "Hello2")); 


       ExecuteNonQuery(command); 

     } 

     public void ThirdMethodCauseNetException() 
     { 
      IDbCommand command = new SqlCommand(inserttStr); 
      command.Parameters.Add(new SqlParameter("@test", "Hello3")); 


       ExecuteNonQuery(command); 
      int a = 0; 
      int b = 1/a; 

     } 

    public void MainWrap() 
    { 


     TransactionOptions tso = new TransactionOptions(); 
     tso.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted; 
     //TransactionScopeOption.Required, tso 
     try 
     { 
      using (TransactionScope sc = new TransactionScope()) 
      { 
       FirstMethod(); 
       SecondMethod(); 
       ThirdMethodCauseNetException(); 
       sc.Complete(); 
      } 
     } 
     catch (Exception ex) 
     { 
      logger.ErrorException("eee ",ex); 

     } 
    } 

Si desea depurar sus transacciones, puede utilizar este script para ver los bloqueos ya la espera de estado, etc.

SELECT 
request_session_id AS spid, 
CASE transaction_isolation_level 
WHEN 0 THEN 'Unspecified' 
WHEN 1 THEN 'ReadUncomitted' 
WHEN 2 THEN 'Readcomitted' 
WHEN 3 THEN 'Repeatable' 
WHEN 4 THEN 'Serializable' 
WHEN 5 THEN 'Snapshot' END AS TRANSACTION_ISOLATION_LEVEL , 
resource_type AS restype, 
resource_database_id AS dbid, 
DB_NAME(resource_database_id) as DBNAME, 
resource_description AS res, 
resource_associated_entity_id AS resid, 
CASE 
when resource_type = 'OBJECT' then OBJECT_NAME(resource_associated_entity_id) 
ELSE 'N/A' 
END as ObjectName, 
request_mode AS mode, 
request_status AS status 
FROM sys.dm_tran_locks l 
left join sys.dm_exec_sessions s on l.request_session_id = s.session_id 
where resource_database_id = 24 
order by spid, restype, dbname; 

Verá un SPID para dos llamadas a métodos antes de llamar al método de excepción.

two calls before exception

nivel de aislamiento predeterminado es serializable. You can read more about locks and transactions here

Cuestiones relacionadas