如何在没有将鉴别器传递给构造函数的情况下映射Enumeration类?

时间:2012-05-01 03:48:23

标签: nhibernate orm

根据Jimmy's Enumeration classes想法,我想知道我是否可以避免using the constructor to instantiate my type(我假设发生在discriminator-value),而是使用“工厂方法” - 以方式从数据库中映射我的实例。

这是我的类型:

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; }

}

以下是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简单地涵盖了平等功能。

在其他地方,我使用静态方法从这个枚举中获取项目(有点像你可以使用核心Enumeration静态方法):

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

这非常方便,但我想知道在补充物体时我是否也可以让NHibernate这样做。

能做到吗?怎么做?

2 个答案:

答案 0 :(得分:4)

使用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配置中添加该约定。

答案 1 :(得分:1)

这似乎也有效,但也许Jimmy's way似乎更容易:

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" />