Everyone knows the benefits of using enums in code.  However, when you use NHibernate to store those enums to a database, all you get are the numeric values those enums represent.  I wanted a way to take the logic I was using to make the numeric values in the database into a business value a user could make use of.

With this problem in mind, I began looking for a solution.  My first thought was to simply override ToString, but a) that can’t be done, and b) even if it could, it wouldn’t allow round tripping of the data.  NHibernate makes available a class called EnumStringType which would allow me to round trip the data successfully, but is ultimately limited to using the ToString function of the enum.  This poses some limitations when the value you want to be placed in the database is more complex than the enum allows.  For instance, you couldn’t make an make an enum value that has special characters in it.  This led me to two posts, Jeffrey Palermo and abhinaba both provided information which I used as the basis for my solution. 

By combining the idea of using EnumStringType with the idea of keeping the String I wanted to show up in the database as an attribute of the enum’s value I came to the following solution.  I also used an extension of Jeff Palermo’s idea posted by Oran Dennison to make the entire functionality generic enough to be able to be used for any enum.  All that needs to be done is drop the following two classes into your project and then setting up your NHibernate mappings (see below for an example).

My extension of the EnumStringType:

Imports System

Imports System.Data

Imports System.Reflection

Imports NHibernate.Type

 

Public Class GenericEnumMapper(Of TEnum)

    Inherits EnumStringType

 

    Public Sub New()

        MyBase.New(GetType(TEnum))

    End Sub

 

    'Get the enum corresponding to the value in the database

    Public Overrides Function GetInstance(ByVal code As Object) As Object

        Dim infos As FieldInfo() = ReturnedClass.GetFields()

        If Not infos Is Nothing AndAlso infos.Length > 0 Then

            For Each fieldInfo As FieldInfo In infos

                Dim attributes As Object() = fieldInfo.GetCustomAttributes(GetType(BusinessDescriptionAttribute), False)

                If Not attributes Is Nothing AndAlso attributes.Length > 0 Then

                    Dim attribute As BusinessDescriptionAttribute = TryCast(attributes(0), BusinessDescriptionAttribute)

                    If Not attribute Is Nothing Then

                        Dim description As String = attribute.Description

                        If description = code.ToString Then

                            Return System.Enum.Parse(ReturnedClass, fieldInfo.Name, True)

                        End If

                    End If

                End If

            Next

        End If

        Return MyBase.GetInstance(code)

    End Function

 

    'Set the value to be stored in the database

    Public Overrides Sub [Set](ByVal cmd As IDbCommand, ByVal value As Object, ByVal index As Integer)

        Dim parameter As IDataParameter = TryCast(cmd.Parameters(index), IDataParameter)

        parameter.Value = DBNull.Value

        If Not value Is Nothing Then

            parameter.Value = System.Enum.Format(ReturnedClass, value, "G")

            Dim infos As FieldInfo() = ReturnedClass.GetFields()

            For Each fieldInfo As FieldInfo In infos

                Dim attributes As Object() = fieldInfo.GetCustomAttributes(GetType(BusinessDescriptionAttribute), False)

                If Not attributes Is Nothing AndAlso attributes.Length > 0 Then

                    Dim attribute As BusinessDescriptionAttribute = TryCast(attributes(0), BusinessDescriptionAttribute)

                    If Not attribute Is Nothing Then

                        If fieldInfo.Name = value.ToString Then

                            Dim description As String = attribute.Description

                            parameter.Value = description

                        End If

                    End If

                End If

            Next

        End If

    End Sub

End Class

The Attribute definition used to mark the values of an enum:

Imports System

 

Public Class BusinessDescriptionAttribute

    Inherits Attribute

 

    Private _description As String

 

    Public Sub New(ByVal description As String)

        _description = description

    End Sub

 

    Public ReadOnly Property Description() As String

        Get

            Return _description

        End Get

    End Property

 

End Class

An example of the NHibernate mappings:

 <property name="Property" column="Column" type="SomePackage.GenericEnumMapper`1[[SomePackage.SomeEnum, SomePackage]], SomePackage" update="true" access="field.camelcase-underscore"/>

An example of how to use the attributes with the enum:

 Public Enum SomeEnum

    'You don’t have to put a BusinessDescription attribute on every value

    UNKNOWN

 

    <BusinessDescription("This value is special")> _

    AValue

 

    <BusinessDescription("This value is not quite as special")> _

    AnotherValue

 

    <BusinessDescription("Actually, this value is the most special…>.>")> _

    NoValueHere

End Enum