2013-07-09 19 views
5

Stavamo cercando di eseguire alcuni controlli di integrità sullo stato del nostro database per ragioni diagnostiche, quindi abbiamo completato le nostre query ORM di modifica in TransactionScope accoppiato con una seconda query che ha eseguito la diagnostica - qualcosa di simile:TransactionScope wrapping chiamate ORM, TransactionStateAborted.CreateAbortingClone exception su second call

using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, _maxTimeout)) 
{ 
    ORM.DeleteItem(); 
    ORM.CheckIntegrity(); 
    scope.Complete(); 
} 

e 'un ORM arrotolato a mano, ed entrambe quelle chiamate finisce per fare la loro parte in un ambito di transazione annidata giù in fondo. In altre parole, quando si scava giù, DeleteItem() ha utilizzando (TransactionScope newScope = new TransactionScope (TransactionScopeOptions.Required, _maxTimeout) {...}

e CheckIntegrity() ha anche lo stesso.

Per la maggior parte funziona bene, ma ho incontrato una strana condizione: quando qualcuno inserisce alcuni dati non validi nella query, la chiamata DeleteItem() può generare un'eccezione. livello di stack sotto il wrapper Credo che sia stata lanciata anche l'eccezione prima del che arriva a nidificare TransactionScope.

Tuttavia, quando si arriva alla creazione dell'ambito nidificata nella chiamata CheckIntegrity(), viene generato un errore "Transaction was aborted" dal costruttore CreateAbortingClone. L'eccezione interna è nulla.

La maggior parte ogni altra menzione dell'interazione CreateAbortingClone ha a che fare con la promozione DTC (o il suo fallimento) e l'eccezione interna lo riflette.

Suppongo che l'eccezione di interruzione nella chiamata CheckIntegrity() sia dovuta al fatto che DeleteItem() ha generato un'eccezione, anche se è stata ingerita.

A) è un'inferenza corretta? TransactionScope è sensibile alle eccezioni generate, gestite o meno?

B) esiste un modo per rilevarlo prima di effettuare la chiamata a CheckIntegrity()? Voglio dire oltre a rifare il nostro ORM per far salire l'eccezione o aggiungere qualche altra bandiera globale?

Grazie Mark

+0

Rovistando un po 'più nel debugger, trovo che TransactionScope.expectedCurrent .InternalTransaction.State è TransactionStateAborted dopo la chiamata DeleteItem(), puntando la mia inferenza. Il problema è che tutti questi membri sono privati ​​... – user1664043

+0

Trovato doc msdn che dice "Se si verifica un'eccezione all'interno di TransactionScope, la transazione viene contrassegnata come incoerente e viene abbandonata." ma ci sono molte cose non dette tra le righe - come non sembra importare se l'eccezione è gestita da un paio di livelli di chiamata al di sotto dell'oscilloscopio e impedisce che eventuali nuovi ambiti vengano nidificati successivamente. – user1664043

+1

Nota in un tempo di bottiglia - alla fine ho trovato System.Transactions.Transaction.Current.TransactionInformation.Status e ho calcolato che può essere usato per dire se eventuali eccezioni (gestite o non gestite) hanno rovinato la tua transazione di wrapping. Se si tratta di TransactionStatus.Aborted you're hosed. Mi è anche venuto in mente che è possibile utilizzare una transazione di wrapping a livello esterno per rilevare quando è stata generata un'eccezione. Alla tua chiamata più esterna, non devi aspettarti chiamate db consecutive in diversi livelli. Naturalmente sarebbe meglio progettare il tuo codice per non inghiottire eventi significativi. – user1664043

risposta

0

Io so solo come funziona con EF (Entity Framework)

using (var context = new MyContext(this._connectionString)) 
{ 

    using (var dbContextTransaction = context.Database.BeginTransaction()) 
    { 


    } 
} 

la transazione è legata al contesto. Non sono formativo sul modo in cui il tuo codice crea quella connessione, ma potrebbe essere un po 'di fantasia nel creare qualcosa.

Allora è meglio per avvolgere questo in un try/catch

try 
{ 
    // do-stuff 
    context.SaveChanges(); 
    //NB!!!!!! 
    //---------------------- 
    dbContextTransaction.Commit(); 
} 
catch (Exception ex) 
{ 
    dbContextTransaction.Rollback(); 
    //log why it was rolled back 
    Logger.Error("Error during transaction,transaction rollback", ex); 
} 

codice in modo finale sarà simile

using (var context = new MyContext(this._connectionString)) 
{ 

    using (var dbContextTransaction = context.Database.BeginTransaction()) 
    { 
     try 
     { 
       // do-stuff // 
       context.SaveChanges(); 
       /////////////////////// 
       //if any exception happen, changes wont be saved unless Commit is called 
       //NB!!!!!! 
       //---------------------- 
       dbContextTransaction.Commit(); 
     } 
     catch (Exception ex) 
     { 
       dbContextTransaction.Rollback(); 
       //log why it was rolled back 
       Logger.Error("Error during transaction,transaction rollback", ex); 
     } 

    } 
}