Do you like NHibernate? Do you like XML? My answer would be yes for the former and no for the latter. But if you want to map your entities to the underlying database tables you have no other choice than use XML. Ok, you are right, we still have the possibility to use attributes for the mapping (e.g. by using Castle Active Record) but in this case we are "polluting" our nice domain objects with infrastructure related information which definitely does NOT belong into the domain model.
Here comes our salvation. We now have a third player in the field. As first published by Jeremy D. Miller and later on by others (James Gregory, Bobby Johnson, Zachariah Young, etc. ) we can map our domain objects by using a fluent interface which solves the following possible problems
- Changing the property names of a domain model can break the NHibernate mapping
- Changing the database fields can break the NHibernate mappings
and has the benefit of
- we can write the mappings in plain old C#
- the mapping is more expressive
- the mapping is better testable
You can download the source code for the Fluent NHibernate project from here.
Sample
Let's make a quick sample. You can download it's source here.
In our domain model we have a Product class defined as follows
public class Product
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
public virtual Decimal UnitPrice { get; set; }
public virtual int UnitsOnStock { get; set; }
public virtual bool Discontinued { get; set; }
}
This class we can now easily map to the underlying database with this code
public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Id(x => x.Id);
Map(x => x.Name);
Map(x => x.UnitPrice);
Map(x => x.UnitsOnStock);
Map(x => x.Discontinued);
}
}
Now let's write a quick test to see whether we can really e.g. add a new product to the underlying database. In our case the database used for tests is the SqlLite database.
Prerequisites
I have to write a helper class which will be my session source. The mapping framework already contains such a session source but its implementation does not fit my needs. Since it is not a complicate class I implement my own one. The session source class has to implement the interface ISessionSource also found in the mapping framework. Here is the code
public class MySessionSource : ISessionSource
{
private readonly ISessionFactory _sessionFactory;
private readonly Configuration _configuration;
public MySessionSource(PersistenceModel model) : this(null, model)
{
}
public MySessionSource(IDictionary<string, string> properties, PersistenceModel model)
{
if(model == null) throw new ArgumentException("Model cannot be null!");
_configuration = new Configuration();
if (properties == null)
_configuration.Configure();
else
_configuration.AddProperties(properties);
Model = model;
model.Configure(_configuration);
_sessionFactory = _configuration.BuildSessionFactory();
}
public PersistenceModel Model { get; private set; }
public ISession CreateSession()
{
return _sessionFactory.OpenSession();
}
public void BuildSchema()
{
var session = CreateSession();
BuildSchema(session);
}
public void BuildSchema(ISession session)
{
var connection = session.Connection;
var drops = _configuration.GenerateDropSchemaScript(_sessionFactory.Dialect);
executeScripts(drops, connection);
var scripts = _configuration.GenerateSchemaCreationScript(_sessionFactory.Dialect);
executeScripts(scripts, connection);
}
private static void executeScripts(string[] scripts, IDbConnection connection)
{
foreach (var script in scripts)
{
var command = connection.CreateCommand();
command.CommandText = script;
command.ExecuteNonQuery();
}
}
}
I have two overloaded constructors. I can either provide only the persistence model I use or pass the model and the necessary NHibernate configuration as dictionary of properties. In the former case the configuration is loaded from the usual hibernate.cfg.xml configuration file. The constructor then creates a new NHibernate configuration instance and uses the passed persistence model instance to feed the mapping information into the configuration instance.
The class also implements a method the create (or open) an new NHibernate session instance.
Finally we have a BuildSchema method which is overloaded. The second overload is needed when testing with SqlLite as database since when using this database in the in-memory mode (which we do in our unit tests) the database is destroyed whenever the session is closed. So, if I use a session instance to create the schema and then another for the test, then the latter does not have access to the database created by the former.
Base class for unit tests
I now write a base class for all unit test that I'll implement. This base class should be responsible for the (re-)creation of the database schema before each test runs. It should also open a new session object before each test which I can then use in my unit tests. Finally the base class should close an dispose the session after each test. Here is the code
public class FixtureBase
{
private MySessionSource _source;
protected ISession Session { get; private set; }
[SetUp]
public void SetupContext()
{
Before_each_test();
}
[TearDown]
public void TearDownContext()
{
After_each_test();
}
protected virtual void Before_each_test()
{
_source = new MySessionSource(new TestModel());
Session = _source.CreateSession();
_source.BuildSchema(Session);
CreateInitialData(Session);
Session.Clear();
}
protected virtual void After_each_test()
{
Session.Close();
Session.Dispose();
}
protected virtual void CreateInitialData(ISession session)
{
}
}
Note that the SetUp and TearDown methods just delegate to protected virtual methods Before_each_test and After_each_test respectively. The latter two methods can be overridden in any child class. In the Before_each_test method I use the session source class to a) create a new session instance and b) (re-)build the database schema. A call to the virtual method CreateInitialData is executed and then the session is cleared. The CreateInitialData method can be used in the child classes to setup the respective context for the unit tests.
The After_each_test method just closes and disposes the session.
The first unit test
Finally we can write our first test and verify whether the mapping with the fluent interface really works. First we want to try to add a new Product to the database. With all the prerequisites in place this is now easy. See the code below
[TestFixture]
public class Product_Fixture : FixtureBase
{
[Test]
public void Can_add_product_to_database()
{
var product = new Product
{
Name = "Apple",
UnitPrice = 0.25m,
UnitsOnStock = 1255,
Discontinued = false
};
Session.Save(product);
// Assertion
Session.Flush();
Session.Clear();
var fromDb = Session.Get<Product>(product.Id);
Assert.AreNotSame(product, fromDb);
Assert.AreEqual(product.Name, fromDb.Name);
Assert.AreEqual(product.UnitPrice, fromDb.UnitPrice);
Assert.AreEqual(product.UnitsOnStock, fromDb.UnitsOnStock);
Assert.AreEqual(product.Discontinued, fromDb.Discontinued);
}
}
We define a new test class which inherits from our base class FixtureBase. The we define the test which creates a new instance of type Product and populates the various properties with sample data. We then call the Save method of the session and pass the product as a parameter.
Then we flush and clear the session and reload the Product from database. We assert that it has really been loaded from the database and not just taken out of NHibernate's first level cache (to avoid that we have flushed and cleared the session). Then we assert that the values of the properties all match.
Unit test revisited
If we have a lot of entities then we potentially have lot's of repetitive code to implement just to unit test all of our entities as above. Hey but wait a minute. There is a "better" way provided by the mapping framework. We can use the PersistenceSpecification class to eliminate the repetitive work. Let's look how our code will be
[Test]
public void Can_add_product_to_database_revisited()
{
new PersistenceSpecification<Product>(SessionSource)
.CheckProperty(x=>x.Name, "Apple")
.CheckProperty(x=>x.UnitPrice, 0.25m)
.CheckProperty(x=>x.UnitsOnStock, 2345)
.CheckProperty(x=>x.Discontinued, true);
}
That's a nice reduction in lines of code. No "noise" any more, just the essential is left.
Note: Unfortunately this second approach has a side effect which in my opinion is not nice! The entity must inherit from the base class Entity provided by the mapping framework. This introduces a dependency in the domain model (the domain model must reference the mapping framework) and also is against the principle of POCO (plain old C# object). Maybe I want to provide a different entity base class. Well there is some room for improvement left...
Summary
If you want a clean domain model free from pollution by mapping attributes you had to define the mapping between entities and the underlying database with XML documents. XML is not very wrist friendly and also not very readable. Now there is a third alternative to define the mapping of entities to database - the fluent NHibernate API. One can now define the mapping in C# with a nice and very readable code.
As always you can download the code for the sample here.
Enjoy
.