Asbestos Supply

2010-01-20 Mapping Serializable types using Fluent NHibernate

Yesterday I posted a question to StackOverflow (http://stackoverflow.com/questions/2097364/mapping-to-serializabletype-in-fluent-nhibernate) asking whether anyone could tell me how to map a Serializable type using Fluent NHibernate.  The outcome was disappointing to say the least.  In 24 hours there have been less than 20 views.  It was voted up twice, but no comments or answers were posted.

Even worse, there's another post on StackOverflow (http://stackoverflow.com/questions/2000798/map-to-serializable-in-fluent-nhibernate) that asks this same question and has an answer that's incorrect (it compiles but throws an exception at runtime).

Serializable types in NHibernate (XML)

Before I continue, I'll give a little background.  Feel free to skip this part.  Suppose you want to map the following object (contrived, I know):

public class Person
{
    public virtual string FirstName { get; set; }
    public virtual string LastName { get; set; }
    public virtual BirthCertificate BirthCertificate { get; set; }
}
public class BirthCertificate
{
    public virtual DateTime DOB { get; set;}
    public virtual string BirthCity { get; set; }
    public virtual string BirthState { get; set; }
    public virtual string HospitalName { get; set;}
}

Imagine, however, that you don't want to map the BirthCertificate fields to a table.  Imagine you want to serialize the BirthCertificate object and store it in a single column in the Person table.

NHibernate makes this really easy.  When mapping the BirthCertificate property on Person, you would write something like this:

<property name="BirthCertificate" column="BirthCertificate" type="Serializable"  />

and of course you would have to add a [Serializable] attribute to the BirthCertificate class like so:

[Serializable]
public class BirthCertificate
{
.......

Mapping Serializable using Fluent NHibernate

This is actually surprisingly easy.

The Fluent interface provides a CustomType method that allows you to map your entity to a custom, IUserType.  The incorrect answer that was posted on the old StackOverflow post (link above) mentioned combining this CustomType method with the SerializableType type, like this:

CustomType();

That will build, but it throws an Exception at runtime:

An invalid or incomplete configuration was used while creating a SessionFactory. Check PotentialReasons collection, and InnerException for more detail.

InnerException:

Could not instantiate IType SerializableType: System.MissingMethodException: No parameterless constructor defined for this object.
    at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandle& ctor, Boolean& bNeedSecurityCheck)
    at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean fillCache)
    at System.RuntimeType.CreateInstanceImpl(Boolean publicOnly, Boolean skipVisibilityChecks, Boolean fillCache)
    at System.Activator.CreateInstance(Type type, Boolean nonPublic)
    at System.Activator.CreateInstance(Type type)
    at NHibernate.Bytecode.ActivatorObjectsFactory.CreateInstance(Type type)
    at NHibernate.Type.TypeFactory.HeuristicType(String typeName, IDictionary`2 parameters)

I'm not a contributor to the Fluent NHibernate project (though I'm considering it after some other things calm down and I have more time), so I wasn't particularly familiar with the codebase.  In order to find the solution, I downloaded the latest code and stepped through it to see how it works.

Basically, the fluent interface collects all your mapping settings and then outputs the appropriate hbm's at configure-time.  Using the incorrect answer above resulted in the following mapping for the BirthCertificate property:

<property name="BirthCertificate" type="NHibernate.Type.SerializableType, NHibernate,Version=2.1.2.4000, Culture=neutral, PublicKeyToken=aa95f207798dfdb4">
<column name="BirthCertificate" />

Hey – that should read type=”Serializable”!  We don't want the whole Type info there!

Turns out one of the overloads of the CustomType method takes….. you guessed it (!)…. a String!

The following is the correct way of mapping a Serializable type in Fluent NHibernate

CustomType("Serializable");

Yes I feel stupid for not figuring this out from the start!  One comfort though is that it doesn't seem that many people know this, even though it seems so obvious.

Stupid as I feel, I also feel like I gained a lot from the hour I spent on this; I finally got to delve into the Fluent NHibernate code.