2009-03-14 11 views
30

Qualcuno può darmi una rapida panoramica sull'utilizzo di TransactionScope con NHibernate? Devo fare qualcosa di speciale con la sessione/IEnlistmentNotification/etc. per farlo funzionare? Ci sono delle insidie ​​di cui dovrei preoccuparmi? Ad esempio, è possibile sostituire tutte le mie operazioni di Hibernate:NHibernate with TransactionScope

var transaction = session.BeginTransaction(); 
try 
{ 
    // code 
    transaction.Commit(); 
} 
catch (Exception) 
{ 
    transaction.Rollback(); 
} 

con questo ?:

using (var scope = new TransactionScope()) 
{ 
    // code 
    scope.Complete(); 
} 

risposta

7

ho prove utilizzando diversi fornitori e funziona solo. Se non si dispone di "scope.Complete()", la transazione verrà ripristinata. Potrebbe essere necessario che MSDTC esegua le macchine interessate se esiste più di una risorsa duratura. In tal caso, MSDTC rileverà automaticamente le transazioni ADO.NET e gestirà il tutto.

+3

Sbagliato; per impostazione predefinita TransactionScope utilizza il Gestore transazioni leggero che monitora la promozione. Da MSDN: fino a quando non viene coinvolto un singolo gestore risorse durevole, non c'è nulla di male nel lasciare che la risorsa sottostante (come Microsoft SQL Server 2005) gestisca la transazione. In tal caso, l'LTM non ha bisogno di gestire effettivamente la transazione, il suo ruolo dovrebbe essere ridotto al monitoraggio della transazione per un bisogno di promozione. – Henrik

+1

Oltre a questo, SqlConnection-s (se questo è il tuo DB), sono 1) raggruppati dietro le quinte, e 2) puoi aprire più di uno da un singolo thread senza promuovere la transazione fintanto che non condividono la transazione. Promuovere la transazione significa iniziare a utilizzare MSDTC. – Henrik

+0

Modificato, grazie per le informazioni aggiuntive. –

6

Quanto sopra funziona OK a condizione che si stia utilizzando un provider di connessione che supporta l'utilizzo di un gestore transazioni leggero come SQL Server 2005/2008.

Se si utilizza SQL Server 7/2000, tutte le transazioni diventeranno transazioni distribuite anche se si colpisce solo un database/risorsa. Questo probabilmente non è ciò che si vorrebbe nella maggior parte dei casi e sarà costoso per quanto riguarda le prestazioni.

Quindi verificare se la combinazione di provider di connessione e server di database è adatta per l'uso con TransactionScope.

1

Inoltre, se si utilizza TransactionScope, eseguire l'aggiornamento a NHibernate 2.1. È solo con 2.1 che NH ha ottenuto una buona integrazione con TransactionScope.

+1

@Rohit: puoi fornire ulteriori dettagli con la tua risposta? –

1

Secondo Fabio Maulo nei commenti relativi alla NH-2107:

È possibile utilizzare TransactionScope e si dovrebbe continuare a utilizzare un'operazione di NH troppo. Dove hai letto che l'uso di TransactionScope significa evitare l'uso della transazione di NH?

avrei ipotizzato che l'uso esplicito di transazioni di NHibernate non è necessario, ma che verrà aggiustata che è meglio la pratica

+0

Sto ancora cercando una risposta autorevole a quella domanda. In NH 3.0, l'uso esplicito non sembra necessario, tranne forse per far funzionare correttamente l'autolavaggio. –

+0

Inoltre causa problemi con SQL Compact (tentativi di creare una transazione nidificata, che mi sembra un bug per me): http://stackoverflow.com/questions/8127735/how-can-i-use-transactionscope-with-sql -compact-4-0-and-nhibernate – Travis

+0

Aggiornare qui il collegamento di Andrew in cui è avvenuta questa discussione: https://nhibernate.jira.com/browse/NH-2107 –

18

Sono stato con NHibernate 2.1 per un po ', e dopo alcuni problemi di produzione e cercando un bel paio di varianti, abbiamo optato per il seguente metodo, come da Avoiding Leaking Connections With NHibernate And TransactionScope:

 using (var scope = new TransactionScope(TransactionScopeOption.Required)) 
     { 
      using (var session = sessionFactory.OpenSession()) 
      using (var transaction = session.BeginTransaction()) 
      { 
       // do what you need to do with the session 
       transaction.Commit(); 
      } 
      scope.Complete(); 
     } 

come stiamo usando MSMQ e WCF così abbiamo dovuto usare la transazione di ambiente.

Abbiamo rilevato che non utilizzare session.BeginTransaction() ha causato una perdita di connessione. Abbiamo anche riscontrato che riutilizzare una sessione dopo aver eseguito una transazione ha causato una condizione di competizione (nHibernate non è thread-safe e DTSC Commits/Rollbacks si verificano su un thread in background).

+0

Questo non sembra funzionare, la transazione interna è commesso prima che l'ambito genitore sia completato, questo significa che un errore sull'ambito genitore si verifica quando la transazione non viene ripristinata –

+0

Sei sicuro? L'hai provato? Dalla memoria la transazione interna non è "indipendente" dall'ambito genitore. La transazione non si impegna realmente fino a 'scope.Complete()'. – Iain

+0

Ehi, grazie per la risposta, capisco che questo post è piuttosto vecchio. Mi aspetto che questo agisca come hai detto, tuttavia non sembra essere il caso. L'abbiamo provato, dai un'occhiata ai test che ho fatto qui: https://gist.github.com/2759471, (scusate non ho avuto il tempo di semplificare la sessioneFactory stuff quindi è un po 'prolisso) –

4

credo è possibile sostituire le transazioni NHibernate con questo il tempo che rispetti alcuni vincoli come alcuni hanno già detto:

  • utilizzare una versione ragionevolmente recente NHibernate (>=3.1).
  • Il provider di dati ADO.NET sottostante deve supportare TransactionScope (ok per SQL-Server, Oracle> = 10 con ODP.NET).
  • Crea la tua sessione NH all'interno di TransactionScope per assicurarti che venga arruolato.
  • Gestire manualmente NHibernate flushing. Vedi anche here e here.
  • Prepararsi per le transazioni distribuite. Se si creano più sessioni in un ambito, la transazione locale iniziale potrebbe essere promossa a una transazione distribuita. Ho visto questo accadere anche con le stesse stringhe di connessione su un DB Oracle 11.2 usando ODAC 11.2.0.3.20. Su SQL Server 2008 R2 non ha promosso. (A proposito, si può vedere questo osservando Transaction.Current.TransactionInformation.DistributedIdentifier che è nullo per le transazioni locali.) Mentre le transazioni distribuite hanno alcuni vantaggi, sono più costose ed è un po 'più difficile impostarle. (Vedi here come fare questo per Oracle). Per assicurarti che la promozione non avvenga mai in Oracle, imposta " = local" nella stringa di connessione. Questo crea un'eccezione se qualche codice cerca di farlo.

Spero che sia tutto ;-)

PS: ho aggiunto i dettagli di Oracle perché dove la mia preoccupazione e altri potrebbero trarre beneficio.

Problemi correlati