Dashboard > iBATIS DataMapper > Home > Frequently Asked Questions > How do I map Enums?
How do I map Enums?
Added by Brian, last edited by Brian on Jun 24, 2008  (view change)
Labels: 
(None)


Enums are already supported provided the database field contains a string that matches a constant defined in the enumeration.

If your database strings contain spaces or other characters not allowed in an enum, this article may help.

To map enums with descriptions (aka annotations)

An example enumeration:

public enum Color
    {
    Red,
    Green,
    [System.ComponentModel.Description("Sky Blue")] SkyBlue
    }

Add this to sqlmaps.config:

<typeHandlers>
    <typeHandler type="System.Enum" callback="MyCompany.MyProject.EnumWithDescriptionTypeHandler, MyCompany.MyProject" />
    </typeHandlers>

You will need to add the EnumWithDescriptionTypeHandler and EnumHelper classes to your project:

namespace MyCompany.MyProject
    {
    public class EnumWithDescriptionTypeHandler : BaseTypeHandler
    {
    // References
            // http://opensource.atlassian.com/confluence/oss/pages/viewpage.action?pageId=17334278
            // http://opensource.atlassian.com/confluence/oss/display/IBATIS/How+do+I+use+a+Custom+Type+Handler+with+complex+property+or+Type+Safe+Enumeration
            // http://ibatis.apache.org/docs/dotnet/datamapper/ch03s05.html#id382510
    
    public override object GetDataBaseValue(object outputValue, Type parameterType)
    {
    return ValueOf(parameterType, outputValue.ToString());
    }
    
    public override object GetValueByIndex(ResultProperty mapping, IDataReader dataReader)
    {
    string value;
    if (dataReader.IsDBNull(mapping.ColumnIndex))
    {
    value = null;
    }
    else
    {
    value = dataReader.GetValue(mapping.ColumnIndex).ToString();
    }
    return ValueOf(mapping.MemberType, value);
    }
    
    public override object GetValueByName(ResultProperty mapping, IDataReader dataReader)
    {
    int ordinal = dataReader.GetOrdinal(mapping.ColumnName);
    string value;
    if (dataReader.IsDBNull(ordinal))
    {
    value = null;
    }
    else
    {
    value = dataReader.GetValue(ordinal).ToString();
    }
    return ValueOf(mapping.MemberType, value);
    }
    
    public override void SetParameter(IDataParameter dataParameter, object parameterValue, string dbType)
    {
    object value = parameterValue;
    
    if (value != null)
    {
    value = EnumHelper.ToNameOrDescription(parameterValue);
    }
    
    if (value == null)
    {
    value = DBNull.Value;
    }
    
    dataParameter.Value = value;
    }
    
    public override object ValueOf(Type type, string s)
    {
    return EnumHelper.Parse(type, s);
    }
    
    public override bool IsSimpleType
    {
    get { return true; }
    }
    }
    
    public class EnumHelper
    {
    
    public static object Parse(Type enumType, string description)
    {
    FieldInfo[] fields = enumType.GetFields(BindingFlags.Public | BindingFlags.Static);
    foreach (FieldInfo info in fields)
    {
    string databaseName = GetDescription(info);
    if (string.Compare(databaseName, description, true) == 0)
    {
    return Enum.Parse(enumType, info.Name);
    }
    }
    throw new ApplicationException(string.Format("Unable to parse '{0}' on enum {1}.", description, enumType.Name));
    }
    
    public static string ToNameOrDescription(object enumValue)
    {
    FieldInfo field = enumValue.GetType().GetField(enumValue.ToString());
    return GetDescription(field);
    }
    
    private static string GetDescription(FieldInfo fieldInfo)
    {
    DescriptionAttribute[] attributes =
    (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
    
    if (attributes != null && attributes.Length > 0)
    {
    return attributes[0].Description;
    }
    else
    {
    return fieldInfo.Name;
    }
    }
    }
    }

Unit tests covering the above EnumWithDescriptionTypeHandlerTests and EnumHelperTests (requires NUnit and RhinoMocks).

Handling nulls option 1: use a null description

Define your enum as:

public enum Color
    {
    [System.ComponentModel.Description(null)] None,
    Red,
    Green,
    [System.ComponentModel.Description("Sky Blue")] SkyBlue
    }

Handling nulls option 2: use a Nullable<T> enum

Define a class with:

public class Car
    {
    public Color? PaintedColor;
    }

Update the EnumWithDescriptionTypeHandler class above with:

public override void SetParameter(IDataParameter dataParameter, object parameterValue, string dbType)
    {
    object value = parameterValue;
    
    if (value != null)
    {
    value = EnumHelper.ToNameOrDescription(parameterValue);
    }
    
    if (value == null)
    {
    value = DBNull.Value;
    }
    
    dataParameter.Value = value;
    }
    
    private static bool IsNullableType(Type type)
    {
    return type.IsGenericType && type.GetGenericTypeDefinition() == typeof (Nullable<>);
    }
    
    private static Type GetUnderlyingType(Type nullableType)
    {
    return nullableType.GetGenericArguments()[0];
    }

However, I haven't found a way to map all Nullable<T> types to this custom type handler. You must map each enum type to the handler with:

<typeHandlers>
    <typeHandler type="System.Nullable`1[[MyCompany.MyProject.Color, MyCompany.MyProject]]" callback="MyCompany.MyProject.EnumWithDescriptionTypeHandler, TDR.Common" />
    </typeHandlers>

Please update this article if you've found a better solution.

EnumHelperTests (iBATIS DataMapper)
EnumWithDescriptionTypeHandlerTests (iBATIS DataMapper)

Site running on a free Atlassian Confluence Open Source Project License granted to OSS. Evaluate Confluence today.
Powered by Atlassian Confluence, the Enterprise Wiki. (Version: 2.5.5 Build:#811 Jul 25, 2007) - Bug/feature request - Contact Administrators