Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

A customized business transaction in C#

2.00/5 (3 votes)
19 Nov 20072 min read 1   281  
An article on implementing a customized business transaction class based on LLBLGen Pro.

Introduction

In every project we need a business transaction class which help us to control our business flow. This class should be able to handle nested transactions too. I wrote a class, named "BusinessTransaction" and want to share it here with every one who has problem with nested transaction handling.

Background

In many situations we have some Services/Facades/Managers which process data containers and save them in database. Sometime these services call each other too, in these cases if any exception occur in inner service, the exception is thrown in a 2 level transaction and both of them should be rolled back.

For example, in an algorithm like this,

public void X() 
{     
   try    
   {    
      transaction.Start();         
      console.WriteLine("X");
      transaction.Commit();
    }     
    catch(Exception ex) 
    { 
        transaction.Rollback();        
        throw ex;    
    }
} 
public void Y() 
{     
   try    
   {    
      transaction.Start();
      console.WriteLine("Y");
      transaction.Commit();
    }     
    catch(Exception ex) 
    { 
        transaction.Rollback();        
        throw ex;    
    }
}  
public void Z() 
{     
   try    
   {    
      transaction.Start();         
      X();
      Y();
      transaction.Commit();
    }     
    catch(Exception ex) 
    { 
        transaction.Rollback();        
        throw ex;    
    }
} 

We can have some cases such as:

1- An Exception occurs between X transaction: X and Z should be rolled back.

2- An Exception occurs after X commit and between Y transaction: X,Y and Z should be rolled back.

3- An Exception occurs After X commit and between Y transaction, but Y doesn't throw it: Only Y should be rolled back, X and Z must be committed.

4- An Exception occurs After Y commit: X,Y and Z should be rolled back.

So, i tried to handle all of these manners in my code and tested them; it worked!
I always use LLBLGen Pro. as ORM and of course you can see it in my code, but if you are not familiar with it, don't worry, the related code is easy to understand!

Using the code

I used a stack to handle nested transactions. In every Start() Method, the related transaction identifier is pushed in it, only if the stack is empty, it means that no outer transaction exists, so i starts a real transaction.

/// <summary>
    /// starts a new Transaction or an inner Transaction
    /// </summary>
    /// <param name="isolationLevel"></param>
    public void Start(IsolationLevel isolationLevel)
    {
        string startMethodName = GetSavePoint();
        int transactionID = tarnsactionIDToInfoMap.Count + 1;

        if (transactionStack.Count == 0)
        {
            adapter.StartTransaction(isolationLevel, "1");
            tarnsactionIDToInfoMap.Clear();
        }
        else
        {
            adapter.SaveTransaction(transactionID.ToString());
        }

        tarnsactionIDToInfoMap.Add(transactionID, startMethodName);
        transactionStack.Push(transactionID);
    }

When commit() or rollback() are called, one transaction is popped, and again if stack becomes empty, it means that the transaction is a real one and have to be rolled back or committed.

/// <summary>
    /// Commits the mosty inner Transaction
    /// </summary>
    public void Commit()
    {
        if (transactionStack.Count == 0)
        {
            throw new FatalException("Error in transaction Handling. Commit on empty stack", true);
        }

        string commitMethodName = GetSavePoint();
        string srartMethodName = tarnsactionIDToInfoMap[(int)(transactionStack.Pop())].ToString();

        if (!commitMethodName.Equals(srartMethodName))
        {
            LogManager.Instance.WriteWarning("Unmatched transaction. " +
                "The Start Transaction was called from " + srartMethodName +
                " but it was commited in " + commitMethodName);
        }

        if (transactionStack.Count == 0)
        {
            adapter.Commit();
        }
    }

    /// <summary>
    /// Rolls back the mosty inner Transaction
    /// </summary>
    public void Rollback()
    {
        if (transactionStack.Count == 0)
        {
            throw new FatalException("Error in transaction Handling. Rollback on empty stack", true);
        }

        int transactionID = (int)(transactionStack.Pop());
        string rollbackMethodName = GetSavePoint();
        string startMethodName = tarnsactionIDToInfoMap[transactionID].ToString();

        if (!rollbackMethodName.Equals(startMethodName))
        {
            LogManager.Instance.WriteWarning("Unmatched transaction. " +
                "The Start Transaction was called from " + startMethodName +
                " but it was rolled back in " + rollbackMethodName);
        }

        if (transactionStack.Count == 0)
        {
            adapter.Rollback();
        }
        else
        {
            adapter.Rollback(transactionID.ToString());
        }
    }
I hope this class be useful!


License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here