Migrating to Fluent NHibernate


Recently, I’ve been entrenched in migrating our existing hbm.xml mapping files to Fluent NHibernate.  Having been through other OSS upgrades, I was expecting something along the lines of pulling teeth.  I pictured a branch, tedious work to try and move mappings over, all the while making parallel changes to changes going on back in the trunk’s hbm files.  Instead, Fluent NHibernate supports a painless migration path, really surprising me.  I haven’t finished the migration, but all of the difficult mappings are behind me, and I haven’t run into a situation that couldn’t be migrated yet.  However, I have found a few things along the way that have helped make the migration much easier.

But first, let’s see how we can take an existing NHibernate application, chock full of hbm goodness, and start the migration to Fluent NHibernate.

Switching configuration

When migrating our mapping configuration, the name of the game is to preserve as much as we can, so that we make as few changes as possible.  The more changes we introduce, the harder time we will have figuring out if our application doesn’t work because of Fluent NHibernate not configured correctly.  Although Fluent NHibernate provides fluent code-based configuration, we’re not going to do that yet.  First, we’ll take our existing configuration strategy, whether it’s using the Configuration object, the hibernate.cfg.xml file, or .config file, and simply augment it so that we can integrate it into Fluent NHibernate’s configuration model.

In our case, we used the hibernate.cfg.xml file, but it all comes down to how you originally create the Configuration object.  One thing we need to augment in our configuration file:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns='urn:nhibernate-configuration-2.2' >
    <session-factory>
        <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
        <property name="connection.connection_string">Data Source=.sqlexpress;Initial Catalog=IBuySpy;Integrated Security=true</property>
        <property name="show_sql">false</property>

        <property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>

        <property name="cache.provider_class">NHibernate.Caches.SysCache.SysCacheProvider,NHibernate.Caches.SysCache</property>
        <property name='proxyfactory.factory_class'>NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property>
        <property name="cache.use_query_cache">true</property>
        <property name="adonet.batch_size">1000</property>
        <property name="hbm2ddl.keywords">none</property>
        <mapping assembly="IBuySpy.Core" />

    </session-factory>
</hibernate-configuration>

Is that piece at the bottom – the “mapping assembly” piece.  We need to remove that, as we’re going to switch to use Fluent NHibernate to pick up all of our entity mapping configuration.  But other than that, leave that configuration file alone!  Whether you do web.config, hibernate.cfg.xml, or programmatic configuration, leave it alone.  Again, we don’t want to change too many things at once to make the switch to Fluent NHibernate.

The next piece is to find that part in your application that creates the Configuration NHibernate object.  That object is used to create the SessionFactory, so you’ll likely only find it in one place in your application, and only called once per AppDomain.  In any case, find the piece that instantiates a Configuration object, it might look something like this:

public class NHibernateBootstrapper
{
    public static Configuration Build()
    {
        var configuration = new Configuration();

        configuration.Configure();

        return configuration;

From here, you can now download the latest Fluent NHibernate release and add a reference to whatever project you load NHibernate (the Core project for us).  Next, change that final piece that returns Configuration object and replace it with this:

public static Configuration Build()
{
    var configuration = new Configuration();

    configuration.Configure();

    return Fluently.Configure(configuration)
        .Mappings(cfg =>
        {
            cfg.HbmMappings.AddFromAssemblyOf<Customer>();
        }).BuildConfiguration();
}

The Fluently class is the main window into Fluent NHibernate configuration.  We’ll use the overload of Configure() and pass in the existing Configuration object we created through our previous setup.  In our case, this means we get all the configuration from the hibernate.cfg.xml file.  This is important because we have quite a bit of deployment code around this XML file that we’d not want to change right now.

Next, we tell FNH to load up our existing entity configuration in the form of hbm.xml configuration that’s included as embedded resources in our assembly with the Customer (or any type in the assembly where you keep your HBMs).  This lets us keep all of our existing hbm’s, but just use FNH to load them up instead.

That’s it for the initial migration.  We used our existing method for creating the Configuration object, but modified it such that Fluent NHibernate is responsible for the final Configuration object used.  We didn’t touch any of the HBMs, but merely instructed FNH to use them.

Migrating individual mapping files

Once the initial shim for Fluent NHibernate is in place, our application will work exactly as before.  But that’s not the interesting part of FNH of course, we want to start taking advantage of the nice fluent mappings.  So what’s the general migration strategy for migrating individual hbm’s?  Pretty easy:

  1. Create a ClassMap for a single entity, matching the original hbm exactly
  2. Delete the original hbm.xml
  3. Perform a schema compare to make sure nothing’s changed
  4. Commit, rinse and repeat for each class map

That’s it!  You can move hbm’s one at a time, with no need to do an all-or-nothing switch.  FNH supports side-by-side HBM and fluent mappings seemlessly, even if you do crazy things like joined subclasses split between fluent and xml files.  Of course, you will need to add your fluent mappings to the Fluenty.Configure piece, but that’s pretty straightfoward:

return Fluently.Configure(configuration)
    .Mappings(cfg =>
    {
        cfg.HbmMappings.AddFromAssemblyOf<Customer>();
        cfg.FluentMappings.AddFromAssemblyOf<Customer>();
    }).BuildConfiguration();

Now as you add the fluent maps, one at a time, don’t try to “fix” the existing maps.  You can do all that after the fluent map replaces the existing HBM.  But we don’t want to change too many things at once, and modifying the DB schema and switching to FNH is too many changes at once.

However, there are some things we can start doing to enhance our mappings as we go.

Discovering and enforcing conventions

Although HBM files themselves have sensible defaults, they do not allow us to create new defaults across all of our HBMs.  For example, we can’t tell our HBMs that all collections are accessed through fields, all foreign keys are suffixed with “Id” and so on.  However, as we add the fluent maps, we should keep an eye on any duplication in our maps.  We can do things like:

  • Define naming conventions for primary/foreign key columns
  • Set access for collections
  • Configure custom NHibernate types for your own custom types (like the Enumeration class)
  • Create inheritance hierarchies in our ClassMaps to match any layer supertype hierarchy in our entities
  • Build extension methods to encapsulate configuring common components (like that Address class everywhere)

We can still preserve our existing DB schema, but it’s quite likely that your team has already settled on things like naming conventions, access conventions and so on.  But with HBMs, you couldn’t describe these things en masse.  With FNH, we can add conventions as we go.  Keep an eye out for these common conventions early, it’s a lot easier to put conventions in place earlier rather than when your HBMs have all been converted.

I still can’t get over how easy it was to migrate to FNH, it was completely seemless and we were able to take advantage of the conventions almost immediately.  Strongly-typed mappings are great, but it’s been the conventions that have really impressed me.  If you’re still on the old HBMs, upgrade now and ditch that XML!

Hitting the upper limit of foreign key constraints