How to map Enumeration class without passing discriminator to constructor?

Based on the Jumy Enumeration classes , I want to see if I can avoid using the constructor to create an instance of my type (which I suppose comes from discriminator-value), but rather use the factory method - a great way to get my instance mapped to db.

Here is my type:

public class Impact : Enumeration
{
    public static readonly Impact Carbon
        = new Impact(1, "Carbon dioxide equivalent", CommonUnit.CO2e);
    public static readonly Impact Energy
        = new Impact(2, "Energy", CommonUnit.MJ);
    public static readonly Impact Cost
        = new Impact(3, "Cost", CommonUnit.Dollars);



    public Impact(int index, string name, CommonUnit unit)
        : base(index, name)
    {
        this.Unit = unit;
    }


    public CommonUnit Unit { get; private set; }

}

And here is the definition for Enumeration:

public class Enumeration : ValueObject
{
    public Enumeration(int index, string displayName)
    {
        this.Index = index;
        this.DisplayName = displayName;
    }


    public int Index { get; private set; }
    public string DisplayName { get; private set; }


    public override string ToString()
    {
        return this.DisplayName;
    }


    public static IEnumerable<T> GetAllFor<T>() where T : Enumeration
    {
        foreach (var publicStatic in typeof(T).GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly))
        {
            Enumeration item = null;
            item = (Enumeration)publicStatic.GetValue(null);
            yield return item as T;
        }
    }

    public static T With<T>(int index) where T : Enumeration
    {
        return GetAllFor<T>().SingleOrDefault(i => i.Index == index);
    }
}

ValueObject just overrides the functionality of Equality.

Elsewhere, I use static methods to get the elements from this enumeration (sort of like you can use the static methods of Core Enumeration):

impact = Impact.With<Impact>(index.ImpactId.Value);

This is very convenient, but I want to know if I can force NHibernate to do this too when rehydrating objects.

?

+3
2

NHibernate:

public class EnumerationType<T> : PrimitiveType where T : Enumeration
{
    public EnumerationType()
        : base(new SqlType(DbType.Int32))
    {
    }

    public override object Get(IDataReader rs, int index)
    {
        object o = rs[index];
        var value = Convert.ToInt32(o);
        return Enumeration.With<T>(value);
    }

    public override object Get(IDataReader rs, string name)
    {
        int ordinal = rs.GetOrdinal(name);
        return Get(rs, ordinal);
    }

    public override Type ReturnedClass
    {
        get { return typeof(T); }
    }

    public override object FromStringValue(string xml)
    {
        return int.Parse(xml);
    }

    public override string Name
    {
        get { return "Enumeration"; }
    }

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

        var val = (Enumeration)value;

        parameter.Value = val.Value;
    }

    public override string ObjectToSQLString(object value, Dialect dialect)
    {
        return value.ToString();
    }

    public override Type PrimitiveClass
    {
        get { return typeof(int); }
    }

    public override object DefaultValue
    {
        get { return 0; }
    }
}

HBM.xml, :

<property name="Impact" column="Impact" type="Namespace.To.EnumerationType`1[[Impact, AssemblyWithDomainEnum]], AssemblyWithNHibCustomType"/>

, Fluent NHibernate, :

public class EnumerationTypeConvention : IPropertyConvention, IPropertyConventionAcceptance
{
    private static readonly Type _openType = typeof(EnumerationType<>);

    public void Apply(IPropertyInstance instance)
    {
        var closedType = _openType.MakeGenericType(instance.Property.PropertyType);

        instance.CustomType(closedType);
    }

    public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
    {
        criteria.Expect(x => typeof(Enumeration).IsAssignableFrom(x.Property.PropertyType));
    }
}

, , Fluent NHibernate.

+4

, , , , :

public class ImpactEnumType : IUserType
{
    public SqlType[] SqlTypes
    {
        get
        {
            //We store our Impact in a single column in the database that can contain a int (for the index value)
            SqlType[] types = new SqlType[1];
            types[0] = new SqlType(DbType.Int32);
            return types;
        }
    }

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

    public bool Equals(object x, object y)
    {
        // Impact is derived from ValueObject which implements Equals
        return x.Equals(y);
    }

    public int GetHashCode(object x)
    {
        // as above
        return x.GetHashCode();
    }

    public object NullSafeGet(IDataReader rs, string[] names, object owner)
    {
        //We get the string from the database using the NullSafeGet used to get ints
        int impactIndex = (int)NHibernateUtil.Int32.NullSafeGet(rs, names[0]);

        // then pull the instance from the Enumeration type using the static helpers
        return Impact.With<Impact>(impactIndex);
    }

    public void NullSafeSet(IDbCommand cmd, object value, int index)
    {
        //Set the value using the NullSafeSet implementation for int from NHibernateUtil
        if (value == null)
        {
            NHibernateUtil.Int32.NullSafeSet(cmd, null, index);
            return;
        }
        value = (value as Impact).Index;
        NHibernateUtil.Int32.NullSafeSet(cmd, value, index);
    }

    public object DeepCopy(object value)
    {
        //We deep copy the Impact by creating a new instance with the same contents
        if (value == null) return null;
        return Impact.With<Impact>((value as Impact).Index);
    }

    public bool IsMutable
    {
        get { return false; }
    }

    public object Replace(object original, object target, object owner)
    {
        //As our object is immutable we can just return the original
        return original;
    }

    public object Assemble(object cached, object owner)
    {
        //Used for casching, as our object is immutable we can just return it as is
        return cached;
    }

    public object Disassemble(object value)
    {
        //Used for casching, as our object is immutable we can just return it as is
        return value;
    }
}

HBM XML:

<property name="Impact" column="ImpactIndex" type="namespace.childnamespace.ImpactEnumType, namespace.childnamespace" />
+1

All Articles