The ObjectMapper .NET Project

Official blog of the AdFactum ObjectMapper .NET

Tutorial 4: Using the ObjectMapper in a multi threaded environment and cascaded transactions

Posted by Gerhard Stephan on April 2nd, 2007

The following article is obsolete!
The OBM (ObjectMapper .NET Manager Class)
is now a part of the AdFactum ObjectMapper .NET 1.90.1917 itself.

This tutorial shows how to use the AdFactum ObjectMapper .NET in an enterprise environment. Therefore we assume that we have several threads (e.g. IIS sessions) that will access the business services.

We will implement all required features within the OBM (ObjectMapper .NET Manager Class). That’s a static helper class which you can simply copy to your project and change to your needs. (Please copy the source from your Tutorial Solution that you’ll find within your AdFactum ObjectMapper .NET download)

The first issue is, that we have to manage different threads. Because the ObjectMapper .NET is not multi threading capable by default, every single thread own it’s ObjectMapper .NET instance. Therefore we are using a feature of the ObjectMapper .NET that allows you to export/import the transcation context. Now we are using the following method to cache the transaction context objects for every single thread. 

/// <summary>
/// Used to access the transaction context
/// </summary>
private static ITransactionContext TransactionContext
{
    get
    {
        permissionSetLock.AcquireReaderLock(Timeout.Infinite);
        
        try
        {
            /*
             * If we don’t have a collection return NULL
             */
            if (transactionContext == null)
                 return null;
            
            int threadId = Thread.CurrentThread.ManagedThreadId;
            
            if (transactionContext.ContainsKey(threadId))
                 return transactionContext[threadId];
            else
                 return null;
        }
        finally
        {
            permissionSetLock.ReleaseReaderLock();
        }
    }
    
    set
    {
        permissionSetLock.AcquireWriterLock(Timeout.Infinite);
        
        try
        {
            /*
             * Check if the transaction Context is initialized
             */
            if (transactionContext == null)
                 transactionContext = new Dictionary<int, ITransactionContext>();
            
            /*
             * Set the value
             */
            transactionContext[Thread.CurrentThread.ManagedThreadId] = value;
        }
        
        finally
        {
            permissionSetLock.ReleaseWriterLock();
        }
    }
}

In order to create an instance of the ObjectMapper .NET for a particular thread we have to use another helper method that checks whether the transaction context has been stored in cache or not.  

/// <summary>
/// Creates the mapper using the standard factory and persister of the tutorial.
/// </summary>
/// <returns></returns>
public static ObjectMapper CreateMapper()
{
    ObjectMapper mapper;
    
    /*
     * Check if a valid transaction Context does exist, if not – create a new mapper object
     */
    if ((TransactionContext == null) || (TransactionContext.IsValid == false))
         mapper = new ObjectMapper(DefaultFactory, DefaultPersister, Transactions.Manual);
    else
         mapper = new ObjectMapper(DefaultFactory, TransactionContext);
    
    /*
     * Store the transaction Context
     */
    TransactionContext = mapper.TransactionContext;
    
    return mapper;
}

If you want to access the ObjectMapper .NET you have to use the CreateMapper method of the OBM helper class. The method retrieves an instance of the ObjectMapper .NET using the current thread and returns it to the calling method where it can be used.

using (ObjectMapper mapper = OBM.CreateMapper())
{
    /*
     * Open a transaction
     */
    bool nested = OBM.BeginTransaction(mapper);
    
    /*
     * Store the data
     */
    User HansDampf = new User();
    HansDampf.Name = "Hans Dampf";
    HansDampf.Logon = "Dampf";
    HansDampf.Md5PasswordKey = Md5CryptHelper.ComputeHash("turboDiesel");
    StoreUser (HansDampf);
    
    User DanielThunderbird = new User();
    DanielThunderbird.Name = "Daniel Thunderbird";
    DanielThunderbird.Logon = "Thunder";
    DanielThunderbird.Md5PasswordKey = Md5CryptHelper.ComputeHash("pokemon");
    StoreUser (DanielThunderbird);
    
    /*
     * Commit the transaction
     */
    OBM.Commit(mapper,nested);
}

As you can see, every method is using a new ObjectMapper .NET instance created by the CreateMapper method of the OBM Helper class. That’s the recommended way of using the ObjectMapper .NET in a multi threaded environment. Unlike other O/R Mapping tools, you don’t have to bother about the initializing ramp up time of the ObjectMapper .NET – it’s negligible.

So what are cascaded transactions?

Imagine you own several business methods which are calling each other in a cascading way. And every method own a separated ObjectMapper .NET instance. How do you manage transactions that are spanned over several business methods? To solve this problem we implemented helper methods for BeginTransaction, Flush and Commit. If you look at the implementation, you’ll see that the BeginTranscation will only return TRUE, if no superior transcation has been opened before.

/// <summary>
/// This method is used for opening an transaction.
/// If a parent transaction is open, the return value is set to true.
/// </summary>
/// <param name="mapper">Database Mapper</param>
/// <returns>
/// Returns True, if it’s a nested transaction
/// </returns>
public static bool BeginTransaction(ObjectMapper mapper)
{
    bool nestedTransaction = mapper.IsTransactionOpen;
    if (!nestedTransaction)
        mapper.BeginTransaction();
    
    return nestedTransaction;
}

/// <summary>
/// Flushes the content to database. This method does not close the transaction
/// </summary>
/// <param name="mapper">The mapper.</param>
public static void Flush(ObjectMapper mapper)
{
    try
    {
        mapper.Flush();
    }
    catch (Exception)
    {
        mapper.Rollback();
        throw;
    }
}

/// <summary>
/// This method executes an guarded commit. That means errors will be cached and resolved.
/// </summary>
/// <param name="mapper">Database Mapper</param>
/// <param name="nestedTransaction">True, if it’s a nested transaction.</param>
public static void Commit(ObjectMapper mapper, bool nestedTransaction)
{
    /*
     * If a surrounding transaction is already open – don’t commit the transaction
     */
    if (nestedTransaction)
        return;
    
    try
    {
        /*
         * Commit work
         */
        mapper.Commit();
    }
    catch (Exception)
    {
        mapper.Rollback();
        throw;
    }
}

The "Commit" method only executes the commit if it’s not a nested transaction. That means that only the first Commit method within the call stack will fire the SQLs to database. Due to that fact you’ll need to call the "flush" in nested business methods if the calling method needs to load data that has been stored by nested methods.

The Tutorial 4 has been included within the ObjectMapper .NET project. So download the ObjectMapper .NET and open the tutorial. If you have any questions, feel free to post them.

Cheers
- Gerhard 

kick it on DotNetKicks.com

One Response to “Tutorial 4: Using the ObjectMapper in a multi threaded environment and cascaded transactions”

  1. AdFactum ObjectMapper .NET Blog » Blog Archive » New Release - AdFactum ObjectMapper .NET 1.90.1917.0 Says:

    [...] First of all the OBM (ObjectMapper .NET Manager Class) has been included in the current release. That means that you don’t have to implement multi threading functionality by your own. You can simple use the OBM helper class. The original idea of the OBM has been explained in Tutorial 4. The only difference is, that the signature of the Method CreateMapper has been changed. [...]

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>