Non-random thoughts.

2007-08-11

NHibernate enum to char(1)

For a new project we are using NHibernate to implement database persistance. Today I ran into an issue with persisting enum's. By default, NHibernate uses type in the database and the enum's underlying type, in order to generate values. For example a simple enum like this:


public enum CoffeType
{
Sugar,
NoSugar
}

with this mapping file:

<property column="CoffeType" type="Business.Entities.CoffeType, Business.Core" name="CoffeType" null="true">

and a column in the database of type integer means that Sugar becomes 0 and NoSugar becomes 1. This is all fine, and works without problems at all. However, I wanted to use predetermined values for the enums, like this:


public enum CoffeType
{
Sugar = 'S',
NoSugar = 'N'
}


I changed the type in the database from integer to Char(1). This made NHibernate puke when inserting data. After digging into the generated sql-statements, it turned out that NHibernate choose to convert my enum-values into "Sugar" and "NoSugar" instead of 'S' and 'N'. This is probably due to recognizing any Char-type in the database as a String-datatype in .NET, and converting the enum's appropriately.

A few minutes of searching the net turned up the following post:

http://blog.bittercoder.com/CategoryView,category,NHibernate.aspx

.. which solved the exact same problem, with Castle's ActiveRecord instead of NHibernate. It turns out that the solution is called NHibernate User Types. This is what I ended up with in the mapping-file:

<property column="CoffeType" type="Business.Entities.CoffeTypeType, Business.Core" name="CoffeType" null="true" />

and this is the corresponding C#-class:


public class CoffeTypeType : NHibernate.Type.PersistentEnumType
{
public CoffeTypeType()
: base(typeof(CoffeType))
{
}

public override object GetValue(object code)
{
return Convert.ToChar(code);
}

public override object GetInstance(object code)
{
return (CoffeType) Enum.ToObject(typeof(CoffeType), (Byte) ((Char) GetValue(code)));
}

public override void Set(IDbCommand cmd, object value, int index)
{
IDataParameter par = (IDataParameter)cmd.Parameters[index];
par.DbType = DbType.StringFixedLength;

if (value == null) {
par.Value = DBNull.Value;
}
else {
par.Value = GetValue(value);
}
}

public new static SqlType GetUnderlyingSqlType(System.Type enumClass)
{
if (Enum.GetUnderlyingType(enumClass).FullName == "System.Char") {
return NHibernate.SqlTypes.SqlTypeFactory.GetString(1);
}
else {
return NHibernate.Type.PersistentEnumType.GetUnderlyingSqlType(enumClass);
}
}
}


This is the way to make NHibernate map an enum property to char(1) database field.