TransactionScope 如何回滚事务?

我正在编写一个集成测试,其中我将向数据库中插入一些对象,然后检查以确保我的方法是否检索到这些对象。

我通过 NHibernate 连接到数据库... ... 我通常创建这样一个测试的方法是执行以下操作:

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();

然而,我最近发现 TransactionScope 事务范围显然可以用于这个目的..。

部分 我找到的示例代码如下:

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;


}

我相信,如果我不包括行 txScope.Complete(),插入的数据将回滚。但不幸的是,我不明白这怎么可能... ... txScope对象如何跟踪数据库中的 deptAdapterempAdapter对象及其事务。

我觉得我在这里遗漏了一些信息... ... 我真的能够用 TransactionScope包围我的代码来替换我的 BeginTransaction()RollbackTransaction()调用吗?

如果没有,那么 TransactionScope如何回滚事务?

100582 次浏览

Essentially TransactionScope doesn't track your Adapter's, what it does is it tracks database connections. When you open a DB connection the connections will looks if there is an ambient transaction (Transaction Scope) and if so enlist with it. Caution if there are more the one connection to the same SQL server this will escalate to a Distribtued Transaction.

What happens since you're using a using block you are ensuring dispose will be called even if an exception occurs. So if dispose is called before txScope.Complete() the TransactionScope will tell the connections to rollback their transactions (or the DTC).

The TransactionScope class works with the Transaction class, which is thread-specific.

When the TransactionScope is created, it checks to see if there is a Transaction for the thread; if one exists then it uses that, otherwise, it creates a new one and pushes it onto the stack.

If it uses an existing one, then it just increments a counter for releases (since you have to call Dispose on it). On the last release, if the Transaction was not comitted, it rolls back all the work.

As for why classes seem to magically know about transactions, that is left as an implementation detail for those classes that wish to work with this model.

When you create your deptAdapter and emptAdapter instances, they check to see if there is a current transaction on the thread (the static Current property on the Transaction class). If there is, then it registers itself with the Transaction, to take part in the commit/rollback sequence (which Transaction controls, and might propogate to varying transaction coordinators, such as kernel, distributed, etc.).