Nhibernate 如何在不将鉴别器传递到构造函数的情况下映射枚举类?

Nhibernate 如何在不将鉴别器传递到构造函数的情况下映射枚举类?,nhibernate,orm,Nhibernate,Orm,基于这个想法,我想看看我是否可以避免(我假设是发生在鉴别器值上),而是使用“工厂方法”(factory method)——类似于从数据库映射实例的方法 这是我的类型: public class Impact : Enumeration { public static readonly Impact Carbon = new Impact(1, "Carbon dioxide equivalent", CommonUnit.CO2e); public static

基于这个想法,我想看看我是否可以避免(我假设是发生在
鉴别器值
上),而是使用“工厂方法”(factory method)——类似于从数据库映射实例的方法

这是我的类型:

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

}
下面是
枚举的定义:

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);
    }
}
这是相当方便,但我想知道我是否可以让NHibernate做这件事时,再水化对象


使用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; }
    }
}
公共类EnumerationType:原语类型,其中T:枚举
{
公共枚举类型()
:base(新的SqlType(DbType.Int32))
{
}
公共重写对象Get(IDataReader rs,int索引)
{
对象o=rs[索引];
var值=转换为32(o);
返回枚举。带(值);
}
公共重写对象Get(IDataReader rs,字符串名称)
{
int ordinal=rs.GetOrdinal(名称);
返回Get(rs,序号);
}
公共重写类型ReturnedClass
{
获取{return typeof(T);}
}
公共重写对象FromStringValue(字符串xml)
{
返回int.Parse(xml);
}
公共重写字符串名
{
获取{返回“枚举”;}
}
公共覆盖无效集(IDbCommand cmd,对象值,int索引)
{
var参数=(IDataParameter)命令参数[索引];
var val=(枚举)值;
参数值=值值;
}
公共重写字符串ObjectToSQLString(对象值,方言)
{
返回值.ToString();
}
公共重写类型基元类
{
获取{return typeof(int);}
}
公共覆盖对象默认值
{
获取{返回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));
    }
}
公共类枚举类型约定:IPropertyConvention,IPropertyConvention Acceptance
{
私有静态只读类型_openType=typeof(枚举类型);
公共无效应用(IPropertyInstance实例)
{
var closedType=\u openType.MakeGenericType(instance.Property.PropertyType);
实例.CustomType(closedType);
}
公共无效接受(IAcceptanceCriteria标准)
{
Expect(x=>typeof(枚举).IsAssignableFrom(x.Property.PropertyType));
}
}

然后在流畅的NHibernate配置中添加您喜欢的约定。

这似乎也可行,但可能更容易:

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;
    }
}
公共类ImpactEnumType:IUserType
{
公共SqlType[]SqlTypes
{
得到
{
//我们将影响存储在数据库中的一列中,该列可以包含int(用于索引值)
SqlType[]types=新的SqlType[1];
types[0]=新的SqlType(DbType.Int32);
返回类型;
}
}
公共类型返回类型
{
获取{return typeof(Impact);}
}
公共布尔等于(对象x、对象y)
{
//影响来自ValueObject,它实现了Equals
返回x等于(y);
}
公共int GetHashCode(对象x)
{
//如上
返回x.GetHashCode();
}
公共对象NullSafeGet(IDataReader rs,字符串[]名称,对象所有者)
{
//我们使用NullSafeGet从数据库中获取字符串,用于获取int
int impactIndex=(int)NHibernateUtil.Int32.NullSafeGet(rs,names[0]);
//然后使用静态帮助程序从枚举类型中提取实例
返回影响。带有(影响指数);
}
public void NullSafeSet(IDbCommand cmd,对象值,int索引)
{
//使用NHibernateUtil中int的NullSafeSet实现设置值
如果(值==null)
{
NHibernateUtil.Int32.NullSafeSet(cmd,null,index);
返回;
}
值=(作为影响的值)。索引;
NHibernateUtil.Int32.NullSafeSet(cmd,value,index);
}
公共对象深度复制(对象值)
{
//我们通过创建具有相同内容的新实例来深度复制影响
if(value==null)返回null;
返回影响。带((值作为影响)。索引);
}
公共布尔可换
{
获取{return false;}
}
公共对象替换(对象原始、对象目标、对象所有者)
{
//因为我们的对象是不可变的,所以我们可以返回原始对象
归还原件;
}
公共对象集合(对象缓存,对象所有者)
{
//用于级联,因为我们的对象是不可变的,所以我们可以按原样返回它
返回缓存;
}
公共对象(对象值)
{
//用于级联,因为我们的对象是不可变的,所以我们可以按原样返回它
返回值;
}
}
我的HBM XML:

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


根据我在您的博客上找到的一些评论,我开始使用
IUserType
。这样不行吗?似乎您对
PrimitiveType
的建议在某种程度上可能与
IUserType
相关联,因为方法似乎相似。是的,PrimitiveType只是使自定义用户类型更容易。请解释Fluent映射中发生的事情的附加值。对于其他:。Fluent映射约定允许我们不必像处理HBM映射那样指定自定义用户类型。使用HBM,我们必须为每个属性指定自定义用户类型。根据约定,我们只需指定一次自定义用户类型。
<property name="Impact" column="ImpactIndex" type="namespace.childnamespace.ImpactEnumType, namespace.childnamespace" />