2009-03-14 9 views
30

Czy ktoś może mi dać szybki przegląd korzystania z TransactionScope z NHibernate? Czy muszę zrobić coś specjalnego z sesją/IEnlistmentNotification/etc. aby to zadziałało? Czy są jakieś pułapki, o które powinienem się martwić? Na przykład, mogę wymienić wszystkich moich transakcji Hibernacja:NHibernate z TransactionScope

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

z tym ?:

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

Odpowiedz

7

Mam testowanie tego, stosując różne sprzedawców i po prostu działa. Jeśli nie masz "scope.Complete()", transakcja zostanie wycofana. Może być konieczne, aby MSDTC obsługiwał te urządzenia, jeśli istnieje więcej niż jeden trwały zasób. W takim przypadku MSDTC automatycznie wykrywa otaczające transakcje ADO.NET i zarządza całą tą sprawą.

+3

Zły; domyślnie TransactionScope używa Lightweight Transaction Manager, który monitoruje promocję. Z MSDN: tak długo jak tylko jeden trwały menedżer zasobów jest zaangażowany, nie ma nic złego w tym, że dane źródło (takie jak Microsoft SQL Server 2005) zarządza transakcją. W takim przypadku LTM nie musi w ogóle zarządzać transakcją - jej rola powinna zostać zredukowana do monitorowania transakcji pod kątem potrzeby promocji. – Henrik

+1

Poza tym SqlConnection-s (jeśli to twój DB), są 1) połączone za kulisami, i 2) można otworzyć więcej niż jeden z jednego wątku bez promowania transakcji, o ile nie udostępniają transakcji. Aby promować transakcję, należy rozpocząć korzystanie z MSDTC. – Henrik

+0

Edytowane, dzięki za dodatkowe informacje. –

6

Powyższe działa poprawnie, pod warunkiem, że korzystasz z dostawcy połączenia, który obsługuje użycie lekkiego menedżera transakcji, takiego jak SQL Server 2005/2008.

Jeśli korzystasz z programu SQL Server 7/2000, wszystkie transakcje staną się transakcjami rozproszonymi, nawet jeśli trafisz tylko na jedną bazę danych/zasób. Prawdopodobnie nie jest to w większości przypadków pożądane i będzie kosztowne pod względem wydajności.

Tak więc, jeśli twój dostawca połączenia i serwer bazy danych są odpowiednie do użycia z TransactionScope.

1

Ponadto, jeśli używasz programu TransactionScope, uaktualnij do NHibernate 2.1. Dopiero w wersji 2.1 NH uzyskał dobrą integrację z TransactionScope.

+1

@Rohit - czy możesz podać bardziej szczegółowe informacje swoją odpowiedzią? –

1

Według Fabio Maulo w komentarzach dotyczących NH-2107:

Można używać TransactionScope i należy kontynuować przy użyciu transakcji NH zbytnio. Gdzie przeczytałeś, że użycie TransactionScope oznacza unikanie transakcji NH?

bym założyć, że jawne użycie transakcji NHibernate nie jest to konieczne, ale aparently to najlepsza praktyka

+0

Wciąż szukam autorytatywnej odpowiedzi na to pytanie. W wersji NH 3.0. Jawne użycie nie wydaje się konieczne - może z wyjątkiem sytuacji, w której auto-płukanie działa poprawnie. –

+0

Powoduje także problemy z SQL Compact (próby utworzenia zagnieżdżonej transakcji, co wydaje się być dla mnie błędem): http://stackoverflow.com/questions/8127735/how-can-i-use-transactionscope-with-sql -compact-4-0-and-nhibernate – Travis

+0

Aktualizuję tutaj link Andrew, w którym odbyła się ta dyskusja: https://nhibernate.jira.com/browse/NH-2107 –

18

Używam NHibernate 2.1 na chwilę, a po kilku problemów produkcyjnych i próbuje sporo odmian, mamy rozliczane w następujący sposób, zgodnie 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(); 
     } 

jak używamy MSMQ i WCF więc musieliśmy użyć transakcji otoczenia.

Okazało się, że nie używanie session.BeginTransaction() spowodowało wyciek połączenia. Stwierdziliśmy również, że ponowne użycie sesji po dokonaniu transakcji spowodowało stan wyścigu (nHibernate nie jest bezpieczny dla wątków, a na wątku tła występują DTSC Commits/Rollbacks).

+0

To nie działa, transakcja wewnętrzna jest zatwierdzony przed zakończeniem zakresu nadrzędnego, co oznacza, że ​​id zakończył się niepowodzeniem w zakresie nadrzędnym, transakcja nie została wycofana –

+0

Czy jesteś pewien? Testowałeś to? Z pamięci transakcja wewnętrzna nie jest "niezależna" od zakresu nadrzędnego. Transakcja tak naprawdę nie jest zatwierdzana do czasu 'scope.Complete()'. – Iain

+0

Hej, dzięki za odpowiedź, Rozumiem, że ten post jest dość stary. Spodziewałbym się, że to zadziała tak, jak pan powiedział, ale wydaje się, że tak nie jest. Przetestowaliśmy to, przyjrzyjmy się testom, które tu zrobiłem: https://gist.github.com/2759471, (przykro mi, nie miałem czasu na uproszczenie rzeczy związanych z sesją, więc jest trochę rozwlekły) –

4

Wierzę, że można zastąpić transakcji NHibernate z tym tak długo, jak szanować pewne ograniczenia, jak niektórzy już powiedzieli:

  • Użyj rozsądnie najnowsza wersja NHibernate (>=3.1).
  • Podstawowy dostawca danych ADO.NET musi obsługiwać TransactionScope (ok w przypadku SQL-Server, Oracle> = 10 w ODP.NET).
  • Utwórz sesję NH w ramach TransactionScope, aby upewnić się, że zostanie zarejestrowana.
  • Ręcznie ręcznie NHibernate flushing. Zobacz także here i here.
  • Przygotowanie do transakcji rozproszonych. Jeśli utworzysz wiele sesji w jednym zakresie, początkowo lokalna transakcja może zostać awansowana do transakcji rozproszonej. Widziałem, jak to się stało, nawet z tymi samymi ciągami połączeń w Oracle 11.2 DB, używając ODAC 11.2.0.3.20. W SQL-Server 2008 R2 nie promował. (BTW, można to zobaczyć, oglądając Transaction.Current.TransactionInformation.DistributedIdentifier, który jest zerowy dla transakcji lokalnych.) Podczas gdy transakcje rozproszone mają pewne zalety, są droższe i jest trochę dodatkowych problemów z ich konfiguracją. (Zobacz here jak to zrobić dla Oracle). Aby mieć pewność, że promocja nigdy nie będzie miała miejsca w Oracle, ustaw "Promotable Transaction = local" w ciągu połączenia. Spowoduje to utworzenie wyjątku, jeśli jakiś kod spróbuje to zrobić.

Mam nadzieję, że to wszystko ;-)

PS: dodałem szczegóły Oracle bo gdzie moja troska i inni mogą korzystać.

Powiązane problemy