This is the first of a series of articles on the topic. Other posts will follow
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
and has the benefit of
You can download the source code for the Fluent NHibernate project from here.
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.
I have to declare to the mapping framework where the mapping classes can be found. For this I define a persistence model class which inherits from the base class PersistenceModel.
public class TestModel : PersistenceModel
{
public TestModel()
{
addMappingsFromAssembly(typeof(ProductMap).Assembly);
}
}
In the constructor I define that all of my mapping classes can be found in the assembly which also contains the mapping class for the Product entity. If the mapping classes can be found in the current assembly I could also use the method addMappingsFromThisAssembly(). To just add a specific mapping I could use addMapping(new ProductMap()).
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
{
protected SessionSource SessionSource { get; set; }
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()
{
SessionSource = new SessionSource(new TestModel());
Session = SessionSource.CreateSession();
SessionSource.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
The After_each_test method just closes and disposes the current session. In our case (we are using SqLite as database) this also means that the database (schema) is destroyed.
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);
}
}
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.
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>(Session)
.CheckProperty(x=>x.Name, "Apple")
.CheckProperty(x=>x.UnitPrice, 0.25m)
.CheckProperty(x=>x.UnitsOnStock, 2345)
.CheckProperty(x=>x.Discontinued, true)
.VerifyTheMappings();
}
That's a nice reduction in lines of code. No "noise" any more, just the essential is left.
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