I Am Not Myself

Bills.Pay(Developer.Skills).ShouldBeTrue()

Using Fluent NHibernate ClassMaps? You’re Doing It Wrong.

After some internal conversations at work, I tweeted that using ClassMaps with Fluent NHibernate is the the wrong way to approach the problem. This of course was a snarky in the moment tweet as is the title of this post, but I do actually think that auto mapping is the best usage pattern for Fluent NHibernate. And I wanted to expand on that a bit and share my reasoning.

The primary reason people use Fluent NHibernate is to get away from xml based mapping files that look like this.

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
    namespace="QuickStart" assembly="QuickStart">
    <class name="Cat" table="Cat">
        <!-- A 32 hex character is our surrogate key. It's automatically
            generated by NHibernate with the UUID pattern. -->
        <id name="Id">
            <column name="CatId" sql-type="char(32)" not-null="true"/>
            <generator class="uuid.hex" />
        </id>
        <!-- A cat has to have a name, but it shouldn' be too long. -->
        <property name="Name">
            <column name="Name" length="16" not-null="true" />
        </property>
        <property name="Sex" />
        <property name="Weight" />
    </class>
</hibernate-mapping>

All of this xml goo can be replaced with a simple ClassMap that allows you to take advantage of intellesense (which technically you don’t lose with hbm files if you have your project setup right). It would look something like this.

public class CatMap : ClassMap<Cat>
    {
        public CatMap()
        {
            Id(x => x.Id).GeneratedBy.UuidHex("B");
            Map(x => x.Name)
                .WithLengthOf(16)
                .CanNotBeNull();
            Map(x => x.Sex);
            Map(x => x.Weight);
        }
    }

What have we gained by doing this? The ClassMap is just as terse as the xml. We have simply avoided having to soil our fingers with the act of typing xml. The real power with FNH is when we start moving toward convention over configuration. Our mapping then becomes as simple as this.

AutoMap.AssemblyOf<IEntity>();

This will map all of the types located in the assembly containing the type IEntity. But let’s say that you want to constrain the mapping to only types in that assembly that implement the interface IEntity or are in a specific namespace. We can specify that by providing an implementation of DefaultAutoMappingConfiguration like this.

public class AutomappingConfiguration : DefaultAutomappingConfiguration
    {
        public override bool ShouldMap(Type type)
        {
            return type.Namespace.StartsWith("Core.Model") && 
                   type.GetInterfaces().Any(y => y == typeof(IEntity));

        }

    }

AutoMap.AssemblyOf<IEntity>(new AutomappingConfiguration());

There are several methods that can be overridden globally via the automapping configuration like this. By default FNH will assume any property with the name Id is the unique identifier for that type. Maybe for some reason you want to be the property named Guid. Simply add this override to your configuration.

public class AutomappingConfiguration : DefaultAutomappingConfiguration
    {
        public override bool IsId(Member member)
        {
            return member.Name == "Guid";
        }
    }

FNH comes with a set of sensible defaults but they can be easily replaced with conventions that better match your particular database schema. In a recent project that I was on, the DBA used schemas heavily. I decided to partition my domain model using the same schema structure and put my entities in different namespaces. I then added the following convention to my automapping.

public class TableNameConvention : IClassConvention
    {
        public void Apply(IClassInstance instance)
        {
            var schema = instance.EntityType.Namespace.Split('.').Last();
            var typeName = instance.EntityType.Name;
            var tableName = "[{0}].t_{1}".FormatWith(schema, typeName);

            instance.Table(tableName);
        }
    }
AutoMap.AssemblyOf<IEntity>(new AutomappingConfiguration())
                          .Conventions.AddFromAssemblyOf<AutomappingConfiguration>()

The auto mapper call then just needs know where to pick up the custom conventions. Maybe your database does not completely follow a standard convention. Maybe it has two completely different conventions for table naming. Feel free to add as many implementations of IClassConvention as you need and then specify what they apply to by implementing IClassConventionAcceptance as well like this. Each of the convention interfaces have a corresponding acceptance interface.

public class TableNameConvention : IClassConvention, IClassConventionAcceptance
    {
        public void Apply(IClassInstance instance)
        {
            var schema = instance.EntityType.Namespace.Split('.').Last();
            var typeName = instance.EntityType.Name;
            var tableName = "[{0}].t_{1}".FormatWith(schema, typeName);

            instance.Table(tableName);
        }

        public void Accept(IAcceptanceCriteria<IClassInspector> criteria)
        {
            criteria.Expect(x => x.Name.StartsWith("foo"));
        }
    }

By far the most common response I got to my twitter sniping was the “My database is special and I have to use class maps to map it because it’s so weird” excuse. And I completely sympathize with inheriting a messy database schema. For each of the types that do not map cleanly you can provide an override like this.

public class SecurityOverride : IAutoMappingOverride<Security>
    {
        public void Override(AutoMapping<Security> mapping)
        {
            mapping.Table("[PortfolioData].[t_LocalSecurityData]");
            mapping.Id(x => x.ID, "SecurityID");
            mapping.Map(x => x.Name, "SecurityName");
            mapping.Map(x => x.Type, "SecurityType");
        }
    }

AutoMap.AssemblyOf<IEntity>(new AutomappingConfiguration())
                          .Conventions.AddFromAssemblyOf<AutomappingConfiguration>()
                          .UseOverridesFromAssemblyOf<AutomappingConfiguration>();

Notice that the auto mapping override looks surprisingly like a class map. In fact anything you can do in a class map you can do with an override. So why not choose today to begin introducing sensible conventions to your data model? Use auto mapping to map new tables and tables that get refactored to the new convention based schema. This puts you in a good place to make improvements on your schema easily instead of simply replacing xml configuration with c# class map configuration.

6 responses to “Using Fluent NHibernate ClassMaps? You’re Doing It Wrong.

  1. cbilson June 22, 2011 at 8:29 am

    Nice post!

    I especially like the way auto mapping encourages (and rewards) consistency and brevity in code. The argument against it I heat most often is that developers are afraid they won’t understand how the code works – before they even look at it. I’ve found that once I know a convention is being followed, I rarely need to go look at “how the code works” and can focus on what it does.

  2. Paul Batum June 22, 2011 at 12:38 pm

    Thanks for writing this Bobby. Now when people ask me what I think about class maps vs automapping I can just link them to your blog post.

  3. EF and XML for Life June 22, 2011 at 1:38 pm

    Blahbity blahbity blah! XML is data, NHibernate is for data, so you should be using xml maps and nothing else! 😛

  4. Pingback: Test Driven Evolutionary Design with Entity Framework « I Am Not Myself

  5. Jeremy Holovacs November 26, 2012 at 11:40 am

    Meh… most business applications are collections of exceptions. There has not been a single time where I could comprehensively adhere to a convention of any kind in any medium-sized or larger business application. Ultimately, I could stick to a generic convention for 75% of my data structures, and write exceptions for the other 25% (which is NOT a significant time-saver, IMHO) or I could be consistent in my code, defining everything. To me, it makes more sense to be consistent rather than writing as little as I have to, especially for the poor SOB that has to figure out what I did.

    I suppose if I was writing something from scratch with no dependencies, it might be a different matter.

  6. Charlie Kilian December 10, 2012 at 3:23 pm

    What you are gaining is easier refactoring. The objects aren’t in an XML file and are instead declared in code. So if you rename a property, for example, the XML mapping is automatically renamed, too.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: