Introduction
Martin Fowler writes Now the code and the implementation As you can see I first need a new session instance. I then set the flush mode to commit and assign the session to an instance variable for further use. Finally I return a new instance of the UnitOfWorkImplementor (which implements the interface IUnitOfWork). The constructor of the UnitOfWorkImplementor class requires two parameters, the session and a reference to this factory. To be able to compile I also have to define the UnitOfWorkImplementor class. Of this class I just implement the minimum needed so far Now back to the implementation of the CreateSession method. To be able to create (and open) a session NHibernate must have been configured previously. So I solve this problem first. First we add a file hibernate.cfg.xml to our test project and put the following content into it (don't forget to set the property 'Copy to Output Directory' of this file to 'Copy always'). The above configuration file assumes that you have an SQL Server 2005 installed on your local machine and that you are using integrated security to authenticate and authorize the access to the database. It further assumes that there exists a database called 'Test' on this server. To configure NHibernate for other types of databases please consult the online documentation here There are just two open points The former I'll cover in the third part of this article and the latter problem can be solved by introducing a (generic) repository. In the previous two chapters I introduced the Unit of Work pattern and developed a working version based on NHibernate. One weak point of the shown implementation is that it is strictly non thread safe. In NHibernate the session object takes the role of the Unit of Work container. The session object is not thread safe! That is a session instance may not be accessed from different threads. If your application is running on multiple threads (typically an application running on the server) then you have to open a new session instance for each running thread. In this post I want to improve the implementation of the Unit of Work pattern such as that it is thread safe. The class which starts a new unit of work is a static class. All static members of this class are accessible from each thread of the application domain in which your application is running. Now fortunately .NET provides us a mean to define pseudo static fields in a type which are local to a single thread. That is this state is not shared between different threads. Each thread has its own thread static state. To make a field thread static one has to decorate it with the [ThreadStatic] attribute. The easiest and most pragmatic approach would now be to just decorate our _innerUnitOfWork field of the UnitOfWork class with the [ThreadStatic] attribute. But eventually we want to keep additional state in our Unit of Work. Thus we implement a special Data class which is a kind of a wrapper around a Hashtable. There are 2 possible scenarios I want to discuss. You either develop an application with a web client or an application with a windows client (e.g. WinForms). In a web application one has the so called Http context for each request. Multiple requests from different clients can run at the same but each of them runs on a different thread and has it's own context. In this situation we store the data relevant to the unit of work in the context of the request. In a windows client application (i.e. Smart Client Application) on the other hand we have a single client that might run on different threads at the same time. In this situation we "attach" the data relevant to the unit of work in so called thread-static fields. A thread-static field is not shared between different threads in the same application. To make a (static) field thread-static we have to just decorate it with a [ThreadStatic] attribute. In our Unit of Work implementation we have the static UnitOfWork class which contains a static field _innerUnitOfWork of type IUnitOfWork. We now need to change this since this kind of implementation is not thread safe. Since we might want to save other data in a thread safe way as well (not just the current instance of the unit of work) we choose a slightly more sophisticated solution. Let's define a static class Local which encapsulates the details whether we run a web- or a win-application. This class has just one (static) read-only property Data of type ILocalData. All details of our implementation are now "hidden" in the class LocalData which we choose to be a private child class of the Local class. We now want to be able to save into and read data from this local container via a key, e.g. This should be in a thread safe way. Obviously the above sample client code implies that we use some kind of dictionary to internally store our data. As usual we formulate our intention in a unit test which might look like this The test shows that I want to be able to not only store basic types but any complex type in my local data container. In this case a string, an integer and an instance of a Person class. The Person class is very basic at looks like follows Please also note that I use keys of different type (string and int). Let's first assume that we want to provide a thread safe unit of work implementation for the smart client application. Then the most basic implementation to fulfill this test is I just take a hash table as local storage and define an indexer property and a Count property (Note: a HashTable is an often used implementation of a Dictionary). We want to also be able to clear the content of the local store and thus we define the following test The code to fulfill this test is trivial, just add the code snippet below to the LocalData class Unfortunately the test still faults when run! This is because the local data storage is static and all test methods access the same container we have a "side effect" which we must now compensate. We can do it by just clearing the local data container before each test. Add this code to the test fixture class to compensate the side effect Re-run all tests. They should now all pass... And now comes the "tricky" part of the implementation where we want to guarantee that our local data store is indeed thread safe. Let's first define a test for this scenario In the above code I run two threads in parallel, the thread on which the test is running (let's call it the main thread) and an additional background thread. The code which is executed in the background thread is implemented in the helper method RunInOtherThread. To be able to synchronize the two threads I use an Event, in this case a ManualResetEvent. I have put some Console.WriteLine statements in the code for debugging purposes. In the main thread I add some data to the local data store. Then I spin up the background thread. In the background thread I first test that my (thread-) local data store is empty and then also fill in some data. With the Assert statements I test that we really have a thread safe behavior. The only change needed to make my implementation thread safe is to decorate the static field _localData with the [ThreadStatic] attribute. When the test is run it should pass and produce an output similar to this As mentioned above in this scenario we want to store our local data in the context of the current HTTP request. First I want to provide an indicator whether I am running in web and thus I add the following to the code snippet to the LocalData class Here I use the HttpContext class of the .NET framework (namespace System.Web). It's static property Current is equal to null if we do not run in the context of a web application. To abstract the details of the context (web- or smart client application) I define a private read-only property LocalHashtable as below In this code I use the RunningInWeb helper method to distinguish the two cases. In the case of NOT running in web context I test whether my thread static _localData field is equal to null, and If so I initialize it with a new instance of type Hashtable. I then return this instance. On the other hand, when running in web context I try to access a hash table instance in the Items collection of the current Http context. If I do not find such an instance then I create a new one and put it into the items collection of the current context. I then return the instance. Now I have to locate every place in the LocalData class where I access the _localData field directly and replace it with a reference to the LocalHashtable property. E.g. the indexer will have to be changed as follows Our thread- and/or Http context local data store is now complete and we can use it in our UnitOfWork class. To make our unit of work implementation thread safe we change the implementation of our static UnitOfWork class as shown below As you can see the field _innerUnitOfWork has gone and is replaced by a private property CurrentUnitOfWork. This property in turn uses the previously defined Local class to store the current unit of work item. Note that there is one place in our unit tests that makes a reference to the field _innerUnitOfWork and must thus be changed. It's the method ResetUnitOfWork in the class UnitOfWork_With_Factory_Fixture. The new implementation is When run, all tests should again pass. So now our implementation of the unit of work is thread safe.
for any details about the Flush Mode)
[Test]
public void Can_create_unit_of_work()
{ IUnitOfWork implementor = _factory.Create();
Assert.IsNotNull(implementor);
Assert.IsNotNull(_factory.CurrentSession);
Assert.AreEqual(FlushMode.Commit, _factory.CurrentSession.FlushMode);
}
public class UnitOfWorkFactory : IUnitOfWorkFactory
{ private static ISession _currentSession;
private ISessionFactory _sessionFactory;
internal UnitOfWorkFactory() { }
public IUnitOfWork Create() { ISession session = CreateSession();
session.FlushMode = FlushMode.Commit;
_currentSession = session;
return new UnitOfWorkImplementor(this, session);
}
}
public class UnitOfWorkImplementor : IUnitOfWork
{ private readonly IUnitOfWorkFactory _factory;
private readonly ISession _session;
public UnitOfWorkImplementor(IUnitOfWorkFactory factory, ISession session) { _factory = factory;
_session = session;
}
}
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="connection.connection_string">Server=(local);Database=Test;Integrated Security=SSPI;</property>
<property name="show_sql">true</property>
</session-factory>
</hibernate-configuration>
. The usage of a Unit of Work pattern simplifies the data manipulation tasks and hides the infrastructure details from the consumer of the UoW. NHibernate offers it's own implementation of the UoW in the form of the Session object. But NHibernate is just one of several possible ORM tools. With this implementation of the UoW pattern we have hidden most of the specifics of NHibernate an provide a generic interface to the consumer of the UoW.
Part 3
Introduction
Container for Thread Static State
using System; using System.Collections;
namespace NHibernateUnitOfWork { public static class Local
{ static readonly ILocalData _data = new LocalData();
public static ILocalData Data
{ get { return _data; } }
private class LocalData : ILocalData
{ // implementation details to de defined }
}
}
Local.Data["some key"] = someValue; // or otherValue = Local.Data["other key"]; using System; using NUnit.Framework;
namespace NHibernateUnitOfWork.Tests { [TestFixture]
public class LocalData_Fixture
{ [Test]
public void Can_store_values_in_local_data()
{ Local.Data["one"] = "This is a string";
Local.Data["two"] = 99.9m; var person = new Person {Name = "John Doe", Birthdate = new DateTime(1991, 1, 15)};
Local.Data[1] = person;
Assert.AreEqual(3, Local.Data.Count);
Assert.AreEqual("This is a string", Local.Data["one"]);
Assert.AreEqual(99.9m, Local.Data["two"]); Assert.AreSame(person, Local.Data[1]);
}
}
}
public class Person
{ public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual DateTime Birthdate { get; set; }
}
Implementation for Smart Client Applications
private class LocalData : ILocalData
{ private static Hashtable _localData = new Hashtable();
public object this[object key]
{ get { return _localData[key]; } set { _localData[key] = value; } }
public int Count
{ get { return _localData.Count; } }
}
[Test]
public void Can_clear_local_data()
{ Local.Data["one"] = "This is a string";
Local.Data["two"] = 99.9m; Assert.AreEqual(2, Local.Data.Count);
Local.Data.Clear();
Assert.AreEqual(0, Local.Data.Count);
}
public void Clear()
{ _localData.Clear();
}
[SetUp]
public void SetupContext()
{ Local.Data.Clear(); // start side-effect free! }
private ManualResetEvent _event;
[Test]
public void Local_data_is_thread_local()
{ Console.WriteLine("Starting in main thread {0}", Thread.CurrentThread.ManagedThreadId); Local.Data["one"] = "This is a string";
Assert.AreEqual(1, Local.Data.Count);
_event = new ManualResetEvent(false);
var backgroundThread = new Thread(RunInOtherThread); backgroundThread.Start();
// give the background thread some time to do its job Thread.Sleep(100);
// we still have only one entry (in this thread) Assert.AreEqual(1, Local.Data.Count);
Console.WriteLine("Signaling background thread from main thread {0}", Thread.CurrentThread.ManagedThreadId); _event.Set();
backgroundThread.Join();
}
private void RunInOtherThread()
{ Console.WriteLine("Starting (background-) thread {0}", Thread.CurrentThread.ManagedThreadId); // initially the local data must be empty for this NEW thread! Assert.AreEqual(0, Local.Data.Count);
Local.Data["one"] = "This is another string";
Assert.AreEqual(1, Local.Data.Count);
Console.WriteLine("Waiting on (background-) thread {0}", Thread.CurrentThread.ManagedThreadId); _event.WaitOne();
Console.WriteLine("Ending (background-) thread {0}", Thread.CurrentThread.ManagedThreadId); }
[ThreadStatic]
private static Hashtable _localData = new Hashtable();
Implementation for Web
public static bool RunningInWeb
{ get { return HttpContext.Current != null; } }
private static readonly object LocalDataHashtableKey = new object();
private static Hashtable LocalHashtable
{ get
{ if (!RunningInWeb) { if (_localData == null)
_localData = new Hashtable(); return _localData; }
else { var web_hashtable = HttpContext.Current.Items[LocalDataHashtableKey] as Hashtable; if (web_hashtable == null)
{ web_hashtable = new Hashtable(); HttpContext.Current.Items[LocalDataHashtableKey] = web_hashtable;
}
return web_hashtable; }
}
}
public object this[object key]
{ get { return LocalHashtable[key]; } set { LocalHashtable[key] = value; } }
Make Unit Of Work implementation thread safe
public static class UnitOfWork
{ private static readonly IUnitOfWorkFactory _unitOfWorkFactory = new UnitOfWorkFactory();
public static Configuration Configuration
{ get { return _unitOfWorkFactory.Configuration; } }
public const string CurrentUnitOfWorkKey = "CurrentUnitOfWork.Key";
private static IUnitOfWork CurrentUnitOfWork
{ get { return Local.Data[CurrentUnitOfWorkKey] as IUnitOfWork; } set { Local.Data[CurrentUnitOfWorkKey] = value; } }
public static IUnitOfWork Current
{ get
{ var unitOfWork = CurrentUnitOfWork;
if (unitOfWork == null)
throw new InvalidOperationException("You are not in a unit of work");
return unitOfWork; }
}
public static bool IsStarted
{ get { return CurrentUnitOfWork != null; } }
public static ISession CurrentSession
{ get { return _unitOfWorkFactory.CurrentSession; } internal set { _unitOfWorkFactory.CurrentSession = value; }
}
public static IUnitOfWork Start()
{ if (CurrentUnitOfWork != null)
throw new InvalidOperationException("You cannot start more than one unit of work at the same time.");
var unitOfWork = _unitOfWorkFactory.Create();
CurrentUnitOfWork = unitOfWork;
return unitOfWork; }
public static void DisposeUnitOfWork(IUnitOfWorkImplementor unitOfWork)
{ CurrentUnitOfWork = null; }
}
private void ResetUnitOfWork()
{ // assert that the UnitOfWork is reset var propertyInfo = typeof(UnitOfWork).GetProperty("CurrentUnitOfWork",
BindingFlags.Static | BindingFlags.SetProperty | BindingFlags.NonPublic);
propertyInfo.SetValue(null, null, null);
}