如何将 NHibernate 与 System.Tuple 等不可变类型一起使用?
Posted
技术标签:
【中文标题】如何将 NHibernate 与 System.Tuple 等不可变类型一起使用?【英文标题】:How can I use NHibernate with immutable type like System.Tuple? 【发布时间】:2012-07-04 11:18:23 【问题描述】:我有一个使用System.Tuple<int,string>
的复合映射,如下所示:
<composite-element
class="System.Tuple`2[[System.Int32, mscorlib],[System.String, mscorlib]], mscorlib">
<property name="Item1" column="DBColumn1"/>
<property name="Item2" column="DBColumn2"/>
</composite-element>
我尝试搞乱BytecodeProvider
、IObjectsFactory
、ReflectionOptimizer
等等,但我无法让 NHibernate 正确加载我的元组(无论我做什么,NHibernate 都坚持首先创建对象并填充值稍后出)。
NHibernate 能否以某种方式强制正确加载和持久化不可变类型?
【问题讨论】:
【参考方案1】:你试过ICompositeUserType
吗?它将允许您为 Tuple<int, string>
属性定义这样的映射:
<property name="MyProperty" type="MyNamespace.TupleIntStringType, MyAssembly">
<column name="Item1"/>
<column name="Item2"/>
</property>
自定义类型定义为:
public class TupleIntStringType : ICompositeUserType
public object GetPropertyValue(object component, int property)
var tuple = (Tuple<int, string>)component;
switch (property)
case 0:
return tuple.Item1;
case 1:
return tuple.Item2;
default:
throw new InvalidOperationException(String.Format("No property number 0 found", property));
public void SetPropertyValue(object component, int property, object value)
throw new InvalidOperationException("Immutable, SetPropertyValue is not allowed");
public new bool Equals(object x, object y)
if (ReferenceEquals(x, y)) return true;
if (x == null || y == null) return false;
return x.Equals(y);
public int GetHashCode(object x)
return x == null ? 0 : x.GetHashCode();
public object NullSafeGet(IDataReader dr, string[] names, ISessionImplementor session, object owner)
var item1 = (int)PropertyTypes[0].NullSafeGet(dr, names[0], session, owner);
var item2 = (String)PropertyTypes[1].NullSafeGet(dr, names[1], session, owner);
return Tuple.Create(item1, item2);
public void NullSafeSet(IDbCommand cmd, object value, int index, bool[] settable, ISessionImplementor session)
if (value == null)
NHibernateUtil.Timestamp.NullSafeSet(cmd, null, index);
NHibernateUtil.TimeSpan.NullSafeSet(cmd, null, index + 1);
else
var tuple = (Tuple<int, String>)value;
PropertyTypes[0].NullSafeSet(cmd, tuple.Item1, index, session);
PropertyTypes[1].NullSafeSet(cmd, tuple.Item2, index + 1, session);
public object DeepCopy(object value)
var tuple = (Tuple<int, String>)value;
return Tuple.Create(tuple.Item1, tuple.Item2);
public object Disassemble(object value, ISessionImplementor session)
return DeepCopy(value);
public object Assemble(object cached, ISessionImplementor session, object owner)
return DeepCopy(cached);
public object Replace(object original, object target, ISessionImplementor session, object owner)
return DeepCopy(original);
public string[] PropertyNames get return new[] "Item1", "Item2" ;
public IType[] PropertyTypes get return new IType[] NHibernateUtil.Int32, NHibernateUtil.String ;
public Type ReturnedClass get return typeof(Tuple<int, string>);
public bool IsMutable get return false;
这里有几个例子:Money object and NHibernate ICompositeUserTypeMapping Timestamp data using NHibernate's ICompositeUserTypeUsing NHibernate ICompositeUserType with a value type
您的<composite-element>
映射然后更改为(如NHIbernate: How to map a bag with an ICompositeUserType 中所示):
<element type="MyNamespace.TupleIntStringType, MyAssembly">
<column name="DBColumn1" />
<column name="DBColumn2" />
</element>
【讨论】:
我没有编辑答案以防我遗漏了什么,但鉴于 ICompositeUserType 的示例实现,映射示例实际上不应该是类型:“MyNamespace.TupleCompositeUserType, MyAssembly”吗? 是的,你是对的。我已更改类名以匹配属性/元素映射中使用的名称。谢谢。以上是关于如何将 NHibernate 与 System.Tuple 等不可变类型一起使用?的主要内容,如果未能解决你的问题,请参考以下文章
NHibernate.Spatial 和 Sql 2008 地理类型 - 如何配置