October 2008 Blog Posts

Last week’s post, Implementing a persistence ignorant Unit of Work framework (if you haven’t read this post yet I would encourage you to do so as this post builds on the previous post), showed how to implement a foundation for the Unit of Work pattern that can be used to implement specific Unit of Work components for any type of persistence framework.

In this post I’m going to enhance on the existing foundation to add another level to the Unit of Work, something I call the Unit of Work Scope.

Revisiting the current implementation

When working with database intensive applications there are bound to be many database actions that form a single logical operation. Taking the simplest example, a Customer Ordering system, saving an Order itself requires saving the order entity along with all the order detail entities containing the items, quantities, costs, etc.

Using the traditional Unit of Work component, such as one defined in the last post, you would start a Unit of Work, make changes to entities or register entities that need to be added / updated / removed, push those database operations to the server and the finally signal that the unit of work has been completed. The saving of an order in a order processing service could look something like this:

   12             UnitOfWork.Start();

   13             IRepository<Order> ordersRepository = IoC.Container.Resolve<IRepository<Order>>();

   14             ordersRepository.Save(order);

   15             UnitOfWork.Finish(true);

That is simple enough and works. Lets expand on the example and assume that as the order is being saved, we need to reserve all the items in the order in our order reservation system so that the stock represents only quantities that are available. So to provide such logic lets assume a service component called IOrderReservationService has been created. The code can then be refactored to something that resembles this:

   12             UnitOfWork.Start();

   13 

   14             IRepository<Order> ordersRepository = IoC.Container.Resolve<IRepository<Order>>();

   15             ordersRepository.Save(order);

   16 

   17             IOrderReservationService reservationService = IoC.Container.Resolve<IOrderReservationService>();

   18             reservationService.ReserveItemsInOrder(order);

   19 

   20             UnitOfWork.Finish(true);

Now since the implementation of the IOrderReservationService is something we don’t really care about, as long as the component does what is is supposed to do, the order processing service shouldn’t have to to care about the reservation service anyways. But what if the reservation service has the following code in ReserveItemsInOrder method:

   23         public void ReserveItemsInOrder (Order order)

   24         {

   25             IList<ItemReservationEntry> reservationEntries = BuildReservationEntries(order);

   26             UnitOfWork.Start();

   27 

   28             IRepository<ItemReservationEntry> reservationEntryRepository = IoC.Container.Resolve<IRepository<ItemReservationEntry>>();

   29             foreach (var entry in reservationEntries)

   30                 reservationEntryRepository.Save(entry);

   31 

   32             UnitOfWork.Finish(true);

   33         }

The above is a perfectly valid implementation… but has one major flaw. Since the ReserveItemsInOrder also operates under a Unit of Work, logically this method should use the same unit of work as the one started in the order processing service. Furthermore, when the ReserveItemsInOrder method calls UnitOfWork.Finish, the Unit of Work shouldn’t submit changes to the database at that time and mark the unit of work as finished, that is because the changes should be accepted only when the order processing service indicates that all operations have been completed, flush changes and set the Unit of Work as complete.

While in last weeks implementation of the UnitOfWork component satisfies the first requirement, i.e. the ReserveItemsInOrder method should operate under the same unit of work as the unit of work started in the order processing service. This is achieved because the implementation stores the current unit of work in the current thread / request and when UnitOfWork.Start() is called, at that time if an existing unit of work exists, it returns that instance instead of creating a completely new one.

The problem comes in the second requirement. When UnitOfWork.Finish(true) is called within the ReserveItemsInOrder method, that signals the UnitOfWork instance to submit changes to the server, causing both the Order that was added to the orders repository in the order processing service to be inserted and also the ItemReservationEntries to be inserted into the database. This then causes an error when UnitOfWork.Finish(true) is called on the order processing service, because the unit of work has already been finished.

This also causes a logical issue as, the order processing service could take additional steps after reserving item quantities for the order, such as creating an order fulfillment entry so that the order can be processed and dispatched, which should logically fall under the same unit of work, but since the Unit of Work was already finished, it is not possible to do so.

Introducing the Unit of Work scope

To solve the above scenario, we would need to cascade the same Unit of Work down to all components that explicitly start a new unit of work, and add logic to not commit the changes until all logical operations have been completed and the original initiator (i.e. top level component) that started the Unit of Work sets the unit of work as completed.

I’m going to term this as a Unit of Work scope. Some points on the Unit of Work scope:

  • All components operating under a Unit of Work scope will share the same IUnitOfWork instance, unless asked otherwise.
  • A unit of work scope does not commit / flush changes to the database unless it is the top-level unit of work scope.

Whenever I start out to develop a new idea or component, I normally write down some sample syntax of how consumers would consume that API or component. In this case I would like to use (or abuse if you like) the IDisposable pattern to define a scope that ensures that all operations within a using statement runs under the unit of work scope and once the using goes out of scope, either all changes are committed to the database or rolledback. Below is the syntax I would like consumers to use:

   12             using (UnitOfWorkScope scope = new UnitOfWorkScope())

   13             {

   14                 IRepository <Order> ordersRepository = IoC.Container.Resolve<IRepository<Order>>();

   15                 ordersRepository.Save(orders);

   16                 scope.Commit();

   17             }

The above code snippet follows very closely to the way a TransactionScope is used and that is very intentional. When using a TransactionScope, in the System.Transactions namespace, code executing within the scope participates in that TransactionScope. The UnitOfWorkScope uses the same semantics where code within a UnitOfWorkScope automatically participates within that unit of work scope.

Now that we have a syntax that represents the way I would like consumers to start and use the UnitOfWorkScope, lets deal with the big issue with child UnitOfWorkScope instances. Based on the example in the Revisiting the current Implementation section, an issue seems to crop up when a component starts a unit of work and then calls another component that itself starts a unit of work, we need to somehow make that second component utilize the same unit of work instance and not commit any changes until the root component decides to commit.

To tackle this scenario we need a way to somehow pass around the IUnitOfWork instance that will be used by all components. Making things more complex, sometimes there needs to be a way to use a completely different unit of work an not enlist in an existing running unit of work. This scenario happens when you have optional individual operations that if failed do not necessary fail the entire business operation.

The implementation

Based on the above assumptions and requirements, lets start with the implementation. Lets start with a component called the UnitOfWorkScopeTransaction. The intention of this class is to provide a component that encapsulates a IUnitOfWork instance that can be shared by multiple UnitOfWorkScope instances. The reason this class has the name Transaction appended to it is because the UnitOfWorkScopeTransaction class will be responsible for controlling when underlying transaction and deciding on when to actually Commit the unit of work.

Below is the complete code for the UnitOfWorkTransactionScope class:

   17 using System;

   18 using System.Collections.Generic;

   19 using System.Data;

   20 using System.Linq;

   21 

   22 namespace Rhinestone.Shared

   23 {

   24     /// <summary>

   25     /// The <see cref="UnitOfWorkScopeTransaction"/> identifies a unique transaciton that can

   26     /// be shared by multiple <see cref="UnitOfWorkScope"/> instances.

   27     /// </summary>

   28     public class UnitOfWorkScopeTransaction : IDisposable

   29     {

   30         #region fields

   31         private readonly Guid _transactionID;

   32         private readonly IUnitOfWork _unitOfWork;

   33         private readonly ITransaction _runningTransaction;

   34         private readonly Stack<UnitOfWorkScope> _attachedScopes;

   35         private readonly IsolationLevel _isolationLevel;

   36         private bool _transactionRolledback;

   37         private bool _disposed;

   38         #endregion

   39 

   40         #region ctor

   41         /// <summary>

   42         /// Overloaded Constructor.

   43         /// Creates a new instance of the <see cref="UnitOfWorkScopeTransaction"/> that takes in a

   44         /// <see cref="IUnitOfWorkFactory"/> instance that is responsible for creating instances of <see cref="IUnitOfWork"/> and

   45         /// a <see cref="IDbConnection"/> that is used by the instance to connect to the data store.

   46         /// </summary>

   47         /// <param name="unitOfWorkFactory">The <see cref="IUnitOfWorkFactory"/> implementation that is responsible

   48         /// for creating instances of <see cref="IUnitOfWork"/> instances.</param>

   49         /// <param name="isolationLevel">One of the values of <see cref="IsolationLevel"/> that specifies the transaction

   50         /// isolation level of the <see cref="UnitOfWorkScopeTransaction"/> instance.</param>

   51         public UnitOfWorkScopeTransaction(IUnitOfWorkFactory unitOfWorkFactory, IsolationLevel isolationLevel)

   52         {

   53             Guard.Against<ArgumentNullException>(unitOfWorkFactory == null,

   54                                                 "A valid non-null instance that implements the IUnitOfWorkFactory is required.");

   55             _transactionID = new Guid();

   56             _transactionRolledback = false;

   57             _disposed = false;

   58             _unitOfWork = unitOfWorkFactory.Create();

   59             _runningTransaction = _unitOfWork.BeginTransaction(isolationLevel);

   60             _isolationLevel = isolationLevel;

   61             _attachedScopes = new Stack<UnitOfWorkScope>();

   62         }

   63         #endregion

   64 

   65         #region properties

   66         /// <summary>

   67         /// Gets a <see cref="Guid"/> that uniqely identifies the transaction.

   68         /// </summary>

   69         /// <value>A <see cref="Guid"/> that uniquely identifies the transaction.</value>

   70         public Guid TransactionID

   71         {

   72             get { return _transactionID; }

   73         }

   74 

   75         /// <summary>

   76         /// Gets the <see cref="IsolationLevel"/> of the <see cref="UnitOfWorkScopeTransaction"/> instance.

   77         /// </summary>

   78         /// <value>One of the values of <see cref="IsolationLevel"/>.</value>

   79         public IsolationLevel IsolationLevel

   80         {

   81             get { return _isolationLevel; }

   82         }

   83 

   84         /// <summary>

   85         /// Gets the <see cref="IUnitOfWork"/> instance of the <see cref="UnitOfWorkScopeTransaction"/> instance.

   86         /// </summary>

   87         public IUnitOfWork UnitOfWork

   88         {

   89             get { return _unitOfWork; }

   90         }

   91 

   92         /// <summary>

   93         /// Gets a <see cref="IList{T}"/> containing instances of <see cref="UnitOfWorkScopeTransaction"/> currently

   94         /// started for the current request / thread.

   95         /// </summary>

   96         private static IList<UnitOfWorkScopeTransaction> CurrentTransactions

   97         {

   98             get

   99             {

  100                 string key = typeof (UnitOfWorkScopeTransaction).FullName;

  101                 if (!Storage.Local.Contains(key))

  102                     Storage.Local.Set<IList<UnitOfWorkScopeTransaction>>(key, new List<UnitOfWorkScopeTransaction>());

  103                 return Storage.Local.Get<IList<UnitOfWorkScopeTransaction>>(key);

  104             }

  105         }

  106         #endregion

  107 

  108         #region methods

  109         /// <summary>

  110         /// Gets a <see cref="UnitOfWorkScopeTransaction"/> instance that can be used by a <see cref="UnitOfWorkScope"/> instance.

  111         /// </summary>

  112         /// <param name="scope">The <see cref="UnitOfWorkScope"/> instance that is requesting the transaction.</param>

  113         /// <param name="isolationLevel">One of the values of <see cref="IsolationLevel"/> that specifies the transaction isolation level.</param>

  114         /// <returns>A <see cref="UnitOfWorkScopeTransaction"/> instance.</returns>

  115         public static UnitOfWorkScopeTransaction GetTransactionForScope (UnitOfWorkScope scope, IsolationLevel isolationLevel)

  116         {

  117             return GetTransactionForScope(scope, isolationLevel, UnitOfWorkScopeTransactionOptions.UseCompatible);

  118         }

  119 

  120         /// <summary>

  121         /// Gets a <see cref="UnitOfWorkScopeTransaction"/> instance that can be used by a <see cref="UnitOfWorkScope"/> instance.

  122         /// </summary>

  123         /// <param name="scope">The <see cref="UnitOfWorkScope"/> instance that is requesting the transaction.</param>

  124         /// <param name="isolationLevel">One of the values of <see cref="IsolationLevel"/> that specifies the transaction isolation level.</param>

  125         /// <param name="options">One of the values of <see cref="UnitOfWorkScopeTransactionOptions"/> that specifies options for using existing

  126         /// transacitons or creating new ones.</param>

  127         /// <returns>A <see cref="UnitOfWorkScopeTransaction"/> instance.</returns>

  128         public static UnitOfWorkScopeTransaction GetTransactionForScope (UnitOfWorkScope scope, IsolationLevel isolationLevel, UnitOfWorkScopeTransactionOptions options)

  129         {

  130             if (options == UnitOfWorkScopeTransactionOptions.UseCompatible)

  131             {

  132                 var transaction = (from t in CurrentTransactions

  133                                    where t.IsolationLevel == isolationLevel

  134                                    select t).FirstOrDefault();

  135                 if (transaction != null)

  136                 {

  137                     transaction.AttachScope(scope);

  138                     return transaction;

  139                 }

  140             }

  141 

  142             var factory = IoC.Container.Resolve<IUnitOfWorkFactory>();

  143             var newTransaction = new UnitOfWorkScopeTransaction(factory, isolationLevel);

  144             newTransaction.AttachScope(scope);

  145             CurrentTransactions.Add(newTransaction);

  146             return newTransaction;

  147         }

  148 

  149         /// <summary>

  150         /// Attaches a <see cref="UnitOfWorkScope"/> instance to the <see cref="UnitOfWorkScopeTransaction"/> instance.

  151         /// </summary>

  152         /// <param name="scope"></param>

  153         private void AttachScope(UnitOfWorkScope scope)

  154         {

  155             Guard.Against<ObjectDisposedException>(_disposed, "Transaction has been disposed. Cannot attach a scope to a disposed transaction.");

  156             Guard.Against<ArgumentNullException>(scope == null, "Cannot attach a null UnitOfWorkScope instance to the UnitOfWorkScopeTransaction instance.");

  157             _attachedScopes.Push(scope); //Push the scope on to the top of the stack.

  158         }

  159 

  160         /// <summary>

  161         /// Causes a comit operation on the <see cref="UnitOfWorkScopeTransaction"/> instance.

  162         /// </summary>

  163         /// <param name="scope">The <see cref="UnitOfWorkScope"/> instance that is calling the commit.</param>

  164         /// <remarks>

  165         /// This method can only by called by the scope currently on top of the stack. If Called by another scope then an

  166         /// <see cref="InvalidOperationException"/> is called. If the calling scope is last in the attached scope hierarchy,

  167         /// then a commit is called on the underling unit of work instance.

  168         /// </remarks>

  169         public void Commit(UnitOfWorkScope scope)

  170         {

  171             Guard.Against<ObjectDisposedException>(_disposed, "Transaction has been disposed. Cannot commit a disposed transaction.");

  172             Guard.Against<InvalidOperationException>(_transactionRolledback,

  173                                                     "Cannot call commit on a rolledback transaction. A child scope or current scope has already rolled back the transaction. Call Rollback()");

  174             Guard.Against<ArgumentNullException>(scope == null,

  175                                                 "Cannot commit the transaction for a null UnitOfWorkScope instance.");

  176             Guard.Against<InvalidOperationException>(_attachedScopes.Peek() != scope,

  177                                                     "Commit can only be called by the current UnitOfWorkScope instance. The UnitOfWorkScope provided does not match the current scope on the stack."); //TODO: Fix wording of exception.

  178             _attachedScopes.Pop();

  179             if (_attachedScopes.Count == 0)

  180             {

  181                 //The calling UnitOfWorkScope is the root of the transaction.

  182                 _unitOfWork.Flush();

  183                 _runningTransaction.Commit();

  184                 _runningTransaction.Dispose();

  185                 _unitOfWork.Dispose();

  186                 CurrentTransactions.Remove(this);

  187             }

  188         }

  189 

  190         /// <summary>

  191         /// Causes a Rollback operation on the <see cref="UnitOfWorkScopeTransaction"/> instance.

  192         /// </summary>

  193         /// <param name="scope">The <see cref="UnitOfWorkScope"/> instance that is calling the commit.</param>

  194         /// <remarks>

  195         /// This method can only be called by the scope currently on top of the stack. If called by another scope than the

  196         /// current <see cref="UnitOfWorkScope"/> instance, then a <see cref="InvalidOperationException"/> is thrown. If the

  197         /// calling scope is the last in the attached scope hierarchy, then a rollback is called on the underlying UnitOfWork

  198         /// instance.

  199         /// </remarks>

  200         public void Rollback(UnitOfWorkScope scope)

  201         {

  202             Guard.Against<ObjectDisposedException>(_disposed, "Transaction has been disposed. Cannot rollback a disposed transaction.");

  203             Guard.Against<ArgumentNullException>(scope == null, "Cannot rollback the transaction for a null UnitOfWork instance.");

  204             Guard.Against<InvalidOperationException>(_attachedScopes.Peek() != scope, "Rollback can only be called by the current UnitOfWorkScope instance. The UnitOfWorkScope provided does not match the current scope on the stack."); //TODO: Fix wording of exception.

  205 

  206             _attachedScopes.Pop();

  207             _transactionRolledback = true;

  208             if (_attachedScopes.Count == 0)

  209             {

  210                 //The calling UnitOfWorkScope is the root of the transaction.

  211                 _runningTransaction.Rollback();

  212                 _runningTransaction.Dispose();

  213                 _unitOfWork.Dispose();

  214                 CurrentTransactions.Remove(this);

  215             }

  216         }

  217         #endregion

  218 

  219         #region Implementation of IDisposable

  220         /// <summary>

  221         /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.

  222         /// </summary>

  223         /// <filterpriority>2</filterpriority>

  224         public void Dispose()

  225         {

  226             if (!_disposed)

  227             {

  228                 _disposed = true;

  229                 GC.SuppressFinalize(this);

  230             }

  231         }

  232         #endregion

  233     }

  234 }

A UnitOfWorkTransactionScope instance maintains a Stack of all scopes that share this transaction which allows it to do is maintain a hierarchal list of all scopes that are currently attached to it. When a scope that is sharing this UnitOfWorkTransactionScope calls Commit / Rollback, the UnitOfWorkTransactionScope instance performs the following checks and actions:

  1. A check is made to ensure that the calling UnitOfWorkScope should be on top of a local field called _attachedScopes, a Stack<UnitOfWorkScope>, indicating that this is the top most scope on the current thread. [I’ll get to how UnitOfWorkScope instances are added to _attachedScopes in a bit]

    This is done to ensure that out of turn Commit or Rollback calls are not made. Consider the following scenario;

       12         public void ScopeAMethod()

       13         {

       14             using (UnitOfWorkScope scopeA = new UnitOfWorkScope())

       15             {

       16                 ScopeBMethod();

       17                 scopeA.Commit();

       18             }

       19         }

       20 

       21         private void ScopeBMethod()

       22         {

       23             using (UnitOfWorkScope scopeB = new UnitOfWorkScope())

       24             {

       25                 scopeB.Commit();

       26             }

       27         }


    What the above dipicts a very common scenario where a root method starts a UnitOfWorkScope and then calls another method that stars another child UnitOfWorkScope where the child Scope should either be committed or rolledback first before the parent scope can be committed or rolledback. If the parent scope calls Commit before the child scope has had a change to vote on the commit or rolledback, that is an out of turn commit or rollback. This check guards the UnitOfWorkScopeTransaction from this scenario.

  2. If the call is Commit, then the instance also checks to ensure that a child scope did not not vote for rollback. If so, Commit cannot be honored and a InvalidOperationException is thrown.
  3. After some additional checks for null references and disposed check, the top most scope in the _attachedScopes Stack is popped. Then the instance checks if there are any more scopes in the stack, if not this is the last and final call to Commit / Rollback, and performs the appropriate action on the underlying IUnitOfWork / ITransaction component.

So how do UnitOfWorkScope instances get on the _attachedScopes stack of a UnitOfWorkScopeTransaction instance? That is via the RegisterScope instance method on the UnitOfWorkScopeTransaction class. Below is a sequence diagram of how a UnitOfWorkScope instance attaches itself to a UnitOfWorkScopeTransaction instance class:

 image 

Now that we have a way to share and pass around the underlying IUnitOfWork instance between UnitOfWorkScope instances, below is the complete implementation of the UnitOfWorkScope class:

   17 using System;

   18 using System.Collections.Generic;

   19 using System.Data;

   20 

   21 namespace Rhinestone.Shared

   22 {

   23     /// <summary>

   24     /// Helper class that allows starting and using a unit of work like:

   25     /// <![CDATA[

   26     ///    using (UnitOfWorkScope scope = new UnitOfWorkScope()) {

   27     ///        //Do some stuff here.

   28     ///        scope.Commit();

   29     ///    }

   30     ///

   31     /// ]]>

   32     /// </summary>

   33     public class UnitOfWorkScope : IDisposable

   34     {

   35         #region fields

   36         private static readonly string UnitOfWorkScopeStackKey = typeof (UnitOfWorkScope).FullName + ".RunningScopeStack";

   37         private UnitOfWorkScopeTransaction _currentTransaction;

   38         private bool _disposed;

   39         #endregion

   40 

   41         #region ctor

   42         /// <summary>

   43         /// Default Constuctor.

   44         /// Creates a new <see cref="UnitOfWorkScope"/> with the <see cref="IsolationLevel.Serializable"/>

   45         /// transaction isolation level.

   46         /// </summary>

   47         public UnitOfWorkScope() : this(IsolationLevel.ReadCommitted, UnitOfWorkScopeTransactionOptions.UseCompatible) {}

   48 

   49         /// <summary>

   50         /// Overloaded Constructor.

   51         /// Creates a new instance of <see cref="UnitOfWorkScope"/> with the specified transaction

   52         /// isolation level.

   53         /// </summary>

   54         /// <param name="isolationLevel">One of the values of <see cref="IsolationLevel"/> that specifies

   55         /// the transation isolation level the scope should use.</param>

   56         public UnitOfWorkScope (IsolationLevel isolationLevel) : this(isolationLevel, UnitOfWorkScopeTransactionOptions.UseCompatible) {}

   57 

   58         /// <summary>

   59         /// Overloaded Constructor.

   60         /// Creates a new instance of <see cref="UnitOfWorkScope"/> with the specified transaction isolation level, option connection and

   61         /// a transaction option that specifies if an existing transaction should be used or to create a new transaction.

   62         /// </summary>

   63         /// <param name="isolationLevel"></param>

   64         /// <param name="transactionOptions"></param>

   65         public UnitOfWorkScope (IsolationLevel isolationLevel, UnitOfWorkScopeTransactionOptions transactionOptions)

   66         {

   67             _disposed = false;

   68             _currentTransaction = UnitOfWorkScopeTransaction.GetTransactionForScope(this, isolationLevel, transactionOptions);

   69             RegisterScope(this);

   70         }

   71         #endregion

   72 

   73         #region properties

   74         /// <summary>

   75         /// Checks if the current thread or request has a <see cref="UnitOfWorkScope"/> instance started.

   76         /// </summary>

   77         /// <value>True if a <see cref="UnitOfWorkScope"/> instance has started and is present.</value>

   78         public static bool HasStarted

   79         {

   80             get

   81             {

   82                 if (!Storage.Local.Contains(UnitOfWorkScopeStackKey))

   83                     return false;

   84                 return RunningScopes.Count > 0;

   85             }

   86         }

   87 

   88         /// <summary>

   89         /// Gets the current <see cref="UnitOfWorkScope"/> instance for the current thread or request.

   90         /// </summary>

   91         /// <value>The current and most recent <see cref="UnitOfWorkScope"/> instance started for the current thread or request.

   92         /// If none started, then a null reference is returned.</value>

   93         public static UnitOfWorkScope Current

   94         {

   95             get

   96             {

   97                 if (RunningScopes.Count == 0)

   98                     return null;

   99                 return RunningScopes.Peek();

  100             }

  101         }

  102 

  103         /// <summary>

  104         /// Gets a <see cref="Stack{T}"/> of <see cref="UnitOfWorkScope"/> that is used to store and retrieve

  105         /// running scope instances.

  106         /// </summary>

  107         private static Stack<UnitOfWorkScope> RunningScopes

  108         {

  109             get

  110             {

  111                 //Note: No locking is required since the stack is stored either on the current thread or on the current request.

  112                 if (!Storage.Local.Contains(UnitOfWorkScopeStackKey))

  113                     Storage.Local.Set(UnitOfWorkScopeStackKey, new Stack<UnitOfWorkScope>());

  114                 return Storage.Local.Get <Stack<UnitOfWorkScope>>(UnitOfWorkScopeStackKey);

  115             }

  116         }

  117 

  118         /// <summary>

  119         /// Gets the <see cref="IUnitOfWork"/> instance used by the <see cref="UnitOfWorkScope"/> instance.

  120         /// </summary>

  121         public IUnitOfWork UnitOfWork

  122         {

  123             get

  124             {

  125                 if (!HasStarted)

  126                     return null;

  127                 return _currentTransaction.UnitOfWork;

  128             }

  129         }

  130         #endregion

  131 

  132         #region methods

  133         /// <summary>

  134         /// Registers a scope as the top level scope on the <see cref="RunningScopes"/> stack.

  135         /// </summary>

  136         /// <param name="scope">The <see cref="UnitOfWorkScope"/> instance to set as the top level scope on the stack.</param>

  137         private static void RegisterScope(UnitOfWorkScope scope)

  138         {

  139             Guard.Against<ArgumentNullException>(scope == null, "Cannot register a null UnitOfWorkScope instance as the top level scope.");

  140             Shared.UnitOfWork.Current = scope.UnitOfWork; //Setting the UnitOfWork isntance held by the scope as the current scope.

  141             RunningScopes.Push(scope);

  142         }

  143 

  144         /// <summary>

  145         /// UnRegisters a <see cref="UnitOfWorkScope"/> as the top level scope on the stack.

  146         /// </summary>

  147         /// <param name="scope"></param>

  148         private static void UnRegisterScope (UnitOfWorkScope scope)

  149         {

  150             Guard.Against<ArgumentNullException>(scope == null, "Cannot Un-Register a null UnitOfWorkScope instance as the top level scope.");

  151             Guard.Against<InvalidOperationException>(RunningScopes.Peek() != scope, "The UnitOfWorkScope provided does not match the current top level scope. Cannot un-register the specified scope.");

  152             RunningScopes.Pop();

  153 

  154             if (RunningScopes.Count > 0)

  155             {

  156                 //If the Stack has additional scopes, set the current unit of work to the UnitOfWork instance held by the top most scope.

  157                 UnitOfWorkScope currentScope = RunningScopes.Peek();

  158                 Shared.UnitOfWork.Current = currentScope.UnitOfWork;

  159             }

  160             else

  161                 Shared.UnitOfWork.Current = null;

  162         }

  163 

  164         ///<summary>

  165         /// Commits the current running transaction in the scope.

  166         ///</summary>

  167         public void Commit()

  168         {

  169             Guard.Against<ObjectDisposedException>(_disposed, "Cannot commit a disposed UnitOfWorkScope instance.");

  170             _currentTransaction.Commit(this);

  171             _currentTransaction = null;

  172         }

  173 

  174         /// <summary>

  175         /// Disposes off the <see cref="UnitOfWorkScope"/> insance.

  176         /// </summary>

  177         public void Dispose()

  178         {

  179             if (!_disposed)

  180             {

  181                 if (_currentTransaction != null)

  182                 {

  183                     _currentTransaction.Rollback(this);

  184                     _currentTransaction = null;

  185                 }

  186                 UnRegisterScope(this);

  187 

  188                 GC.SuppressFinalize(this);

  189                 _disposed = true;

  190             }

  191         }

  192         #endregion

  193     }

  194 }

The UnitOfWorkScope class is fairly simple, when an instance is created it calls the GetTransactionForScope static method on the UnitOfWorkScopeTransaction class which returns a UnitOfWorkScopeTransaction instance for the scope. When Commit is called on the UnitOfWorkScope instance, it calls Commit on the underlying UnitOfWorkScopeTransaction instance, or if the object is disposed before a commit, then a Rollback is called on the UnitOfWorkScopeTransaction.

The only complex part of this implementation is how UnitOfWorkScope supports multiple scopes that could run within a hierarchy that do not share the same transaction, and how to expose the current running UnitOfWork to the Repositories.

Lets assume the following example:

   12         public void ScopeAMethod()

   13         {

   14             using (UnitOfWorkScope scopeA = new UnitOfWorkScope())

   15             {

   16                 ScopeBMethod();

   17                 scopeA.Commit();

   18             }

   19         }

   20 

   21         private void ScopeBMethod()

   22         {

   23             using (UnitOfWorkScope scopeB = new UnitOfWorkScope(IsolationLevel.ReadCommitted, UnitOfWorkScopeTransactionOptions.CreateNew))

   24             {

   25                 ScopeCMethod();

   26                 scopeB.Commit();

   27             }

   28         }

   29 

   30         private void ScopeCMethod()

   31         {

   32             using (UnitOfWorkScope scopeC = new UnitOfWorkScope())

   33             {

   34                 scopeC.Commit();

   35             }

   36         }

In the above example, ScopeB requests a UnitOfWorkScope that does not share an existing UnitOfWorkScopeTransaction  instance and forces the instance to use its own instance. So what we have here is ScopeA using a IUnitOfWork, lets say uowA, and ScopeB and ScopeC share one IUnitOfWork, uowB.

Lets also assume that repositories will use the UnitOfWork.Current property to get the current IUnitOfWork instance. So what we have is a scenario where when executing under ScopeA, a repository should get uowA as the unit of work to operate on, when moving to ScopeB, the repository should get uowB, same in the case of ScopeC. Now when ScopeB is disposed, a repository should get back to using uowA.

This follows the semantics of a Stack frame. As you create UnitOfWorkScope instances, they are put onto a Stack that push the IUnitOfWork that is tied with to that scope. As the scope goes out of… ah scope… the IUnitOfWork tied to that scope should be popped along with the scope and the next scope on the stack should then push it’s IUnitOfWork back as the current unit of work to use.

And my head hurts, yours might be hurting as well.

Anyways, this Stack frame like semantics is tackled by using the ReigsterScope and UnRegisterScope methods. As instances of UnitOfWorkScopes are created they call RegisterScope forcing the IUnitOfWork tied to that scope as the current IUnitOfWork. As a UnitOfWorkScope is dispoed it calls the UnRegisterScope method that removes the scope from the stack and if a scope on the stack still exists, then the method sets the IUnitOfWork associated with the scope, now on top of the stack, as the current IUnitOfWork.

Final thoughts

The UnitOfWorkScope might seem complicated enough at first, but it does provide elegance and semantics to consumer code which is well worth the implementation. If follows existing design patterns closely, like TransactionScope, and is transparent enough so that you can extend it to add your own logic if required. Speaking of TransactionScope, if the underlying IDbConnection supports transaction enlistment, the UnitOfWorkScope implementation is perfectly compatible with TransactionScope.

Full source code, along with tests, can be found in Rhinestone’s source tree. Now that some of the foundational and infrastructure elements are complete, future posts are going to focus back on Rhinestone and it’s implementation.

Rhinestone Source: http://rhinestone.googlecode.com/svn

I would appreciate any feedback on bugs or inconsistencies in logic or even general comments. Thanks!


In the previous post; The Unit of Work Pattern I put down some thoughts on the Unit of Work pattern. In this post I delve into implementing a Unit of Work framework that allows persistence ignorance and provides a base for a unit of work component that I can use in Rhinestone.

Note: It would be highly rude of me to not mention that a lot of the ideas around UnitOfWork comes from Aynde’s excellent implementation of the pattern in Rhino.Commons. Even though Aynde’s implementation can be taken as is, I wanted to start with a framework that provided a bit more flexibility and a new twist on unit of work scoping…

Defining the interfaces:

Since we are all good developers here and follow the design principal of contract first / interface based development, lets started chalking out the IUnitOfWork interface.

   22 /// <summary>

   23     /// A unit of work contract that that commits / flushes changes to the store

   24     /// within a single transaction. The commit / flush should be called when the

   25     /// implementation is disposed.

   26     /// </summary>

   27     public interface IUnitOfWork : IDisposable

   28     {

   29         #region properties

   30         bool IsInTransaction { get; }

   31         #endregion

   32 

   33         #region methods

   34         /// <summary>

   35         /// Instructs the <see cref="IUnitOfWork"/> instance to being a new transaction.

   36         /// </summary>

   37         /// <returns></returns>

   38         ITransaction BeginTransaction();

   39         /// <summary>

   40         /// Instructs the <see cref="IUnitOfWork"/> instance to being a new transaction

   41         /// with the specified isolation level.

   42         /// </summary>

   43         /// <param name="isolationLevel">One of the values of <see cref="IsolationLevel"/>

   44         /// that specifies the isolation level of the transaction.</param>

   45         /// <returns></returns>

   46         ITransaction BeginTransaction(IsolationLevel isolationLevel);

   47         /// <summary>

   48         /// Flushes the changes made in the unit of work to the data store.

   49         /// </summary>

   50         void Flush();

   51         /// <summary>

   52         /// Flushes the changes made in the unit of work to the data store

   53         /// within a transaction.

   54         /// </summary>

   55         void TransactionalFlush();

   56         /// <summary>

   57         /// Flushes the changes made in the unit of work to the data store

   58         /// within a transaction with the specified isolation level.

   59         /// </summary>

   60         /// <param name="isolationLevel"></param>

   61         void TransactionalFlush(IsolationLevel isolationLevel);

   62         #endregion

   63     }

In keeping things simple, the IUnitOfWork interface mainly defines methods to ask the implementation to simply start transactions and provide an instance that implements from ITransaction interface (shown below). There are also two methods that flush any changes made to entities retrieved via the IUnitOfWork instance in a transactional manner.

   21 ///<summary>

   22     /// Encapsulates a data store transaction. Used by <see cref="IUnitOfWork"/> to flush changes

   23     /// under a transaction.

   24     ///</summary>

   25     public interface ITransaction : IDisposable

   26     {

   27         /// <summary>

   28         /// Commits the changes made to the data store.

   29         /// </summary>

   30         void Commit();

   31 

   32         /// <summary>

   33         /// Rollsback any changes made.

   34         /// </summary>

   35         void Rollback();

   36     }

Beyond the above two interfaces another interface is required for a factory interface. This interface will be responsible for creating instances of the IUnitOfWork interface:

   21 /// <summary>

   22     /// Factory interface that the <see cref="UnitOfWork"/> uses to create instances of

   23     /// <see cref="IUnitOfWork"/>

   24     /// </summary>

   25     public interface IUnitOfWorkFactory

   26     {

   27         /// <summary>

   28         /// Creates a new instance of <see cref="IUnitOfWork"/>.

   29         /// </summary>

   30         /// <returns></returns>

   31         IUnitOfWork Create();

   32     }

Nothing fancy there, just one simple Create() method that asks the factory to create a unit of work instance.

The UnitOfWork class:

[
NOTE: This class uses some helper components that were described in previous posts. If you havn’t already taken a look at them, you might want to read the following posts for a complete understanding of the following code;

  1. Building a generic IoC wrapper
  2. Using generics and lambdas to create a Guard class

]

Lets start off with the UnitOfWork class. This is the main class that the service layer will use to start off unit of work sessions and Repositories will use this class to obtain the current running UnitOfWork instances

   10         public static class UnitOfWork

   11         {

   12 

   13         }

The UnitOfWork class is static which means that the intention is not to create instances of the UnitOfWork class. Instead this static class will contain static methods to manage the lifecycle of a unit of work.

The first and obvious method will be to start a unit of work session. So lets first create a test method that tests starting a unit of work via the UnitOfWork static class.

   14         [Test]

   15         public void Calling_Start_Starts_A_UnitOfWork_Instance()

   16         {

   17             Assert.That(!UnitOfWork.HasStarted);

   18             Assert.That(UnitOfWork.Current, Test.Is.Null);

   19 

   20             var uowInstance = UnitOfWork.Start();

   21 

   22             Assert.That(UnitOfWork.HasStarted);

   23             Assert.That(uowInstance, Test.Is.Not.Null);

   24             Assert.That(UnitOfWork.Current, Test.Is.Not.Null);

   25             Assert.That(uowInstance, Test.Is.SameAs(UnitOfWork.Current));

   26         }

The test basically checks that there is no unit of work session currently running, which is done by checking the HasStaqrted property and checks the Current property on the UnitOfWork class to return null, then starts the a new unit of work and does checks to ensure that the Current property returns a non null reference and that the IUnitOfWork instance returned by the Start() method is the same as the IUnitOfWork instance returned by the Current property. I know the properties and methods in the unit test above don’t exist on the UnitOfWork class just yet… but that’s what TDD is isn’t it?

Okay, so the first step is to implement the HasStarted and Current property on the UnitOfWork class:

   24         #region const

   25         /// <summary>

   26         /// The Key used to store the current unit of work in <see cref="Storage.Local"/>.

   27         /// </summary>

   28         private static readonly string currentUnitOfWorkKey = "CurrentUnitOfWorkSession.Key"; //The key used to store the current unit of work in the session.

   29         #endregion

   30 

   31         #region properties

   32         /// <summary>

   33         /// Gets a boolean value indicating whether a _unitOfWork has been started for the current

   34         /// thread or current session.

   35         /// </summary>

   36         /// <value>

   37         /// True if a _unitOfWork has already started for the current thread or request.

   38         /// </value>

   39         public static bool HasStarted

   40         {

   41             get

   42             {

   43                 return Storage.Local.Contains(currentUnitOfWorkKey);

   44             }

   45         }

   46 

   47         /// <summary>

   48         /// Gets the current <see cref="IUnitOfWork"/> instance.

   49         /// </summary>

   50         /// <value>

   51         /// A <see cref="IUnitOfWork"/> instance for the current thread or request.

   52         /// </value>

   53         public static IUnitOfWork Current

   54         {

   55             get

   56             {

   57                 if (!HasStarted)

   58                     return null;

   59                 return Storage.Local.Get<IUnitOfWork>(currentUnitOfWorkKey);

   60             }  

   61             set

   62             {

   63                 if (value == null)

   64                     Storage.Local.Remove(currentUnitOfWorkKey); //Remove if the value is sepcified as null

   65                 else

   66                     Storage.Local.Set(currentUnitOfWorkKey, value);

   67             }

   68         }

   69         #endregion

NOTE: The implementation of the Unit of Work pattern here assumes that a Unit of Work session is local to the current thread / web request. If you require a Unit of Work to span multiple multiple web requests and use Session affinity, you can easily modify the implementation here to support session level unit of work instances.

The HasStarted property simply checks the current local storage, a helper class explained in post: Building a generic IoC wrapper that encapsulates storing application specific data to the thread / web request storage (or even Session and Application level). The Current property simply checks if a HasStarted returns true, if so it retrieves the IUnitOfWork instances from the Storage and returns that instance. More interestingly, the Current property also has a public Set accessor. This allows components to register their own IUnitOfWork instances that may be started outside the scope of the UnitOfWork static class.

Now that HasStarted and Current properties are implemented, the next step is to focus on the Start method of the UnitOfWork class. The Start method will first get a IUnitOfWorkFactory instance from the IoC container and request it to create a new IUnitOfWork instance:

   72         /// <summary>

   73         /// Starts a new <see cref="IUnitOfWork"/> session that implements a unit of work operation.

   74         /// </summary>

   75         /// <returns></returns>

   76         public static IUnitOfWork Start ()

   77         {

   78             if (HasStarted)

   79                 return Current; //returning the current uniut of work if it has already been started.

   80             IUnitOfWorkFactory factory = IoC.Container.Resolve<IUnitOfWorkFactory>();

   81             Current = factory.Create();

   82             return Current;

   83         }

Lets modify the unit test to setup the IoC container and add additional checks to ensure that the IUnitOfWork instance returned by the UnitOfWork.Current property is indeed the same IUnitOfWork instance returned by the IUnitOfWorkFactory instance

I’ve added some more tests in the source to ensure that if a UnitOfWork has already started by one component, when another component issues a Start statement again, it should return the same UnitOfWork instance.

   15         [Test]

   16         public void Calling_Start_Starts_A_UnitOfWork_Instance()

   17         {

   18             Assert.That(!UnitOfWork.HasStarted);

   19             Assert.That(UnitOfWork.Current, Test.Is.Null);

   20 

   21             var mockUOWFactory = MockRepository.GenerateStub<IUnitOfWorkFactory>();

   22             var mockUOWInstance = MockRepository.GenerateStub<IUnitOfWork>();

   23             mockUOWFactory.Stub(x => x.Create()).Return(mockUOWInstance);

   24 

   25             var mockIoCInstace = MockRepository.GenerateMock<IoC>();

   26             mockIoCInstace.Stub(x => x.Resolve<IUnitOfWorkFactory>()).IgnoreArguments().Return(mockUOWFactory);

   27 

   28             IoC.SetContainer(mockIoCInstace);

   29 

   30             var uowInstance = UnitOfWork.Start();

   31 

   32             Assert.That(UnitOfWork.HasStarted);

   33             Assert.That(uowInstance, Test.Is.Not.Null);

   34             Assert.That(UnitOfWork.Current, Test.Is.Not.Null);

   35             Assert.That(uowInstance, Test.Is.SameAs(UnitOfWork.Current));

   36             Assert.That(uowInstance, Test.Is.SameAs(mockUOWInstance));

   37         }

   38 

   39         [Test]

   40         public void Calling_Start_On_Already_Started_UnitOfWork_Returns_Same_UnitOfWork ()

   41         {

   42             var mockIoCContainer = MockRepository.GenerateStub<IoC>();

   43             var mockUOWFactory = MockRepository.GenerateStub<IUnitOfWorkFactory>();

   44             var mockUOWInstance = MockRepository.GenerateStub<IUnitOfWork>();

   45 

   46             mockIoCContainer.Stub(x => x.Resolve<IUnitOfWorkFactory>()).Return(mockUOWFactory);

   47             mockUOWFactory.Stub(x => x.Create()).Return(mockUOWInstance);

   48 

   49             IoC.SetContainer(mockIoCContainer);

   50 

   51             IUnitOfWork uowInstance = UnitOfWork.Start();

   52             Assert.That(UnitOfWork.HasStarted);

   53             Assert.That(UnitOfWork.Current, Test.Is.SameAs(uowInstance));

   54             Assert.That(UnitOfWork.Start(), Test.Is.SameAs(uowInstance));

   55         }

[Note: You may notice that I am not dealing with database connections here. This is a explicit design decision because I believe the implementing IUnitOfWork instance should be the one that decides on the connection string to use. The decision on which connection string(s) to use is often based on the environment an application is running on and sometimes involves application level logic]

Okay, now that we have a Start method, lets deal with a Finish method. Logically once you start a UnitOfWork there must be a way to end one so that all changes are flushed to the database. Here are some unit tests to test the implementation, before the implementation:

   57         [Test]

   58         public void Calling_Finish_Without_Start_Should_Throw_InvalidOperationException()

   59         {

   60             Assert.That(!UnitOfWork.HasStarted);

   61             Assert.That(UnitOfWork.Current, Test.Is.Null);

   62             Assert.That(() => UnitOfWork.Finish(false), Test.Throws.Exception<InvalidOperationException>());

   63         }

   64 

   65         [Test]

   66         public void Calling_Finish_When_UnitOfWork_Started_Finishes_Current_UnitOfWork_An_Resets_Current_UnitOfWork()

   67         {

   68             //Mock setups

   69             var mockIoCContainer = MockRepository.GenerateStub<IoC>();

   70             var mockUOWFactory = MockRepository.GenerateStub<IUnitOfWorkFactory>();

   71             var mockUOWInstance = MockRepository.GenerateStub<IUnitOfWork>();

   72 

   73             mockIoCContainer.Stub(x => x.Resolve<IUnitOfWorkFactory>()).Return(mockUOWFactory);

   74             mockUOWFactory.Stub(x => x.Create()).Return(mockUOWInstance);

   75 

   76             IoC.SetContainer(mockIoCContainer);

   77 

   78             IUnitOfWork uowInstance = UnitOfWork.Start();

   79             Assert.That(UnitOfWork.HasStarted);

   80             Assert.That(UnitOfWork.Current, Test.Is.Not.Null);

   81             Assert.That(uowInstance, Test.Is.Not.Null);

   82 

   83             UnitOfWork.Finish(true);

   84 

   85         }

Below is the implementation of the Finish method:

   87         /// <summary>

   88         /// Finishes the current IUnitOfWork instance.

   89         /// </summary>

   90         /// <param name="flush">bool. True if the Finish operation should flush the changes made

   91         /// to the current <see cref="IUnitOfWork"/> instance.</param>

   92         public static void Finish (bool flush)

   93         {

   94             Guard.Against<InvalidOperationException>(!HasStarted, "There is no running UnitOfWork session to finish.");

   95             if (flush)

   96                 Current.TransactionalFlush();

   97             Current.Dispose();

   98             Current = null;

   99         }

The one thing to Note in the Finish method is that I don’t explicitly call the BeginTransaction of the internal IUnitOfWork implementation. Since the intention of TransactionalFlush is flush all changes captured by the IUnitOfWork within a Tranaction, calling BeginTransaction() would be redundant.

Time for a Spike:

Now that we have a basic unit of work framework it’s time to do a spike and put the framework to test. I have updated the previous sample I had created to demonstrate persistence agnostic implementation of RepositoryBase (Post: Persistence Ignorance and DDD with RepositoryBase) and the source can be found here: https://rhinestone.googlecode.com/svn/samples/LinqRepositorySample

I basically updated the sample to implement a IUnitOfWork instance for both Linq and NHibernate. Then updated the tests to demonstrate that the you can easily switch between either Linq or NHibernate and tests pass regardless of the selection.

Some notes on the implementation for Linq though. First of all the DataContext has no real way to Rollback a transaction or Rollback any changes made to entities in the DomainContext. This makes implementing the Rollback method in ITransaction difficult as the Rollback method really doesn’t have any logic associated with it… the rollback basically disposes of the transaction.

Talking about transactions, both implementations will work with TransactionScope. So if you have a UnitOfWork instance working inside a TransactionScope, then any Commit call on the UnitOfWork will not succeed until the containing TransactionScope.Complete is called. That being said, the TransactionScope approach will only work in NHibernate if you’re underlying provider is the ADO.Net SQL Client provider. I know providers for other databases do provide some level of support for auto enlistment in transaction scope, but the support is spotty at best.

Coming up next…

In the next post I’m going to extend the current Unit of Work framework and add additional hooks to semantically specify unit of work scopes. This new additional will provide similar semantics as the TransactionScope, but still being persistence ignorant.


This post assumes you are aware of Dependency Injection pattern and what Inversion of Control containers are. If not, I would recommend reading this post: Building a generic IoC wrapper or google Dependency Injection and IoC to get some background information.

Okay anyone who follows DDD, patterns or cares about data access knows about the Unit of Work pattern. The concept isn’t hard especially when you’ve had to work with transaction and synchronization issues when dealing with a single logical operation that is composed of multiple database operations.

The problem.

Consider you have a order dispatch application that consumes a B2B order and for each order has to do various operations:

  1. For each item in the order check the inventory for quantity in stock.
  2. When there is enough quantity in stock for an item, update the stock for that item to indicate quantity that has been reserved for this order and deduct the total quantity available.
  3. In case there isn’t enough quantity in stock for an item then whatever is available should be reserved and the for the remaining quantity a item refill order should be generated and dispatched so that the item can be restocked. The order then should be updated to indicate back-order quantity.
  4. Generate a dispatch order that is sent out to the warehouse for dispatching. The dispatch order should contain the shipping address for the Customer.

Now all this operations above logically fall under one operation: Process Order. While processing an order either all actions succeed or none. Meaning they run under one transaction. Simple enough right? Now lets assume being a good developer you are who really, I mean REALLY, cares about separation of concerns and contract driven development, develop this order dispatching system as dependency agnostic components that do a single operation. So the application might end up with many different components that perform multiple data operations, but all those database operations must operate within one singe transaction and any changes made to the database should fall under a single operation. If even on of those database changes do not go through, for whatever reason that may be, none should.

The way I used to do it…

Okay so way back when I didn’t know about patterns and just about the only thing that mattered to me was shipping the damn thing cause my boss is breathing down my neck that we had promised our customer that the order dispatch system will be delivered by the end of the week, which of course means that I was told about this new application that I had to develop just 2 days about, to ensure all operations fall under one transaction I would pass around a IDbConnection and a IDbTransacton…

   11     public interface IInventoryRepository

   12     {

   13         ItemInventory GetItemInventory(Item item, IDbConnection connection, IDbTransaction transaction);

   14         void UpdateItemInventory(ItemInventory inventory, IDbConnection connection, IDbTransaction transaction);

   15         void PlaceItemRefreshOrder(Item item, RefreshOrder order, IDbConnection connection, IDbTransaction transaction);

   16     }

   17 

   18     public interface IDispatchOrderRepository

   19     {

   20         void SaveDispatchOrder(DispatchOrder order, IDbConnection connection, IDbTransaction transaction);

   21     }

   22 

   23     public interface IOrdersRepository

   24     {

   25         Order GetNextOrder(IDbConnection connection, IDbTransaction transaction);

   26         void UpdateOrder(Order order, IDbConnection connection, IDbTransaction transaction);

   27     }

The above code snippet shows three interfaces of would be repositories of this application. Each method in the repository interface accepts a IDbConnection and IDbTransaction instance that the repository uses to issue commands to the database.

   15         public void ProcessOrder(Order order)

   16         {

   17             using (IDbConnection connection = ConnectionProvider.GetConnnection())

   18             {

   19                 connection.Open();

   20                 using (IDbTransaction transaction = connection.BeginTransaction())

   21                 {

   22                     try

   23                     {

   24                         IInventoryRepository inventoryRepository = GetInventoryRepository();

   25                         DispatchOrder dispatchOrder = new DispatchOrder();

   26                         foreach (var item in order.Items)

   27                         {

   28                             order.Status = "In Process";

   29                             ItemInventory inventory = inventoryRepository.GetItemInventory(item, connection, transaction);

   30                             //Process the inventory.. then update it....

   31                             inventoryRepository.UpdateItemInventory(inventory, connection, transaction);

   32 

   33                             //Code edited for brevity... create a dispatch order and save both the dispatch order and the base order

   34                             dispatchRepository.SaveDispatchOrder(dispatchOrder, connection, transaction);

   35                         }

   36 

   37                         transaction.Commit();

   38                     }

   39                     catch (Exception)

   40                     {

   41                         transaction.Rollback();

   42                     }

   43                 }

   44             }

   45         }

The ProcessOrder method shown above can serve as an example of how the Process Order operation would be responsible for maintaining the connection and transaction used for each repository action. [Yes I could use TransactionScope in the above and do away with passing IDbTransaction to the repositories but the problem still remains that the connection management is now in the hands of my business logic layer]

Issues with the previous approach.

The biggest problem I have with the above approach is that a) I have introduced connection management and transaction concerns into by business logic or service layer and b) I have leaked infrastructure concerns into all layers of my application.

The other issue is that each individual update call made to the repository causes an actual update operation to be sent to the database. Ideally I would like to have a method that could be called “SubmitChanges” that would send all necessary updates to the database at one go and would then rollback all changes if the operation did not succeed. This also means that when an operation spans multiple requests or sessions the connection and transactions have to remain open which is impractical and definitely not recommended.

Introducing the Unit Of Work pattern.

The idea behind the unit of work pattern is to introduce a dedicated component, i.e. the UnitOfWork, that keeps track of changes made to domain entities and then is responsible for persisting those changes to the database under a single transaction.

The Unit of Work pattern operates on two simple principles; 1. Changes made to the domain model via the domain entities are considered to be under a Business Transaction where changes are made in memory and not persisted to the database and 2. Once the Business Transaction is completed, all changes are persisted to the database under a single database transaction.

The concept of a Business Transaction is analogous to a database transaction. Where a database transaction groups multiple SQL statements as a single operation, i.e. either all succeed or all rollback, similarly a business transaction spans all in-memory operations and actions that are considered as a business operation. In the above order dispatching example the Process Order would be considered as a Business Transaction.

Below is an example of what a very simple UnitOfWork component may look like:

   15     public interface IUnitOfWork

   16     {

   17         void AddNew(object Entity);

   18         void Update(object entity);

   19         void Delete(object entity);

   20         void Commit();

   21         void Rollback();

   22     }

What the above interface suggests is that the UnitOfWork component simply tracks changes to domain entities via the AddNew / Update / Delete methods and once the the business transaction is done a Commit is called to send all changes to the database, or perform a Rollback if they don’t succeed.

Right now there might be a little voice in you’re head saying how does the IUnitOfWork component know how to persist the changes. In the above example you could use a Data Mapper pattern that provides the logistics of mapping the necessary operation, i.e. Insert / Update / Delete, for an entity type. The example below might make things clearer:

   19     public interface IUnitOfWork

   20     {

   21         void AddNew(object entity);

   22         void Update(object entity);

   23         void Delete(object entity);

   24         void Commit();

   25         void Rollback();

   26     }

   27 

   28     public interface IEntityMapper

   29     {

   30         void Insert(object entity, IDbConnection connection);

   31         void Update(object entity, IDbConnection connection);

   32         void Delete(object entity, IDbConnection connection);

   33     }

   34 

   35     public class BasicUnitOfWork : IUnitOfWork

   36     {

   37         #region fields

   38         private HashSet<object> _newEntities = new HashSet<object>();

   39         private HashSet<object> _updatedEntities = new HashSet<object>();

   40         private HashSet<object> _deletedEntities = new HashSet<object>();

   41         #endregion

   42 

   43         public void AddNew(object entity)

   44         {

   45             _newEntities.Add(entity);

   46         }

   47 

   48         public void Update(object entity)

   49         {

   50             _updatedEntities.Add(entity);

   51         }

   52 

   53         public void Delete(object entity)

   54         {

   55             _deletedEntities.Add(entity);

   56         }

   57 

   58         public void Commit()

   59         {

   60             using (TransactionScope scope = new TransactionScope())

   61             {

   62                 IDbConnection connection = GetConnection();

   63 

   64                 //Perform Inserts

   65                 foreach (var insert in _newEntities)

   66                 {

   67                     IEntityMapper mapper = IoC.Container.Resolve<IEntityMapper>(insert.GetType());

   68                     mapper.Insert(insert, connection);

   69                 }

   70 

   71                 //Perform Updates

   72                 foreach (var update in _updatedEntities)

   73                 {

   74                     IEntityMapper mapper = IoC.Container.Resolve<IEntityMapper>(update.GetType());

   75                     mapper.Update(update, connection);

   76                 }

   77 

   78                 //Perform Deletes

   79                 foreach (var delete in _deletedEntities)

   80                 {

   81                     IEntityMapper mapper = IoC.Container.Resolve<IEntityMapper>(delete.GetType());

   82                     mapper.Delete(delete, connection);

   83                 }

   84                 scope.Complete();

   85             }

   86         }

   87 

   88         public void Rollback()

   89         {

   90             _newEntities.Clear();
                  _updatedEntities.Clear();
                  _deletedEntities.Clear();

   91         }

[Update: shereef in the comments pointed out that Rollback should actually clear the Hastables containing the updates, which is correct. I’ve updated the code to clear them out in the Rollback method call.]

The BasicUnitOfWork component shown in the example above merely adds entities to internal HashSet collections that indicate whether the entity should be either Inserted / Updated / Deleted. In the Commit method, it enumerates each list and for each item in a list the component attempts to get a IEntityMapper that can handle mapping database operations for the specified entity type. Once it gets a mappper, it delegates the appropriate call, i.e. Insert / Update / Delete, to the mapper and finally commits.

The Unit of Work pattern with ORM frameworks

The above example is fine when working with the ADO.Net stack directly, but with the popularization of the ORM tools out there there is a movement to use more and more of these ORM framework to simplify the data access layer.

Most ORM frameworks, at least the ones I know of, implement the Unit of Work pattern directly within their framework, allowing you to concentrate on the actual business and service layer of your application. For example, in Linq to SQL the DataContext class can be thought of as a Unit of Work implementations (same for the ISession interface in NHibernate). Changes you make to the entities retrieved via a DataContext instance is tracked using the DataContext’s change tracking functionality, but those changes are not persisted until you call the SubmitChanges method on the DataContext.

In such a scenario where ORM frameworks are being used along with a Repository pattern, our UnitOfWork component’s responsibilities are slightly altered. Taking Linq to SQL as an example (you can easily apply the same thing to NHibernate); say we were to refactor the order dispatching example above to use Linq to SQL, Repositories and Unit of Work pattern, then the requirements from our Unit of Work component changes. First of all we don’t need the AddNew / Update / Delete methods since the DataContext takes care of that for us. Second, is to allow the Repository to ask the current running Unit of Work to give it the DataContext it should use. So after refactoring the IUnitOfWork and it’s implementation looks like this:

   20     public interface IUnitOfWork

   21     {

   22         DataContext CurrentContext { get; }

   23         void Commit();

   24         void Rollback();

   25     }

   26 

   27     public class BasicUnitOfWork : IUnitOfWork

   28     {

   29         private DataContext _currentContext = new DataContext(GetConnection(), GetMappingSource());

   30 

   31         public DataContext CurrentContext

   32         {

   33             get { return _currentContext; }

   34         }

   35 

   36         public void Commit()

   37         {

   38             _currentContext.SubmitChanges(); //No transaciton required since SubmitChanges uses an implicit transaction.

   39         }

   40 

   41         public void Rollback()

   42         {

   43             //Do nothing.

   44         }

   45     }

Basically I yanked out the AddNew / Update / Delete methods and added a get property for retrieving the DataContext that should be used. The next step is to find a way for repositories to be able to get the current running Unit of Work. For that we can create an static factory class that is responsible for starting, ending and getting the current unit of work. And to make things interesting lets make it support durable unit of work instances so that we can allow the unit of work to span multiple web requests.

   20     public static class UnitOfWorkFactory

   21     {

   22         private const string SESSION_KEY = "UNITOFWORK_SESSION_CURRENT";

   23 

   24         public static IUnitOfWork Current

   25         {

   26             get

   27             {

   28                 return (IUnitOfWork) HttpContext.Current.Session[SESSION_KEY];

   29             }

   30         }

   31 

   32         public static void Start()

   33         {

   34             HttpContext.Current.Session[SESSION_KEY] = IoC.Container.Resolve<IUnitOfWork>();

   35         }

   36 

   37         public static void Finsh ()

   38         {

   39             HttpContext.Current.Session.Remove(SESSION_KEY);

   40         }

   41     }

When the Start method is called on the UnitOfWorkFactory class, it basically asks the IoC container to get an instance of IUnitOfWork, which in our case will be the BasicUnitOfWork implementation, and saves that into a Session variable (making the unit of work durable).

Now repositories can use the UnitOfWorkFactory class to get the current Unit of Work and through it get the DataContext to use like so:

   44     public class OrdersRepository : IOrdersRepository

   45     {

   46         public Order GetNextOrder ()

   47         {

   48             var unitOfWork = (BasicUnitOfWork) UnitOfWorkFactory.Current;

   49             var ordersTable = unitOfWork.CurrentContext.GetTable(Order);

   50             return (from order in ordersTable select order).FirstOrDefault();

   51         }

   52 

   53         public void AddOrder (Order order)

   54         {

   55             var unitOfWork = (BasicUnitOfWork) UnitOfWorkFactory.Current;

   56             var ordersTable = unitOfWork.CurrentContext.GetTable(Order);

   57             ordersTable.InsertOnSubmit(order);

   58         }

   59     }

Coming up next…

Hopefully the above explains the Unit of Work pattern well (and correctly) and there are tons of examples out there that implement the pattern. In the next post I will dive into implementing a persistent ignorant unit of work framework, with a slight twist, that will be used in Rhinestone.

PLEASE NOTE: The examples above are just that… examples. They are by no means complete implementations of the Unit of Work pattern or the Data Mapper pattern. For a complete implementation please see my upcoming post on this topic.