W jaki sposób TransactionScope wycofuje transakcje?

100

Piszę test integracji, w którym wstawię wiele obiektów do bazy danych, a następnie sprawdzę, czy moja metoda pobiera te obiekty.

Moje połączenie z bazą danych odbywa się przez NHibernate ... a moja zwykła metoda tworzenia takiego testu polega na wykonaniu następujących czynności:

NHibernateSession.BeginTransaction();

//use nhibernate to insert objects into database
//retrieve objects via my method
//verify actual objects returned are the same as those inserted

NHibernateSession.RollbackTransaction();

Jednak niedawno dowiedziałem się o TransactionScope, który podobno może być użyty do tego właśnie celu ...

Oto przykładowy kod, który znalazłem :

public static int AddDepartmentWithEmployees(Department dept)
{

    int res = 0;

    DepartmentAdapter deptAdapter = new DepartmentAdapter();
    EmployeeAdapter empAdapter = new EmployeeAdapter();
    using (TransactionScope txScope = new TransactionScope())
    {

        res += deptAdapter.Insert(dept.DepartmentName);
        //Custom method made to return Department ID 
        //after inserting the department "Identity Column"
        dept.DepartmentID = deptAdapter.GetInsertReturnValue();
        foreach(Employee emp in dept.Employees)
        {

            emp.EmployeeDeptID = dept.DepartmentID;
            res += empAdapter.Insert(emp.EmployeeName, emp.EmployeeDeptID);

        }
        txScope.Complete();

    }
    return res;

}

Uważam, że jeśli nie dołączę wiersza txScope.Complete(), wstawione dane zostaną wycofane. Ale niestety ja nie rozumiem, jak to jest możliwe ... w jaki sposób txScopeobiekt zachować ścieżki deptAdapteri empAdapterobiektów oraz ich transakcji w bazie danych.

Czuję, że brakuje mi tu trochę informacji ... czy naprawdę jestem w stanie zastąpić moje wywołania BeginTransaction()i RollbackTransaction() otaczając mój kod za pomocą TransactionScope?

Jeśli nie, to jak TransactionScopedziała wycofywanie transakcji?

mezoid
źródło
Nigdy nie używałem NHibernate, ale może ten link ci pomoże.
Jeśli szukasz lepszego sposobu zarządzania sesjami NHibernate w celu grupowania operacji w transakcje, możesz przeczytać mój wpis na blogu na ten temat dotnetchris.wordpress.com/2009/01/27/ ...
Chris

Odpowiedzi:

108

Zasadniczo TransactionScope nie śledzi adaptera, tylko śledzi połączenia z bazą danych. Po otwarciu połączenia z bazą danych połączenia sprawdzą, czy istnieje transakcja otoczenia (zakres transakcji), a jeśli tak, zarejestrują się w niej. Przestroga, jeśli istnieje więcej połączeń z tym samym serwerem SQL, spowoduje to eskalację do transakcji rozproszonej.

Co się dzieje, ponieważ używasz bloku using, co do którego masz pewność, że dispose zostanie wywołane, nawet jeśli wystąpi wyjątek. Więc jeśli dispose zostanie wywołane przed txScope.Complete (), TransactionScope powie połączeniom, aby wycofały swoje transakcje (lub DTC).

JoshBerke
źródło
10
TransactionScope nie śledzi niczego poza bieżącą transakcją w wątku i modyfikuje ją w razie potrzeby w oparciu o model (wymaga, wymaga nowego itp.). Transakcja po prostu powiadamia wszystko, co się z nią wiąże, nie tylko połączenia z bazą danych.
casperOne
1
Myślę, że to nie do końca prawda. Zrobiłem częściowe prześledzenie kodu źródłowego TransactionScope i zobaczyłem również ten msdn.microsoft.com/en-us/library/ms172152(v=vs.90).aspx z napisem „Jeśli nie utworzył transakcji, zatwierdzenie następuje za każdym razem, gdy właściciel obiektu CommittableTransaction wywołuje Commit. W tym momencie Menedżer transakcji wywołuje menedżerów zasobów i informuje ich o zatwierdzeniu lub wycofaniu, w zależności od tego, czy metoda Complete została wywołana w obiekcie TransactionScope. " Źródło śledzenia również wskazuje na to zachowanie.
user44298
Uważam, że ten SO Q&A jest przydatny: IEnlistmentNotification
mungflesh
Wciąż niejasne dla mnie. Wydaje się, że obiekty z metodami wywoływanymi w zakresie transakcji muszą współpracować z mechanizmem Transakcji. Muszą szukać powiadomień o zatwierdzeniu / wycofaniu przez system i być w stanie wycofać się, ponieważ to oni wykonują wycofanie w razie potrzeby (jeśli usunięcie pliku zostało wykonane, oczywiście nie możemy magicznie go cofnąć, chyba że pewne środki ostrożności). Wydaje się również, że operacje serwera SQL są kooperatywne. Ale czy są jakieś inne obiekty współpracy w .Net? A jak napisać zajęcia oparte na współpracy? Dokumentacja?
min
54

TransactionScopeKlasa współpracuje z Transactionklasy , która jest gwint specyficzny.

Po TransactionScopeutworzeniu sprawdza, czy istnieje znak Transactiondla wątku; jeśli ktoś istnieje, to używa tego, w przeciwnym razie tworzy nowy i odkłada go na stos.

Jeśli używa istniejącego, po prostu zwiększa licznik wydań (ponieważ musisz wywołać Dispose ). W ostatnim wydaniu, jeśli Transactionnie został zatwierdzony , wycofuje całą pracę.

Jeśli chodzi o powód, dla którego klasy wydają się magicznie wiedzieć o transakcjach, pozostaje to jako szczegół implementacji dla tych klas, które chcą pracować z tym modelem.

Podczas tworzenia deptAdapterand emptAdapterinstancji, sprawdzają, czy nie jest obecna transakcja na gwincie (statyczne Currentwłasności w Transactionklasie). Jeśli tak, rejestruje się Transactionw sekwencji zatwierdzania / wycofywania zmian (która Transactionkontroluje i może być propagowana do różnych koordynatorów transakcji, takich jak jądro, dystrybucja itp.).

casperOne
źródło
4
Jak to działa z połączeniami SqlConnection utworzonymi w zakresie? Czy klasa SqlConnection wewnętrznie używa klasy Transaction, która z kolei rejestruje się w TransactionScope? Czy może bezpośrednio zaciąga się do TLS?
Kakira