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

Posted

技术标签:

【中文标题】如何在不将鉴别器传递给构造函数的情况下映射 Enumeration 类?【英文标题】:How do I map an Enumeration class without the discriminator being passed into the constructor? 【发布时间】:2012-05-01 03:48:22 【问题描述】:

基于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 只是掩盖了平等功能。

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

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

这很方便,但我想知道是否可以让 NHibernate 在为对象补水时也这样做。

能做到吗?怎么做?

【问题讨论】:

【参考方案1】:

使用 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 配置中添加您喜欢的约定。

【讨论】:

根据我在您的博客上找到的一些 cmets,我开始使用 IUserType。这行不通吗?看起来您对PrimitiveType 的建议实际上可能会在某些时候与IUserType 相关,因为这些方法看起来很相似。 是的,PrimitiveType 只是让自定义用户类型更容易 您能解释一下 Fluent 映射中发生的事情的附加价值吗? 其他人:Jimmy kindly blogged about this in more detail. 流畅的映射约定让我们不必像使用 HBM 映射那样指定自定义用户类型。使用 HBM,我们必须为每个属性指定自定义用户类型。按照惯例,我们只需指定自定义用户类型 ONCE。【参考方案2】:

这似乎也有效,但也许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" />

【讨论】:

以上是关于如何在不将鉴别器传递给构造函数的情况下映射 Enumeration 类?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不将图像保存在本地的情况下将捕获的图像(Surface View)传递给另一个片段?

是否可以在不将编码器传递给 json.dumps() 的情况下转储 json 中的枚举?

我们如何在不将 ViewController 对象推入其中的情况下将对象分配给 `UINavigationController`。

构造函数不将初始数组值传递给方法

如何在不使用向量的情况下将数组传递给函数?

设置自定义异常的消息而不将其传递给基本构造函数