2015-12-08 17 views
8

Attualmente sto utilizzando TransactionScope per gestire le transazioni nel mio livello dati, ma ho riscontrato problemi con transazioni nidificate e asincrono in cui la connessione sembra essere chiusa durante la transazione nidificata o la transazione viene promossa a MSDTC. Non ho trovato il problema esatto, ma dopo aver letto in giro sembra che questo scenario non sia particuarly well supportato e che dovrei usare Database.BeginTransaction().Comportamento transazioni nidificate in EF6

Il mio problema è che non riesco a trovare informazioni su come Database.BeginTransaction() funziona con le transazioni nidificate, in particolare nel mio scenario in cui sto volendo utilizzare la transazione ambientale piuttosto che crearne una nuova. Il mio sospetto è che non è destinato a funzionare in questo modo e se voglio gestire le transazioni nidificate dovrei astrarre la gestione delle transazioni per darmi più controllo.

Non volendo aggiungere strati inutili di astrazioni, volevo sapere se qualcuno ha esperienza in quest'area e potrebbe confermare il comportamento di Database.BeginTransaction() quando è annidato in un'altra transazione?

Ulteriori informazioni sul mio DAL: Sulla base di modello CQS, tendo a incapsulare Db codice relativo al comando o di query gestori, quindi un esempio semplificato/artificiosa di come si verifica questo nidificazione sarebbe:

public class AddBlogPostHandler 
{ 
    private readonly MyDbContext _myDbContext; 

    public AddBlogPostHandler(MyDbContext myDbContext) 
    { 
     _myDbContext = myDbContext; 
    } 

    public async Task ExecuteAsync(AddBlogPostCommand command) 
    { 
     using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) 
     { 
      // .. code to create and add a draft blog post to the context 
      await _myDbContext.SaveChangesAsync(); 

      var publishBlogPostCommand = new PublishBlogPostCommand(); 
      // ..set some variables on the PublishBlogPostCommand 
      await PublishBlogPostAsync(command); 

      scope.Complete(); 
     } 
    } 
} 

public class PublishBlogPostHandler 
{ 
    private readonly MyDbContext _myDbContext; 

    public PublishBlogPostHandler(MyDbContext myDbContext) 
    { 
     _myDbContext = myDbContext; 
    } 

    public async Task ExecuteAsync(PublishBlogPostCommand command) 
    { 
     using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) 
     { 
      // .. some code to do one set of update 
      await _myDbContext.SaveChangesAsync(); 

      // .. some other db updates that need to be run separately 
      await _myDbContext.SaveChangesAsync(); 

      scope.Complete(); 
     } 
    } 
} 

risposta

5

Ci non è un'operazione come transazioni nidificate nel senso che l'interno può eseguire il commit o il rollback in modo indipendente. Le transazioni nidificate mantengono solo un conteggio ref. All'ultimo commit otteniamo un commit fisico. Al primo rollback otteniamo un rollback fisico. Solo assicurandoti di esserne a conoscenza.

È importante evitare l'utilizzo di MSDTC. Questo è possibile sia con TransactionScope sia con BeginTransaction. Con il primo è necessario esplicitamente Open la connessione all'interno dell'oscilloscopio in modo che EF non apra nuove connessioni tutte le volte.

Come hai letto nel problema, questo è un difetto in EF (che L2S non aveva). Si prega di prendere il tempo di commentare il problema per assicurarsi che il team sia consapevole del fatto che i clienti si imbattono in questo problema.

in particolare nel mio scenario in cui sto volendo utilizzare la transazione ambientale piuttosto che crearne una nuova.

Questo è perfetto per TransactionScope. Penso che il tuo passaggio a BeginTransaction sia basato su un malinteso. Forse puoi chiarire nei commenti.

confermano il comportamento di Database.BeginTransaction() quando nidificato all'interno di un altro transazione

spiegato nel primo paragrafo.

Ulteriori informazioni sul mio DAL: Sulla base di modello CQS, tendo a incapsulare Db codice relativo al comando o di query gestori, in modo da un semplificato/esempio forzato di come questo nidificazione si verifica potrebbe essere:

Il codice sembra buono tranne la chiamata mancante db.Connection.Open() (come spiegato sopra).

Questo modello supporterà l'esecuzione di più query e comandi nella stessa transazione.Basta avvolgere un altro ambito intorno ad esso. Assicurati di non aprire le connessioni due volte, ad es. controllare conn.State prima di agire.

+0

Credo di chiedere davvero se una chiamata nidificata a "BeginTransaction" crea una nuova transazione (equivalente a "TransactionScopeOption.RequiresNew') o utilizza la transazione ambientale (Equivalente a" TransactionScopeOption.Required')? A parte la mia incapacità di farlo funzionare, il passaggio a 'BeginTransaction' era basato sulla sua raccomandazione per EF6 +. Sono aperto a provare a farlo funzionare con 'TransactionScope', ma se apro la connessione manualmente ottengo un'eccezione prima che la transazione esterna sia completata (transazione" completata ma non disposta ") –

+1

BeginTransaction è come Richiesto. Non c'è modo di avere più trans sulla stessa connessione. Pertanto, non può possibilmente comportarsi come RequiresNew. "Essendo la loro raccomandazione per EF6" Ho letto anche questo, ma penso che non sia stato fornito alcun ragionamento. Strano consiglio 'Ricevo un'eccezione prima che la transazione esterna sia completata. Immagino che dovresti investigare e correggere il bug. Perché abbandonare l'intero approccio se tutto ciò che devi fare è correggere un bug minore? – usr

+0

Ottimo, non sapevo che si potesse avere una sola transazione per connessione. Non potevo proprio "TransactionScope" lavorare nel mio scenario, così ho finito per scrivere una piccola astrazione, il che significava che avrei potuto avere scope di transazioni nidificate e comunque usare 'Database.BeginTransaction' per aprire alla transazione sottostante. Grazie per l'aiuto @ usr –