Los Techies : Blogs about software and anything tech!

Mapping Strings to Booleans Using NHibernate's IUserType


Update: I failed to realized that the functionality of converting "Y" or "N" to a boolean is already built into NHibernate by doing type="YesNo" (see comments). I'll leave this post up just for the academics of creating a IUserType. Go figure that I would come up with something that's built in!

We have some legacy data where boolean values are stored as strings. "Y" is true and "N" is no. We're also creating entities which have properties that map to this data. It would be just plain evil to have to create all the boolean properties as strings! We previously solved this problem by creating our own .NET type that had a string property for the "Y" or "N" value. This type had an implicit cast defined as a bool, so we could treat it as a boolean in expressions and assignments. We mapped this in NHibernate as a component with the single string property and this worked pretty well, but I suspected that there was a better way. Today I bought the e-book version of NHibernate in Action and quickly read through the section on creating an implementation of IUserType. I then came up with the following class that allows me to keep my types as true booleans in the code.

public class YesNoType : IUserType
{
    public bool IsMutable
    {
        get { return false; }
    }

    public Type ReturnedType
    {
        get { return typeof(YesNoType); }
    }

    public SqlType[] SqlTypes
    {
        get { return new[]{NHibernateUtil.String.SqlType}; }
    }

    public object NullSafeGet(IDataReader rs, string[] names, object owner)
    {
        var obj = NHibernateUtil.String.NullSafeGet(rs, names[0]);

        if(obj == null ) return null;

        var yesNo = (string) obj;

        if( yesNo != "Y" && yesNo != "N" )
            throw new YourException("Expected data to be 'Y' or 'N' but was '{0}'.", yesNo);

        return yesNo == "Y";
    }

    public void NullSafeSet(IDbCommand cmd, object value, int index)
    {
        if(value == null)
        {
            ((IDataParameter) cmd.Parameters[index]).Value = DBNull.Value;
        }
        else
        {
            var yes = (bool) value;
            ((IDataParameter)cmd.Parameters[index]).Value = yes ? "Y" : "N";
        }
    }

    public object DeepCopy(object value)
    {
        return value;
    }

    public object Replace(object original, object target, object owner)
    {
        return original;
    }

    public object Assemble(object cached, object owner)
    {
        return cached;
    }

    public object Disassemble(object value)
    {
        return value;
    }

    public new bool Equals(object x, object y)
    {
        if( ReferenceEquals(x,y) ) return true;

        if( x == null || y == null ) return false;

        return x.Equals(y);
    }

    public int GetHashCode(object x)
    {
        return x == null ? typeof(bool).GetHashCode() + 473 : x.GetHashCode();
    }
}

The mapping looks something like:

<property name="Active" type="NHTypes.YesNoType, NHTypes" column="ACTIVE_YN" not-null="false" />

I'm not going to go into detail and explain what all the parts are and do (see link), but I thought that I would share the class. Please let me know if you spot any problems.

Technorati Tags: ,
Kick It on DotNetKicks.com
Posted Mar 23 2008, 01:03 AM by Ray Houston

Comments

Ken Scott wrote re: Mapping Strings to Booleans Using NHibernate's IUserType
on 03-23-2008 10:32 AM

Once you have created this user type and reference it in your Domain model, is there any way to not have a reference to NHibernate in the Domain model?

I've done something similar (created a user type for the version column to allow mapping of a SQL Server rowversion column to something other than Byte[]). It works fine, but I feel "dirty" seeing that reference to NHibernate in the model.

Do I need to just get over it?

Thanks,

Ken Scott

Ray Houston wrote re: Mapping Strings to Booleans Using NHibernate's IUserType
on 03-23-2008 10:47 AM

@Ken - that's the cool thing about this. My domain doesn't reference my YesNoType. It my domain, I use plain old booleans. It might be less confusing if I had named this class YesNoMapper. It's used for translating the database types, to .NET types.  The .NET type can be anything you want as long as you supply the logic here to translate it.

Ken Scott wrote re: Mapping Strings to Booleans Using NHibernate's IUserType
on 03-23-2008 3:00 PM

So, the classes that you have talking directly to NHibernate are just a form of mapper or DTO class? Interesting...

How much churn do you have in your model that forces you to update the intermediate layer and/or the database? Do you have some sort of reflection/code generation to create an entity class from a DTO and vice versa?

Ray Houston wrote re: Mapping Strings to Booleans Using NHibernate's IUserType
on 03-23-2008 5:05 PM

@Ken - I think we're talking about two different things.

In my entity I may have a property like

public bool Active { get; set; }

In the database, the column is a string (because it's legacy). My custom IUserType only shows up in the NH mapping and no where else. NH invokes it for me. It expects a string to come out of the database and then converts it to a bool (and vise versa going the other way). This all happens under the hood and my domain classes never know that it's stored as a string in the database.

I'm going to post another example which might clear things up a bit.

Ray Houston wrote Mapping Timestamp Data Using NHibernate's ICompositeUserType
on 03-23-2008 10:21 PM

In my previous post , I took some string data and mapped it directly to a boolean property on an entity

Matt wrote re: Mapping Strings to Booleans Using NHibernate's IUserType
on 03-24-2008 5:22 AM

This is already built into NHibernate.. do type=YesNo

www.hibernate.org/.../html_single

Ray Houston wrote re: Mapping Strings to Booleans Using NHibernate's IUserType
on 03-24-2008 7:10 AM

@Matt - dang! How did I miss that? Anyway, thanks for pointing that out.

Rune wrote re: Mapping Strings to Booleans Using NHibernate's IUserType
on 12-22-2008 9:33 AM

Just spotted this post. I have a similar problem and this solution does not seem directly applicable. I am from Denmark which means that instead of using Y/N in the database my predecessors opted for J/N. Can I somehow still use type="YesNo" or do I have to use your YesNoType class?

Dru Sellers wrote Business Primitives (2/2)
on 02-04-2010 6:19 AM

Ok, so now that we have talked about the Business Primitive concept lets go through some of the actual

Add a Comment

(required)  
(optional)
(required)  
Remember Me?

Enter the numbers above:
Copyright Los Techies 2008, 2009. All rights reserved.
Powered by Community Server (Commercial Edition), by Telligent Systems