Implementing custom types in nHibernate

nHibernate can persist a lot of different datatypes, of course all the basic types in the .NET framework and some more sophisticated. However if you feel some type is missing go ahead and implement it. All that is required is that your type implements the IUserType interface shown below:

public interface IUserType
{
	/// <summary>
	/// The SQL types for the columns mapped by this type. 
	/// </summary>
	SqlType[] SqlTypes { get; }

	/// <summary>
	/// The type returned by <c>NullSafeGet()</c>
	/// </summary>
	System.Type ReturnedType { get; }

	/// <summary>
	/// Compare two instances of the class mapped by this type for persistent "equality"
	/// ie. equality of persistent state
	/// </summary>
	/// <param name="x" /></param>
	/// <param name="y" /></param>
	/// <returns></returns>
	bool Equals(object x, object y);

	/// <summary>
	/// Get a hashcode for the instance, consistent with persistence "equality"
	/// </summary>
	int GetHashCode(object x);

	/// <summary>
	/// Retrieve an instance of the mapped class from a JDBC resultset.
	/// Implementors should handle possibility of null values.
	/// </summary>
	/// <param name="rs" />a IDataReader</param>
	/// <param name="names" />column names</param>
	/// <param name="owner" />the containing entity</param>
	/// <returns></returns>
	/// <exception cref="HibernateException">HibernateException</exception>
	object NullSafeGet(IDataReader rs, string[] names, object owner);

	/// <summary>
	/// Write an instance of the mapped class to a prepared statement.
	/// Implementors should handle possibility of null values.
	/// A multi-column type should be written to parameters starting from index.
	/// </summary>
	/// <param name="cmd" />a IDbCommand</param>
	/// <param name="value" />the object to write</param>
	/// <param name="index" />command parameter index</param>
	/// <exception cref="HibernateException">HibernateException</exception>
	void NullSafeSet(IDbCommand cmd, object value, int index);

	/// <summary>
	/// Return a deep copy of the persistent state, stopping at entities and at collections.
	/// </summary>
	/// <param name="value" />generally a collection element or entity field</param>
	/// <returns>a copy</returns>
	object DeepCopy(object value);

	/// <summary>
	/// Are objects of this type mutable?
	/// </summary>
	bool IsMutable { get; }

	/// <summary>
	/// During merge, replace the existing (<paramref name="target" />) value in the entity
	/// we are merging to with a new (<paramref name="original" />) value from the detached
	/// entity we are merging. For immutable objects, or null values, it is safe to simply
	/// return the first parameter. For mutable objects, it is safe to return a copy of the
	/// first parameter. For objects with component values, it might make sense to
	/// recursively replace component values.
	/// </summary>
	/// <param name="original" />the value from the detached entity being merged</param>
	/// <param name="target" />the value in the managed entity</param>
	/// <param name="owner" />the managed entity</param>
	/// <returns>the value to be merged</returns>
	object Replace(object original, object target, object owner);

	/// <summary>
	/// Reconstruct an object from the cacheable representation. At the very least this
	/// method should perform a deep copy if the type is mutable. (optional operation)
	/// </summary>
	/// <param name="cached" />the object to be cached</param>
	/// <param name="owner" />the owner of the cached object</param>
	/// <returns>a reconstructed object from the cachable representation</returns>
	object Assemble(object cached, object owner);

	/// <summary>
	/// Transform the object into its cacheable representation. At the very least this
	/// method should perform a deep copy if the type is mutable. That may not be enough
	/// for some implementations, however; for example, associations must be cached as
	/// identifier values. (optional operation)
	/// </summary>
	/// <param name="value" />the object to be cached</param>
	/// <returns>a cacheable representation of the object</returns>
	object Disassemble(object value);
}

Most of these is comments(which are rather useful and hinting the implementation), actually you need to implement only 8 methods and a couple of properties. The most straightforward thing to implement is immutable types, one good example is System.Uri which is immutable and can keep an Uri. When we store this in our database we can just save it as a string but in our application it would be nice to get features for getting the domain etc. and these are built into the System.Uri class so why not make nHibernate return instances of this instead of strings. So lets go ahead and implement the IUserType interface:

public class UriType : IUserType
{
    public SqlType[] SqlTypes
    {
        get
        {
            //We store our Uri in a single column in the database that can contain a string
            SqlType[] types = new SqlType[1];
            types[0] = new SqlType(DbType.String);
            return types;
        }
    }

    public System.Type ReturnedType
    {
        get { return typeof(Uri); }
    }

    public new bool Equals(object x, object y)
    {
        //Uri implements Equals it self by comparing the Uri's based 
        // on value so we use this implementation
        if (x == null)
        {
            return false;
        }else
        {
            return x.Equals(y);
        }
    }

    public int GetHashCode(object x)
    {
        //Again URL itself implements GetHashCode so we use that
        return x.GetHashCode();
    }

    public object NullSafeGet(IDataReader rs, string[] names, object owner)
    {
        //We get the string from the database using the NullSafeGet used to get strings 
        string uriString = (string) NHibernateUtil.String.NullSafeGet(rs, names[0]);
        
        //And save it in the Uri object. This would be the place to make sure that your string 
        //is valid for use with the System.Uri class, but i will leave that to you
        Uri result = new Uri(uriString);
        return result;
    }

    public void NullSafeSet(IDbCommand cmd, object value, int index)
    {
        //Set the value using the NullSafeSet implementation for string from NHibernateUtil
        if (value == null)
        {
            NHibernateUtil.String.NullSafeSet(cmd, null, index);
            return;
        }
        value = value.ToString(); //ToString called on Uri instance
        NHibernateUtil.String.NullSafeSet(cmd, value, index);
    }

    public object DeepCopy(object value)
    {
        //We deep copy the uri by creating a new instance with the same contents
        if (value == null) return null;
        return new Uri(value.ToString());
    }

    public bool IsMutable
    {
        get { return false; }
    }

    public object Replace(object original, object target, object owner)
    {
        //As our object is immutable we can just return the original
        return original;
    }
    
    public object Assemble(object cached, object owner)
    {
        //Used for casching, as our object is immutable we can just return it as is
        return cached;
    }

    public object Disassemble(object value)
    {
        //Used for casching, as our object is immutable we can just return it as is
        return value;
    }
}

So that's it, pretty straight forward as we have an immutable type and using an already existing type as storage. This allows us to use this types NullSafeSet/NullSafeGet implementations.

To use this in our application and write code like this:

Website intellectWebsite = new Website();
intellectWebsite.Url = new Uri("http://www.intellect.dk/");
sess.Save(intellectWebsite);

We have to specify our type in the mapping file, its pretty easy we just have to tell nHibernate what type our property is, that is UriType in this case:

<class name="Website">
	<id name="ID">
		<generator class="native" />
	</id>
	<!-- this is the interresting line the type supplied is the name of our class with full namespace and the assembly it resides in -->
	<property name="Url" type="MyCompany.MyApplication.UriType, MyCompany.MyApplication"></property>
</class>

Thats it, now nHibernate knows how to persist an Uri and how to retrieve it again.


Comments

March 18. 2008 06:05 PM

Mark S. Rasmussen

A couple of initial thoughts:

Why would we not make a generic interface instead? That'd be a lot cleaner so we could avoid all the type unsafe object returns. It would also eliminate the need for the ReturnedType property.
What's the need for the SqlTypes property? And does SqlTypes[0] map to the SqlType of Column[0]? It seems ugly to me.

Mark S. Rasmussen

March 18. 2008 10:38 PM

Jakob Andersen

There is a quite simple explanation for the use of non generic types, nHibernate has been around for a long time also before generics was available on the platform.

And it works, custom user types isn't something you write everyday. They are easily testable and when tested not likely to suddenly fail, so one could argue that this is code that needs to be written and probably not be touc again unless something drastic happens.

Hovever it could be refactored to make use of generics to make usertypes easier to read but it has a lot of impact in the nHibernate codebase so its not just a 5 minute job(and it would be a breaking change as far as i can tell).


The reason SqlTypes don't refer to column index but refers to the types of the different columns this usertype spans as usertypes can span several columns.

Jakob Andersen

January 11. 2009 05:05 PM

pingback

Pingback from blog.jagregory.com

James Gregory - Fluent NHibernate: Auto Mapping Type Conventions

blog.jagregory.com

January 19. 2009 10:48 AM

Harry M

I've written a generic implementation - see www.adverseconditionals.com/.../...ue-objects.html

Harry M

February 10. 2009 08:06 PM

pingback

Pingback from darrell.mozingo.net

Taller Code » Blog Archive » Generic NHibernate User Type Base Class

darrell.mozingo.net

October 16. 2009 04:09 AM

pingback

Pingback from randomcode.net.nz

Simplify IUserType Testing « Random Code

randomcode.net.nz

Comments are closed