2010-04-30 20 views
26

Estoy tratando de anidar TransactionScopes (.NET 4.0) ya que anidaría Transacciones en SQL Server, sin embargo, parece que operan de manera diferente. Quiero que las transacciones de mi hijo puedan retrotraerse si fallan, pero permite que la transacción padre decida si comprometer/revertir toda la operación. El problema es cuando ocurre la primera finalización, la transacción se revierte. Me doy cuenta de que completar es diferente a comprometerse.Anulado/Ejecución de TransactionScope Rollback

Un ejemplo muy simplificado de lo que estoy tratando de hacer:

static void Main(string[] args) 
{ 
    using(var scope = new TransactionScope()) // Trn A 
    { 
     // Insert Data A 

     DoWork(true); 
     DoWork(false); 

     // Rollback or Commit 
    } 
} 

// This class is a few layers down 
static void DoWork(bool fail) 
{ 
    using(var scope = new TransactionScope()) // Trn B 
    { 
     // Update Data A 

     if(!fail) 
     { 
      scope.Complete(); 
     } 
    } 
} 

no puedo usar las opciones de suprimir o requiresNew como Trn B se basa en los datos insertados por Trn A. Si hago uso de esas opciones , Trn B está bloqueado por Trn A.

¿Alguna idea de cómo conseguiría que funcione, o si es posible utilizando el espacio de nombres System.Transactions?

Gracias

Respuesta

46

usted probablemente no va a gustar esta respuesta, pero ...

votación dentro de un ámbito anidado

Aunque un ámbito anidado puede unirse a la transacción ambiental del ámbito raíz, llamando al Complete en el ámbito anidado no tiene efecto en el ámbito raíz. Solo si todos los ámbitos desde el alcance raíz hasta el último ámbito anidado votan para confirmar la transacción, se comprometerá la transacción.

(De Implementing an Implicit Transaction using Transaction Scope)

La clase TransactionScope lamentablemente no proporciona ningún mecanismo (que yo sepa) para las unidades de trabajo segregación. Es todo o nada. Puede evitar que ocurra cualquier transacción en una unidad de trabajo específica utilizando TransactionScopeOption.Suppress, pero probablemente eso no es lo que desea, ya que entonces perdería atomicidad por lo que esté dentro de ese alcance.

Hay una sola transacción "ambiental" cuando utiliza un TransactionScope. Una vez que un TransactionScope se elimina o se recopila sin que se ejecute Complete, se revierte toda la transacción ambiental; eso es todo, se acabó el juego.

De hecho, SQL Server no es compatible con verdaderas transacciones anidadas en absoluto, a pesar de que es posible (aunque algo poco intuitivo) para lograr el mismo resultado final con el uso apropiado de SAVE TRAN declaraciones. Volver a implementar esta lógica como un Procedimiento almacenado (o varios de ellos) podría ser su mejor opción si requiere este comportamiento en particular.

+0

Gracias por eso, tenía miedo de una respuesta como esta. –

+0

No lo entendí: ¿por qué la respuesta es decepcionante? Robert quiere que cualquier alcance interno pueda retroceder, mientras que solo el más externo debería poder comprometerse. Una solución simple será: en cada ámbito, llame a complete() si no desea deshacer. Entonces, el alcance más externo tiene la opción de confirmar, solo si todos los internos eligen no retroceder. – Alireza

+3

@Alireza: el OP desea comprometer selectivamente ciertas transacciones internas mientras revierte todo * else * que no se ha comprometido explícitamente.Su "solución simple" es exactamente cómo funciona el 'TransactionScope' y está documentado que funciona y está claramente explicado por esta misma respuesta, pero en realidad no comprometerá los cambios realizados en ninguno de los ámbitos internos si el alcance externo no se completa. Eso simplemente no es posible usando 'System.Transactions'. – Aaronaught