- ok AIC was a bit to depressing moving on to... youtube.com/watch?v=rCVQoG… 20 hours ago
- my sample lol while working on the #cheezburger content card.. i<3 @github http://t.co/03Bc0hEW 20 hours ago
- little AIC jar of flies & hacking on some less styles while sipping a fine brew from Olympia Coffee Roasting Company #goodmorning #cheezlife 21 hours ago
- hey @marthakelly have you met @garannm? she is also a big javascript fan... 1 day ago
- woot looks like i get to spend a week working with the Know Your Meme team haxoring on some ruby in the near future. 1 day ago
I Am Not Myself
Bills.Pay(Developer.Skills).ShouldBeTrue()
Using Fluent NHibernate ClassMaps? You’re Doing It Wrong.
Posted by on June 22, 2011
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.
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.
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.
Blahbity blahbity blah! XML is data, NHibernate is for data, so you should be using xml maps and nothing else!
Pingback: Test Driven Evolutionary Design with Entity Framework « I Am Not Myself