In the first part of this series I have started to implement the Unit of Work pattern.
In this post I'll finish this implementation (for non-thread safe version) and in a third post I'll discuss how to make this implementation thread safe.
Design and Implementation
The Unit Of Work Factory
The creation of a unit of work instance is a complex process and as such is a good candidate for a factory.
Since a UoW (Unit of Work) is basically a wrapper around a NHibernate session object I'll need to open such a session whenever I start a new UoW. But to be able to get such a session NHibernate has to be configured and a NHibernate Session Factory has to be available. The interface of my UoW factory is defined as follows
I basically have two public methods on my factory. One for the creation of the UoW and the second for the disposal of a specific UoW instance. The disposal involves also some work and thus justifies the existence of this second factory method. The 3 properties Configuration, SessionFactory and CurrentSession are there mostly for advanced scenarios (and as such could theoretically be omitted in a first simple implementation).
Let's now write the test for the Create method of the UoW factory implementation. At first I prepare a new test fixture class
using System;
using NHibernate;
using NUnit.Framework;
namespace NHibernateUnitOfWork.Tests
{
[TestFixture]
public class UnitOfWorkFactory_Fixture
{
private IUnitOfWorkFactory _factory;
[SetUp]
public void SetupContext()
{
_factory = (IUnitOfWorkFactory) Activator.CreateInstance(typeof (UnitOfWorkFactory), true);
}
}
}
Why the heck do I need the Activator.CreateInstance to get an instance of the factory? Well I decided that the construction of a new factory instance should be internal to the assembly implementing the Unit of Work pattern. Thus the (default) constructor of the UnitOfWorkFactory class has a modifier internal and as a consequence cannot be used from code outside the assembly in which the factory is implemented. But I need an instance when testing and thus have to resort to the technique used above.
Now the question is: "what confirms me that the method Create of the factory works as expected?". I have chosen the following: I expect
- the method returning a non null instance of type IUnitOfWork
- the returned instance having a non null (NHibernate) session
- the (NHibernate) session having the Flush Mode set to commit (that is: the session is never flushed except when I explicitly commit a transaction - see the NHibernate documentation for any details about the Flush Mode)
Now the code
[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);
}
and the implementation
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);
}
}
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
public class UnitOfWorkImplementor : IUnitOfWork
{
private readonly IUnitOfWorkFactory _factory;
private readonly ISession _session;
public UnitOfWorkImplementor(IUnitOfWorkFactory factory, ISession session)
{
_factory = factory;
_session = session;
}
}
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').
<?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 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.
Now we define this test method
[Test]
public void Can_configure_NHibernate()
{
var configuration = _factory.Configuration;
Assert.IsNotNull(configuration);
Assert.AreEqual("NHibernate.Connection.DriverConnectionProvider",
configuration.Properties["connection.provider"]);
Assert.AreEqual("NHibernate.Dialect.MsSql2005Dialect",
configuration.Properties["dialect"]);
Assert.AreEqual("NHibernate.Driver.SqlClientDriver",
configuration.Properties["connection.driver_class"]);
Assert.AreEqual("Server=(local);Database=Test;Integrated Security=SSPI;",
configuration.Properties["connection.connection_string"]);
}
Here we test whether we can successfully create a configuration and whether this configuration contains the attributes we have defined via the NHibernate configuration file.
To fulfill this test we can e.g. implement the following code
public Configuration Configuration
{
get
{
if (_configuration == null)
{
_configuration = new Configuration();
string hibernateConfig = Default_HibernateConfig;
//if not rooted, assume path from base directory
if (Path.IsPathRooted(hibernateConfig) == false)
hibernateConfig = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, hibernateConfig);
if (File.Exists(hibernateConfig))
_configuration.Configure(new XmlTextReader(hibernateConfig));
}
return _configuration;
}
}
Note that I have defined a class variable _configuration such as that I only execute the configuration logic once during the life time of the application. Note further that in this implementation I assume that the configuration of NHibernate is defined via the hibernate.cfg.xml file which must be in the same directory as the test assembly.
As soon as I have a valid configuration I can create a NHibernate session factory. Note that this is a rather expensive operation (takes some time depending on the number of entities you are mapping) and thus should only be executed once during the life time of the application. The access to the NHibernate SessionFactory is thread safe!
Let's write a test for the creation of the SessionFactory.
[Test]
public void Can_create_and_access_session_factory()
{
var sessionFactory = _factory.SessionFactory;
Assert.IsNotNull(sessionFactory);
Assert.AreEqual("NHibernate.Dialect.MsSql2005Dialect", sessionFactory.Dialect.ToString());
}
This is a rather basic test and only checks whether I can successfully create a session factory and whether at least one of its properties is configured as I expect it, namely the dialect used.
The implementation to fulfill the test is rather simple
public ISessionFactory SessionFactory
{
get
{
if (_sessionFactory == null)
_sessionFactory = Configuration.BuildSessionFactory();
return _sessionFactory;
}
}
I have defined a class variable _sessionFactory such that I only execute the BuildSessionFactory method once during the life time of the application. The session factory is created on demand, that is when first needed. At the same time this property getter method accesses the previously defined Configuration property which in turn triggers the configuration of NHibernate (if not already done).
The last missing piece is the implementation of the CreateSession method which now is trivial
private ISession CreateSession()
{
return SessionFactory.OpenSession();
}
I want to be able to access the current (open) session via my factory. If there is no such session open at the moment a meaningful exception should be raised. To test this write
[Test]
public void Accessing_CurrentSession_when_no_session_open_throws()
{
try
{
var session = _factory.CurrentSession;
}
catch (InvalidOperationException)
{ }
}
and the implementation is easy
public ISession CurrentSession
{
get
{
if (_currentSession == null)
throw new InvalidOperationException("You are not in a unit of work.");
return _currentSession;
}
set { _currentSession = value; }
}
The last method to implement is the UoW disposal method. In this method I want to reset the CurrentSession to null and then forward the call to our static UnitOfWork class (defined in the first part of this post series) such as that this class can also make an internal clean-up. The implementation might look as follows
public void DisposeUnitOfWork(UnitOfWorkImplementor adapter)
{
CurrentSession = null;
UnitOfWork.DisposeUnitOfWork(adapter);
}
That's all we have to do for now regarding the UoW factory. Again we have implemented it in a TDD way. When we later re-factor our implementation to make it thread-safe this TDD approach will be a huge benefit since we have a complete test coverage of out code.
The Unit of Work Implementor
This class defines an actual Unit of Work instance. It implements the already mentioned (and defined) interface IUnitOfWork which in turn inherits from IDisposal. This allows us to work with a UoW using the following syntax
using(UnitOfWork.Start())
{
// use Unit of Work
}
at the end of the block the Dispose method of the UoW will be called. So let's first think of a test for the implementation of this method. Here we have to use mocking to be able to test our SUT (system under test) in isolation. So let me setup a test fixture first.
[TestFixture]
public class UnitOfWorkImplementor_Fixture
{
private readonly MockRepository _mocks = new MockRepository();
private IUnitOfWorkFactory _factory;
private ISession _session;
private IUnitOfWorkImplementor _uow;
[SetUp]
public void SetupContext()
{
_factory = _mocks.DynamicMock<IUnitOfWorkFactory>();
_session = _mocks.DynamicMock<ISession>();
}
}
Again I use Rhino.Mocks as my mocking framework. My UoW instance has two external dependencies namely the UnitOfWork factory and the NHibernate session. In the SetupContext I generate a dynamic mock for each of them.
Now I can write my test method
[Test]
public void Can_Dispose_UnitOfWorkImplementor()
{
using(_mocks.Record())
{
Expect.Call(() => _factory.DisposeUnitOfWork(null)).IgnoreArguments();
Expect.Call(_session.Dispose);
}
using (_mocks.Playback())
{
_uow = new UnitOfWorkImplementor(_factory, _session);
_uow.Dispose();
}
}
During the recording phase I define my expectations (of what should happen when calling the Dispose method of the UoW). First the DisposeUnitOfWork method of the factory and second the Dispose method of the session object should be called.
Here is the code which fulfills this test
public void Dispose()
{
_factory.DisposeUnitOfWork(this);
_session.Dispose();
}
As you can see I just forward the call to the Unit of Work factory and dispose my internal (NHibernate) session instance as formulated in the expectations of the test.
When working with a UoW I also have to be able to use transactions. Thus I need a way to "play" with transactions. Let's call the corresponding methods BeginTransaction and TransactionalFlush.
Generic Transaction
To shield the client code from all NHibernate specifics and to provide a simple(r) interface I define a GenericTransaction class (which implements the interface IGenericTransaction) which is just a wrapper around the NHibernate transaction.
I think that the implementation is trivial and doesn't need any further explanation
public interface IGenericTransaction : IDisposable
{
void Commit();
void Rollback();
}
public class GenericTransaction : IGenericTransaction
{
private readonly ITransaction _transaction;
public GenericTransaction(ITransaction transaction)
{
_transaction = transaction;
}
public void Commit()
{
_transaction.Commit();
}
public void Rollback()
{
_transaction.Rollback();
}
public void Dispose()
{
_transaction.Dispose();
}
}
just note that the IGenericTransaction inherits form IDisposable.
With this definition of a generic transaction we can proceed to the implementation of the BeginTransaction and TransactionalFlush methods.
First I'll implement the BeginTransaction method. As usual I define the test first
[Test]
public void Can_BeginTransaction()
{
using(_mocks.Record())
{
Expect.Call(_session.BeginTransaction()).Return(null);
}
using (_mocks.Playback())
{
_uow = new UnitOfWorkImplementor(_factory, _session);
var transaction = _uow.BeginTransaction();
Assert.IsNotNull(transaction);
}
}
I expect that during execution of the method under test the BeginTransaction method of the NHibernate session is called. After calling BeginTransaction on the UoW (in the Playback phase) I additionally test whether I get a not-null transaction.
I also want to be able to start a transaction and define an isolation level for the transaction. The corresponding test might look like
[Test]
public void Can_BeginTransaction_specifying_isolation_level()
{
var isolationLevel = IsolationLevel.Serializable;
using(_mocks.Record())
{
Expect.Call(_session.BeginTransaction(isolationLevel)).Return(null);
}
using (_mocks.Playback())
{
_uow = new UnitOfWorkImplementor(_factory, _session);
var transaction = _uow.BeginTransaction(isolationLevel);
Assert.IsNotNull(transaction);
}
}
To fulfill the test(s) I just have to create a new instance of a generic transaction and pass the result of the _session.BeginTransaction call to the constructor (that is: a NHibernate ITransaction object)
public IGenericTransaction BeginTransaction()
{
return new GenericTransaction(_session.BeginTransaction());
}
public IGenericTransaction BeginTransaction(IsolationLevel isolationLevel)
{
return new GenericTransaction(_session.BeginTransaction(isolationLevel));
}
The methods each return me an instance of type GenericTransaction which I can use in my code and call their respective commit or rollback methods if appropriate.
The TransactionalFlush method should flush the content of the NHibernate session to the database wrapped inside a transaction. Let's define the test(s) for it
[Test]
public void Can_execute_TransactionalFlush()
{
var tx = _mocks.CreateMock<ITransaction>();
var session = _mocks.DynamicMock<ISession>();
SetupResult.For(session.BeginTransaction(IsolationLevel.ReadCommitted)).Return(tx);
_uow = _mocks.PartialMock<UnitOfWorkImplementor>(_factory, _session);
using (_mocks.Record())
{
Expect.Call(tx.Commit);
Expect.Call(tx.Dispose);
}
using (_mocks.Playback())
{
_uow = new UnitOfWorkImplementor(_factory, session);
_uow.TransactionalFlush();
}
}
[Test]
public void Can_execute_TransactionalFlush_specifying_isolation_level()
{
var tx = _mocks.CreateMock<ITransaction>();
var session = _mocks.DynamicMock<ISession>();
SetupResult.For(session.BeginTransaction(IsolationLevel.Serializable)).Return(tx);
_uow = _mocks.PartialMock<UnitOfWorkImplementor>(_factory, session);
using (_mocks.Record())
{
Expect.Call(tx.Commit);
Expect.Call(tx.Dispose);
}
using (_mocks.Playback())
{
_uow.TransactionalFlush(IsolationLevel.Serializable);
}
}
and the implementation is straight forward
public void TransactionalFlush()
{
TransactionalFlush(IsolationLevel.ReadCommitted);
}
public void TransactionalFlush(IsolationLevel isolationLevel)
{
IGenericTransaction tx = BeginTransaction(isolationLevel);
try
{
//forces a flush of the current unit of work
tx.Commit();
}
catch
{
tx.Rollback();
throw;
}
finally
{
tx.Dispose();
}
}
Note that when you don't provide an explicit transaction isolation level our implementation automatically assumes an isolation level equal to "read commited". Note also that if the Commit of the transaction fails the transaction is rolled back.
Important: In the case of an exception during TransactionalFlush do not try to reuse the current unit of work since the session is in an inconsistent state and must be closed. Thus abandon this UoW and start a new one.
To increase the usability of the UoW instance a little bit I add the following members (which need no further explanation)
public bool IsInActiveTransaction
{
get
{
return _session.Transaction.IsActive;
}
}
public IUnitOfWorkFactory Factory
{
get { return _factory; }
}
public ISession Session
{
get { return _session; }
}
public void Flush()
{
_session.Flush();
}
That's it! We have now a simple implementation of the Unit of Work pattern for NHibernate. Let me provide an overall picture of our implementation
Please remember: In this current version the UoW is strictly NOT thread-safe! But I'll show you how to make it thread-safe in the next post.
Using the Unit of Work
In the following section I'll quickly show the typical usage of the UoW. As usual I do this via TDD. I want to demonstrate how one can define a new instance of an entity and add it to the database. In this case the entity is a simple Person type object.
Let us first set up a test fixture as follows
using System;
using System.Reflection;
using NHibernate.Tool.hbm2ddl;
using NUnit.Framework;
namespace NHibernateUnitOfWork.Tests
{
[TestFixture]
public class Test_usage_of_UnitOfWork
{
[SetUp]
public void SetupContext()
{
UnitOfWork.Configuration.AddAssembly(Assembly.GetExecutingAssembly());
new SchemaExport(UnitOfWork.Configuration).Execute(false, true, false, false);
}
}
}
In the SetupContext method I first add the current assembly (the one in which this test is running) as a source of the schema mapping information to the configuration of NHibernate (which I can access via our UnitOfWork class). It is important that this call is done prior to the creation of the SessionFactory (that is prior to using a session).
After extending the configuration I use the SchemaExport class of NHibernate to automatically generate the database schema for me.
But wait: up to now I have not defined any schema at all! So let's do it now. Let's define a simple Person entity. Add a new file Person.cs to the test project and add the following code
public class Person
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual DateTime Birthdate { get; set; }
}
add an XML mapping document called Person.hbm.xml to the test project and define the following content
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="NHibernateUnitOfWork.Tests"
namespace="NHibernateUnitOfWork.Tests">
<class name="Person">
<id name="Id">
<generator class="guid"/>
</id>
<property name="Name" not-null="true" length="50"/>
<property name="Birthdate" not-null="true"/>
</class>
</hibernate-mapping>
Don't forget to set the 'Build Action' property of this item to 'Embedded Resource'!
Now we can write a test to insert a new person instance into the database.
[Test]
public void Can_add_a_new_instance_of_an_entity_to_the_database()
{
using(UnitOfWork.Start())
{
var person = new Person {Name = "John Doe", Birthdate = new DateTime(1915, 12, 15)};
UnitOfWork.CurrentSession.Save(person);
UnitOfWork.Current.TransactionalFlush();
}
}
When running this test the output generated by NHibernate looks similar to this
NHibernate: INSERT INTO Person (Name, Birthdate, Id) VALUES (@p0, @p1, @p2);
@p0 = 'John Doe', @p1 = '15.12.1915 00:00:00',
@p2 = 'bb72ae4c-7d64-4c16-84dc-c0ba2c7d306b'
Summary
I have shown you a possible implementation of the Unit of Work pattern for NHibernate. The implementation has been developed by using TDD. 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.
There are just two open points
- our implementation in not thread safe
- we still need to access the session object (through UnitOfWork.CurrentSession) to read or modify our entites
The former I'll cover in my next post and the latter problem can be solved by introducing a (generic) repository which I'll also discuss in one of the next posts.
Any feedback and suggestions for improvement is welcome.
Enjoy
.