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 txScope
obiekt zachować ścieżki deptAdapter
i empAdapter
obiektó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 TransactionScope
działa wycofywanie transakcji?
Odpowiedzi:
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).
źródło
TransactionScope
Klasa współpracuje zTransaction
klasy , która jest gwint specyficzny.Po
TransactionScope
utworzeniu sprawdza, czy istnieje znakTransaction
dla 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śliTransaction
nie 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
deptAdapter
andemptAdapter
instancji, sprawdzają, czy nie jest obecna transakcja na gwincie (statyczneCurrent
własności wTransaction
klasie). Jeśli tak, rejestruje sięTransaction
w sekwencji zatwierdzania / wycofywania zmian (któraTransaction
kontroluje i może być propagowana do różnych koordynatorów transakcji, takich jak jądro, dystrybucja itp.).źródło