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: ,

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Ray Houston

Ray is a software development leader and architect with 20 years hands-on experience. He enjoys finding elegant solutions to complex problems and delivering real value to customers. Ray is a speaker and contributor to community events.
This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

9 Responses to Mapping Strings to Booleans Using NHibernate’s IUserType

  1. Ken Scott says:

    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

  2. Ray Houston says:

    @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.

  3. Ken Scott says:

    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?

  4. Ray Houston says:

    @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.

  5. Ray Houston says:

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

  6. Rune says:

    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?

  7. eric swann says:

    Cool, glad I found this article Ray, I was just trying to do a wierd date conversion from our DB, I think this approach is what I’m looking for.

  8. Pingback: FluentNhibernate: map byte enum as char | trouble86.com

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>