How does TransactionScope roll back transactions?

97,792

Solution 1

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).

Solution 2

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.).

Share:
97,792

Related videos on Youtube

user3953201
Author by

user3953201

A C# .NET Developer with a passion for coding best practices and Test Driven Development

Updated on July 08, 2022

Comments

  • user3953201
    user3953201 almost 2 years

    I'm writing an integration test where I will be inserting a number of objects into a database and then checking to make sure whether my method retrieves those objects.

    My connection to the database is through NHibernate...and my usual method of creating such a test would be to do the following:

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

    However, I've recently found out about TransactionScope which apparently can be used for this very purpose...

    Some example code I've found is as follows:

    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;
    
    }
    

    I believe that if I don't include the line txScope.Complete() that the data inserted will be rolled back. But unfortunately I don't understand how that is possible... how does the txScope object keep a track of the deptAdapter and empAdapter objects and their transactions on the database.

    I feel like I'm missing a bit of information here...am I really able to replace my BeginTransaction() and RollbackTransaction() calls by surrounding my code using TransactionScope?

    If not, how then does TransactionScope work to roll back transactions?

    • Admin
      Admin over 15 years
      I have never used NHibernate, but maybe this link will help you.
    • Chris Marisic
      Chris Marisic over 15 years
      If you're looking at a better way to manage your NHibernate sessions to group operations into transactions you might want to check out my blog post on that topic dotnetchris.wordpress.com/2009/01/27/…
  • casperOne
    casperOne over 15 years
    TransactionScope doesn't track anything but the current Transaction on the thread, and modifies it if it needs to based on the model (requires, requires new, etc, etc). The transaction simply notifies anything that enlists with it, not just database connections.
  • user44298
    user44298 over 13 years
    I think is this is not entirely true. I did a partial trace of the source code of TransactionScope and and I also saw this msdn.microsoft.com/en-us/library/ms172152(v=vs.90).aspx which says "If it did not create the transaction, the commit occurs whenever Commit is called by the owner of the CommittableTransaction object. At that point the Transaction Manager calls the resource managers and informs them to either commit or rollback, based on whether the Complete method was called on the TransactionScope object." The source trace also indicates this behaviour.
  • Kakira
    Kakira over 9 years
    How does it work with SqlConnection(s) created within the scope? Does the SqlConnection class internally use the Transaction class which in-turn enlists with the TransactionScope? Or does it directly enlist into the TLS?
  • mungflesh
    mungflesh over 9 years
    I found this SO Q&A useful: IEnlistmentNotification
  • mins
    mins almost 4 years
    Still unclear to me. It seems the objects with methods called within the transaction scope must be cooperative with the Transaction mechanism. They must look for Commit/Rollback notifications by the system, and be able to roll back themselves since they are the ones to perform the rollback when required (if a file deletion was executed, obviously we can't magically roll it back unless we took some precautionary measure). It also seems SQL server operations are cooperative. But are there any other cooperative object in .Net? And how to write a class which is cooperative? Documentation?