2009-07-28 10 views
17

Czy mogę zagnieżdżać transakcje w NHibernate i jak je wdrożyć? Używam SQL Server 2008, więc wsparcie jest zdecydowanie w DBMS.Jak wykonać zagnieżdżone transakcje w NHibernate?

Uważam, że jeśli spróbuję coś takiego:

using (var outerTX = UnitOfWork.Current.BeginTransaction()) 
{ 
    using (var nestedTX = UnitOfWork.Current.BeginTransaction()) 
    { 
     ... do stuff 
     nestedTX.Commit(); 
    } 

    outerTX.Commit(); 
} 

następnie przez czas chodzi o outerTX.Commit() transakcja stała się nieaktywna, a wyniki w ObjectDisposedException na AdoTransaction sesji.

Czy w związku z tym mamy utworzyć sesje zagnieżdżonych NHibernate? Czy jest jakaś inna klasa, której powinniśmy używać do zawijania transakcji (słyszałem o TransactionScope, ale nie jestem pewien co to jest)?

Używam teraz Ayende's UnitOfWork implementation (dzięki Sneal).

Przebaczam jakąkolwiek naiwność w tym pytaniu, wciąż jestem nowy w NHibernate.

Dzięki!

EDIT: Odkryłam, że można używać TransactionScope, takich jak:

using (var transactionScope = new TransactionScope()) 
{ 
    using (var tx = UnitOfWork.Current.BeginTransaction()) 
    { 
     ... do stuff 
     tx.Commit(); 
    } 

    using (var tx = UnitOfWork.Current.BeginTransaction()) 
    { 
     ... do stuff 
     tx.Commit(); 
    } 

    transactionScope.Commit(); 
} 

Jednak nie jestem podekscytowany, że wszystko to, co zaskoczy nas do programu SQL Server, a również znalazłem, że jeśli baza danych jest zdalna, musisz się martwić o włączoną obsługę MSDTC ... jeszcze jeden składnik, który się nie powiedzie. Zagnieżdżone transakcje są tak użyteczne i łatwe do wykonania w SQL, że zakładam, że NHibernate będzie miał jakiś sposób naśladowania tego samego ...

+1

Czy udało się znaleźć odpowiedź? Jak w końcu zagnieżdżać transakcje? – learning

+0

@ user281180, sort. Nie znalazłem sposobu, aby je faktycznie wykonać, ale możesz przybliżyć to doświadczenie. Blogowałem o tym tutaj: http://blog.constructionhive.com/2010/07/22/nested-transactions-and-nhibernate/ – Gavin

Odpowiedz

1

Ta implementacja nie obsługuje zagnieżdżania, jeśli chcesz zagnieżdżać, użyj Ayende's UnitOfWork implementation. Innym problemem związanym z implementacją, z której korzystasz (przynajmniej w przypadku aplikacji internetowych), jest to, że utrzymuje instancję ISession w zmiennej statycznej.

Właśnie przepisałem nasz UnitOfWork wczoraj z tych powodów, pierwotnie był oparty na Gabriel's.

Nie używamy UnitOfWork.Current.BeginTransaction(), używamy UnitofWork.TransactionalFlush(), który tworzy oddzielną transakcję na samym końcu, aby opróżnić wszystkie zmiany na raz.

using (var uow = UnitOfWork.Start()) 
{ 
    var entity = repository.Get(1); 
    entity.Name = "Sneal"; 
    uow.TransactionalFlush(); 
} 
+0

Tak, mam wrażenie, że powinienem był używać Rhino Commons od samego początku ... Poszedłem z Gabrielem, dzięki czemu mogłem zorientować się, jak działa wzór Unit of Work, budując go od zera (i to naprawdę bardzo mi pomogło), ale może teraz czas na zabawę z dużymi chłopcami ... dzięki . – Gavin

+0

@Sneal: Wdrożyliśmy UnitOfWork Ayende przy użyciu Rhino.Commons i jest to dobre, ale wciąż nie jestem pewien, jak działają transakcje zagnieżdżone.Kod, który opisałem w moim oryginalnym pytaniu, działa dokładnie w taki sam sposób jak poprzednio (tzn. Obiekty transakcji wydają się być udostępniane). Czy możesz mi podać jakieś wskazówki? Dzięki. – Gavin

2

Od pewnego czasu walczę z tym. Mam zamiar zrobić kolejny krok.

Chcę zaimplementować transakcje w poszczególnych kontenerach usługowych - ponieważ to czyni je samowystarczalnymi - ale wtedy będzie w stanie zagnieździć kilka z tych metod obsługi w ramach większej transakcji iw razie potrzeby wycofać całą partię.

Ponieważ używam Rhino Commons zamierzam teraz spróbować refaktoryzacji przy użyciu metody With.Transaction. Zasadniczo pozwala nam to pisać kod tak, jakby transakcje były zagnieżdżone, choć w rzeczywistości jest tylko jeden.

Na przykład:

private Project CreateProject(string name) 
{ 
    var project = new Project(name); 
    With.Transaction(delegate 
    { 
     UnitOfWork.CurrentSession.Save(project); 
    }); 
    return project; 
} 

private Sample CreateSample(Project project, string code) 
{ 
    var sample = new Sample(project, code); 
    With.Transaction(delegate 
    { 
     UnitOfWork.CurrentSession.Save(sample); 
    }); 
    return sample; 
} 

private void Test_NoNestedTransaction() 
{ 
    var project = CreateProject("Project 1"); 
} 

private void TestNestedTransaction() 
{ 
    using (var tx = UnitOfWork.Current.BeginTransaction()) 
    { 
     try 
     { 
      var project = CreateProject("Project 6"); 
      var sample = CreateSample(project, "SAMPLE006", true); 
     } 
     catch 
     { 
      tx.Rollback(); 
      throw; 
     } 
     tx.Commit(); 
    } 
} 

W Test_NoNestedTransaction(), kreujemy sam projekt, bez kontekście większej transakcji. W tym przypadku w CreateSample zostanie utworzona i zatwierdzona nowa transakcja lub wycofana, jeśli wystąpi wyjątek.

W Test_NestedTransaction() tworzymy zarówno próbkę, jak i projekt. Jeśli coś pójdzie nie tak, chcemy, aby oba zostały wycofane. W rzeczywistości kod w CreateSample i CreateProject będzie działał tak, jakby nie było żadnych transakcji; jest to całkowicie zewnętrzna transakcja, która decyduje o wycofaniu lub zatwierdzeniu, i robi to na podstawie tego, czy zgłoszony zostanie wyjątek. Naprawdę dlatego używam ręcznie utworzonej transakcji dla zewnętrznej transakcji; więc mamy kontrolę nad tym, czy zatwierdzić lub wycofać, a nie tylko domyślnie w przypadku wyjątku-wycofania-else-commit.

Można osiągnąć to samo bez Rhino.Commons umieszczając całe mnóstwo tego typu rzeczy za pośrednictwem kodu:

if (!UnitOfWork.Current.IsInActiveTransaction) 
{ 
    tx = UnitOfWork.Current.BeginTransaction(); 
} 

_auditRepository.SaveNew(auditEvent); 
if (tx != null) 
{ 
    tx.Commit(); 
} 

... i tak dalej. Ale pomimo tego, że trzeba tworzyć anonimowych delegatów, robi się to całkiem wygodnie.

Zaletą takiego podejścia do używania TransactionScope s (poza zależnością od MSDTC) jest to, że powinien istnieć tylko jeden spłukiwany do bazy danych w ostatnim zatwierdzeniu transakcji zewnętrznej, niezależnie od tego, ile metod zostało nazwanych pomiędzy. Innymi słowy, nie musimy zapisywać niezatwierdzonych danych w bazie danych, ponieważ zawsze je zapisujemy w lokalnej pamięci NHibernate.

Krótko mówiąc, to rozwiązanie nie zapewnia całkowitej kontroli nad transakcjami, ponieważ nie wykorzystuje więcej niż jednej transakcji. Sądzę, że mogę to zaakceptować, ponieważ transakcje zagnieżdżone i tak nie są w żadnym wypadku powszechnie wspierane w każdym DBMS. Ale teraz mogę przynajmniej napisać kod, nie martwiąc się, czy jesteśmy już w transakcji, czy nie.

13

Sesje NHibernate nie obsługują transakcji zagnieżdżonych.

Poniższy test jest zawsze prawdziwe w wersji 2.1.2:

var session = sessionFactory.Open(); 
var tx1 = session.BeginTransaction(); 
var tx2 = session.BeginTransaction(); 
Assert.AreEqual(tx1, tx2); 

Trzeba owinąć go w TransactionScope do obsługi transakcji zagnieżdżonych.

MSDTC musi być włączony lub dostaniesz błąd:

{"Network access for Distributed Transaction Manager (MSDTC) has been disabled. Please enable DTC for network access in the security configuration for MSDTC using the Component Services Administrative tool."}

+0

Czy to oznacza, że ​​nawet jeśli utworzę zagnieżdżone transakcje w NHibernate, będą one traktowane jako jedno zewnętrzne połączenie? –

2

Jak sugeruje Satish transakcje zagnieżdżone nie są obsługiwane w NHibernate. Nie natknąłem się na scenariusze, w których były potrzebne transakcje zagnieżdżone, ale z pewnością miałem problemy, gdy musiałem ignorować tworzenie transakcji, jeśli inne były już aktywne w innych jednostkach pracy.

Link blog poniżej stanowi realizację przykład dla NHibernate, ale powinien również pracować na serwerze SQL: http://rajputyh.blogspot.com/2011/02/nested-transaction-handling-with.html