by Andreas

TransactionScope – atomic transactions in C#

From the world of databases we are familier with the term "atomic operation" : If not all operations succeed, don’t do any of them. An example is a banking system, where you move funds between accounts. If you deduct an amount from account #1 in operation 1, you definately want the amount to be added to account #2 in the next operation. If the second operation fails, roll back all previous changes.

Another example is if you are performing complex database operations that involve multiple queries run from a variety of classes and methods. You’ll eventually face the challenge of cleaning up any changes you might have made before an exception is thrown or an unexpected event occurs. This is where the System.Transactions.TransactionScope namespace comes in handy.

The System.Transactions assembly needs to be added to your project manually. Just add a reference like normal – you’ll find it under the .NET tab:

image

It requires your project to run on the .NET 4.0 platform, so change this in the project properties if necessary. Once referenced you can run your code in a transactional block, calling methods in different classes and commit it all (or roll back) in the end. My example below shows how two methods in different classes can do database operations within the same scope, and if an exception is thrown Complete() is not invoked and changes are rolled back:

static void RunInTransactionScope()
{
    using (TransactionScope trans = new TransactionScope(TransactionScopeOption.Required))
    {
        try
        {
            SomeClass1.DBOperation1();
            AnotherClass.DBOperation2();

            // commit
            trans.Complete();
        }
        catch (Exception e)
        {
            // all changes will be rolled back
        }

    } // transaction scope end
}

 

Take note of the different parameter options for the TransactionScope constructor. One in particular is the scope timeout, which is useful if your processing takes a long time or when you’re debugging.

Also check out a great tip for how to build dynamic WHERE clauses with LINQ in another post on this blog.

Tags: ,
  • Peter Parker

    No code needed in catch? So that would mean that as long as you don’t run complete, it will never commit? Where does the magic happen? 😀

    • http://blog.degree.no/bloggere/ Andreas

      That’s right, if you leave the transaction scope without running commit it will automatically roll back. The magic happens within the entity framework, and we can stay blissfully ignorant to the actual operation :)

  • Lionel

    In fact, the keyword “using” is a “syntactic sugar” so this :
    [code]
    using(var foo = new Foo())
    {

    }
    [/code]
    is the same as this :
    [code]
    var foo = new Foo();
    try
    {

    }
    finally
    {
    foo.Dispose();
    }
    [/code]

    The rollback happens in the Dispose() method (maybe even if you call the Complete() method)!

    You don’t need a try…catch in your using, it’s redundant. But if you need to handle the exception, you can surround the using with a try…catch.
    Furthermore, you don’t need to write catch(Exception e) if you don’t use the e variable. You can simply write catch { }.
    😉