.NET and me Coding dreams since 1998!

3Jan/096

Fluent NHibernate – Convention over configuration – AutoPersistenceModel auto map magic


Story about how fluent nhibernate idea was born


POST UPDATE: Due to changes in Auto mapping API this blog post is outdated. In order to see auto mapping in action with FNH 1.0 go check out http://blog.vuscode.com/malovicn/archive/2009/11/04/fluent-nhibernate-samples-auto-mapping-part-1-2.aspx


*******************************************************************************************************


Fluent NHibernate is an open source project (fluent NHibernate trunk) which implements fluent interface on top of the NHibernate ORM configuration capabilities which are based on xml configuration files (as most of the Java ports are) where every entity is defined in it’s own configuration file.


The main reason behind existence of the Fluent NHibernate project is that working with those configuration files is not something very productive and (in general) it is one of the major turning off points for developers trying to get NHibernate.


As I mentioned in my Using NHibernate without configuration files post, it is possible to replace the act of creation of configuration files with the c# code where instead of every configuration file defining entities we would have separate mapping class.  That functionality per se is awesome improvement comparing to configuration files, but it is still cumbersome work requiring a lot of repetitive work.


Here are couple of illustration of what kind of repentance I have on my mind with previous sentence:



  1. My tables are having the same name as my entities (just in plural form)

  2. I could have 50 entity classes where every identity property would be called ID and in database I would always have primary key consisting of “table name + ID”

  3. Data table foreign key can be always defined following the naming rule of “parent table name + ID”

  4. Most of my text based columns have 255 length characters

  5. etc

Those rules are changing but each one of us (personally or on organization level) has a certain style of doing this things, predefined code and database standards on organization level or just agreement on team level. Based on that fact of convention existence, The Don Ayende asked for some magic to be added to Fluent NHibernate. His point was that he would like to be able to describe easily what the conventions (similar to the one I’ve enlisted above) are used in ORM area of the project and then based on those conventions fluent NHibernate should generate all of the configuration files automatically. Once those default configuration files state  would be achieved, the developer would then modify configuration mappings to cover only the cases being an exception from the conventions.


Brilliant minds behind Fluent NHibernate get their heads together and implemented that AutoMap functionality Ayende was asking and I was recently playing with it and want to share my experiences with the community.


(Source code of the example used in this blog post can be found here)


Blog post set up


I’ll use the same example as the one I’ve used in  Using NHibernate without configuration files post. The good side of that decision is that you would be able to see same example done on both (manual and auto map) ways but on the flip side (to understand in detail what is the use case scenario etc) you would have to read the original post because here I would just summarize it.


Class diagram


image


SQL Database model


image


Couple of major points to be noted here:



  • Customer has an address (Customer.CustomerAddress property) and a collection of reference people handling his requests (References property)

  • SQL Data model has two tables while class diagram has 3 classes. The difference is that Address class data on database level is embedded into customer table. In NHibernate parlance, that is called component.

  • Class diagrams all have their identity property named “ID” while database primary key columns are named following the “table name + ID” rule.

  • Database table names are plural form of class entities. (Customer –> Customers, ReferencePerson –> ReferencePeople)

  • ReferencePerson class has property named LastName while ReferencePeople table has column named SurName

  • All of the entities implement IEntity which is totally not necessary for this example andor your code but is something so commonly used that most of the examples on the web contain base entity class or interface. That’s why I added IEntity to this example to illustrate how AutoMap works in that use case too

Project structure


image



The project structure of the solution used in this example looks like this:



  • BusinessLayer project contains entities presented in calss diagram without referencing infrastructure (PIPOCO design criteria)


  • NHibernateInfrastructure
    project contains two files:

    • Inflector – this class is “borrowed” from Active Record code base and it sole purpose is to pluralize entity names to database table names

    • NHibernateBootstrapper.cs is a class which performs complete initialization of NHibernate engine based on auto map and manual definitions and result with NHibernate session being created at the end of build up process. Most of this blog post would be related to this class

  • NHibernateInfrastructure.Test project contains test fixture (MappingTest) similar to the one from the manual map post.


An example of Fluent NHibernate auto mapping functionality



Before I start


Unfortunately there are no official documents presenting “the right way” to do this auto mapping. Most of the examples on net are covering same trivial example and they are not very helpful too. (Btw, lack of documentation was the reason behind writing this blog post) .I am by no mean expert in fluent NHibernate so while the code presented in this blog post is fully functional there might be some better ways how to do some of the presented functionality. In other words, take the example code bellow more as a starting point and spend some time with Fluent NHibernate examining it. It would be very worth spent time, I can promise you that.


Now the disclaimer is done, let’s roll :)


High level overview


Here’s the content of NHibernateBootstrapper.cs class (warning: significant amount of code coming but worth of reading I hope :) )


public ISessionFactory InitSessionFactory()
{
var config =
MsSqlConfiguration.MsSql2005
.ConnectionString.Is(
@"Data Source=.SQL2008;Initial Catalog=NHibernateBlog;"
+ @"Integrated Security=True")
.UseReflectionOptimizer()
.ConfigureProperties(new Configuration());

// 1. defining conventions and scope of the auto persistence model
var autoPersistenceModel = GetPersistenceModelFromAutoMapping();

// 2. defining exclusions form auto persistence
UpdatePersistenceModelWithExclusions(autoPersistenceModel);

// 3. overriding auto mapping defaults
UpdatePersistenceModelWithManualMappingInformations(autoPersistenceModel);

// 4. writing auto mapping definitions (needed just for blog post)
autoPersistenceModel.WriteMappingsTo(@"C:Temp");

// 5. Configuring NHibernate configuration using auto persistence model
autoPersistenceModel.Configure(config);

// returning session factory from given configuration
return config.BuildSessionFactory();
}


InitSessionFactory is the the only public method which is called from application assembly and which task is to return session factory created based on defined mapping informations.


Beginning of the method is related to creating Sql 2005 standard configuration (NHibernateBlog DB backup file is given in zip file accompanying this blog post)


Then the method perform 5 step configuration initialization:



  1. Create AutoPersistanceModel containing all the default mapping rules regarding how and what to auto map.

  2. Define domain elements which are to be excluded from the auto mapping persistence model definition due to the fact that they don’t fit to defined conventions

  3. Define all of the manual mapping information fore domain elements which are not auto mapped

  4. Write mappings xml configuration files to hard drive (not needed in production, just for you to check out the result of the fluent NHibernate auto mapping routine)

  5. Configure NHibernate configuration with AutoPeristenceModel initialized in steps #1 - #3

Once NHibernate is configured session factory is built and returned for further consumption.


Now when we saw high level steps, let check out how those steps look in more detail..



Creating of AutoPersistenceModel


Let see what is inside of the GetPerisicatnceModelFromAutoMapping method

namespace NHConfigMappings
{
...
private static AutoPersistenceModel GetPersistenceModelFromAutoMapping()
{
return AutoPersistenceModel.MapEntitiesFromAssemblyOf<Customer>()

// defining what is to be auto mapped
.Where(type =>
typeof(IEntity).IsAssignableFrom(type)
&& type.IsClass
&& !type.IsAbstract
&& type.Namespace == "CustomerConfiguration")

// defining convention attributes
.WithConvention(convention =>
{
convention.FindIdentity = p => p.Name == "ID";
convention.GetTableName = type => Inflector.Pluralize(type.Name);
convention.GetPrimaryKeyNameFromType = type => type.Name + "ID";
convention.GetForeignKeyNameOfParent = p => p.Name + "ID";
convention.DefaultStringLength = 50;
convention.OneToManyConvention = o => o.Cascade.All();
});
}
...
}


AutoPersistenceMode has a static factory method MapEntitiesFromAssemblyOf<T> where T is a type defined in a assembly containing entities which are about to be auto mapped. Very cool way for providing assembly name information. In the case of this blog post, Customer type is defined in BusinessLayer asembly, so this line would provide that information to fluent nhibernate auto mapping logic.


Then there’s a Where method which purpose is to define what types of a given assembly are to be mapped. In a sense it defines filter constraint criteria for auto mapping logic. In the case of this blog post, filter criteria is set to something like “all non abstract types implementing the IEntity which are defined in CustomerConfiguration namespace” . Any type not matching those rules wouldn’t be included in auto mapping process.


Once I’ve defined “what”, I start defining “how” by defining auto mapping conventions which are set of general settings defining how auto mapping logic should behave.


In the case of this blog post, conventions are defined like this:



  • Every class would have identity property with name “ID” (If your property would be named “Id” there won’t be need for explicit definition because that is default setting Fluent Nhibernate uses)

  • Database table name should be plural form of the class entity name (example: class Customer –> table Customers)

  • Primary key  column name  would consist of type name and “ID” suffix  (example: PK of Customer table would be named CustomerID)

  • Foreign key column name would consist of parent type name and ID suffix   (example: FK of ReferencePeople table would be CustomerID)

  • Every string class property would be by default mapped to 50 character length column

  • Every One – To – Many mapping would have Cascade.All cascade setting by default


Defining exclusions from AutoPersistenceModel definition


Now when I defined default mapping rules, I need to define what needs to be excluded from that auto mapping


 

namespace NHConfigMappings
{
private static void UpdatePersistenceModelWithExclusions(AutoPersistenceModel persistenceModel)
{
persistenceModel
.ForTypesThatDeriveFrom<Customer>(c => c.IgnoreProperty(p => p.CustomerAddress))
.ForTypesThatDeriveFrom<ReferencePerson>(c => c.IgnoreProperty(p => p.LastName));
}
...
}

 


As we can see here I’ve defined next exclusions:



  • Cutomer.CustomerAddress property shouldn’t be auto mapped (because that would be Component and not One-To-Many type of mapping)

  • ReferencePerson.LastName  property shouldn’t be auto mapped (LastName property wouldn’t be mapped to LastName column)

Adding manual mapping definition to AutoPersistenceModel


For all of the non mapped and excluded exceptional entities which we still need to map to database entities, Fluent Nhibernate defines  a way of adding manual mapping definitions to auto map defined auto persistence model


Here’s the code sample:

namespace NHConfigMappings
{
...
private static void UpdatePersistenceModelWithManualMappingInformations(AutoPersistenceModel persistenceModel)
{
// adding manual mapping to auto mapped persistence model
// 1. column level exceptions
persistenceModel.FindMapping<ReferencePerson>().Map(p => p.LastName, "SurName");

// 2. components definition
persistenceModel.FindMapping<Customer>()
.Component<Address> (
x => x.CustomerAddress,
m =>
{
m.Map(x => x.Street).WithLengthOf(100);
m.Map(x => x.PostalCode).WithLengthOf(6);
m.Map(x => x.Town).WithLengthOf(30);
m.Map(x => x.Country).WithLengthOf(50);
});
}
...
}


UpdatePersistenceModelWithManualMappingInformations method updates persistance model like this:



  • In auto persistence model definition find mapping for ReferencePerson entity and add mapping of the LastName property (to be mapped to SurName property)

  • In auto persistence model definition find mapping for Customer entity and add component mapping for CustomerAddress property as defined in code sample


Checking the xml configuration files


Let’s take a quick peek at how configuration files generated from AutoPersistenceModel would look like


CustomerConfiguration.Customer.xml


 

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-lazy="true" assembly="CustomerConfiguration" namespace="CustomerConfiguration">
<class name="ReferencePerson" table="ReferencePeople" xmlns="urn:nhibernate-mapping-2.2">
<id name="ID" column="ReferencePersonID" type="Guid">
<generator class="guid.comb" />
</id>
<property name="LastName" column="Surname" length="50" type="String">
<column name="Surname" />
</property>
<property name="FirstName" column="FirstName" length="50" type="String">
<column name="FirstName" />
</property>
</class>
</hibernate-mapping>

 


CustomerConfiguration.ReferencePerson.xml


 

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping namespace="CustomerConfiguration" assembly="CustomerConfiguration" default-lazy="true" xmlns="urn:nhibernate-mapping-2.2">
<class name="ReferencePerson" xmlns="urn:nhibernate-mapping-2.2" table="ReferencePeople">
<property name="LastName" type="String" length="50" column="Surname">
<column name="Surname" />
</property>
</class>
</hibernate-mapping>

 


Now imagine you have hundreds of tables and entities and think about how much time and effort Fluent Nhibernate auto mapping functionality saves. Neat isn’t it?


Testing the AutoPersistenceModel based auto mappings


For the end of the blog post there’s only one thing left and that is to run the same test I wrote for Using NHibernate without configuration files post and to show that auto mapping works like it was working with manual class map definition


Here’s test fixture code…

namespace NHConfigMappings.Test
{
using System.Collections.Generic;
using CustomerConfiguration;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NHibernate;

[TestClass]
public class MappingsTest
{
private static ISessionFactory _sessionFactory;

[ClassInitialize]
public static void FixtureInit(TestContext testContext)
{
_sessionFactory = new NHibernateBootstrapper().InitSessionFactory();
}

[TestMethod]
public void ReferencePerson_Create_ShouldCreateRowInDb()
{
var customer = new Customer
{
Name = "John Doe",
CustomerNumber = "12345",
CustomerAddress = new Address
{
Street = "1st Mayson Street",
PostalCode = "01754",
Town = "Maynard",
Country = "USA"
},
References = new List
{
new ReferencePerson
{
FirstName = "Nikola",
LastName = "Malovic"
}
}
};

ISession session = _sessionFactory.OpenSession();
session.Save(customer);
session.Flush();

session.Evict(customer);

var customerDB= session.Get<Customer>(customer.ID);
Assert.IsTrue( customerDB.ID == customer.ID && customerDB.Name == customer.Name
&& customerDB.CustomerAddress.Street == "1st Mayson Street"
&& customerDB.References[0].FirstName == "Nikola");

}
}
}


The main difference between tests in previous blog post and this one is that we don’t have in fixture init explicit session factory initialization. Instead, there’s only call to NHIbernateBootstrapper class InitSessionFactory method which I’ve already presented in this blog post. Test method is completely the same as the one from previous post. So it is the result of test run


image



 


Conclusions



  1. NHIbernate rock!

  2. Fluent NHibernate Auto mapping functionality rock!

There are a lot of blog posts about auto mapping functionality you might want to check out:



Filed under: Uncategorized 6 Comments