如何在不将鉴别器传递给构造函数的情况下映射 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`。