我有一个旧架构,其中包含主键为binary(16)的表 - 它是其他列的MD5哈希值。 NHibernate不能使用byte []作为键,因为它没有实现Equals所以我将它包装在一个自定义类型中,并为NHibernate提供了IUserType的实现。请注意,MD5Hash是结构而不是类。
public struct MD5Hash : IComparable, IComparable<MD5Hash>, IEquatable<MD5Hash> {
private readonly byte[] contents;
...
}
在我使用MD5Hash作为密钥的类型创建多对一映射之前,一切正常。
public class Referenced : IEquatable<Referenced> {
...
public virtual MD5Hash Id { get; set; }
public virtual string Name { get; set; } // must NOT be null
...
}
public class Referencer : IEquatable<Referencer> {
...
public virtual MD5Hash Id { get; set; }
public virtual Referenced Other { get; set } // may be null
...
}
当我尝试加载Referencer类型的对象时,当行包含NULL值时,NHibernate看不到键的空值,因此它尝试实例化类型的对象 引用,将其分配给Referencer,并更新数据库中的Referencer。由于Referenced有一个属性Name,它映射到一个不可为空的列,NHibernate引发了一个异常。我想要的是NHibernate将Other属性设置为null。
我可以将MD5Hash的定义更改为类而不是结构但是我在代码中有一个未知数量的位置,可能假设MD5Hash永远不能为空,所以我正在寻找另一种解决方案。
自定义类型的代码......
internal class MD5HashType : IUserType {
public SqlType[] SqlTypes {
get { return new[] { new SqlType(DbType.Binary, 16) }; }
}
public Type ReturnedType {
get { return typeof(MD5Hash); }
}
public new bool Equals(object x, object y) {
return Object.Equals(x, y);
}
public int GetHashCode(object x) {
return (null == x) ? 0 : x.GetHashCode();
}
public object NullSafeGet(IDataReader rs, string[] names, object owner) {
var val = NHibernateUtil.Binary.NullSafeGet(rs, names[0]);
return (null == val || DBNull.Value == val) ? MD5Hash.Empty : new MD5Hash((byte[])val);
}
public void NullSafeSet(IDbCommand cmd, object value, int index) {
var val = (MD5Hash.Empty == ((MD5Hash)value)) ? null : ((MD5Hash)value).ToByteArray();
NHibernateUtil.Binary.NullSafeSet(cmd, val, index);
}
public object DeepCopy(object value) {
return value;
}
public bool IsMutable {
get { return false; }
}
public object Replace(object original, object target, object owner) {
return original;
}
public object Assemble(object cached, object owner) {
return cached;
}
public object Disassemble(object value) {
return value;
}
}
答案 0 :(得分:0)
问题似乎是NHibernate不能告诉MD5Hash.Empty意味着没有价值。您是否尝试过创建自定义事件侦听器(如下所示)来处理此问题?类似的东西:
public class CustomLoadListener : DefaultLoadEventListener {
public override void OnLoad(LoadEvent @event, LoadType loadType) {
if(@event.EntityId is MD5Hash) {
var id = (MD5Hash) @event.EntityId;
if(id == MD5Hash.Empty) {
@event.Result = new Referenced { Id = MD5Hash.Empty };
return;
}
}
base.OnLoad(@event, loadType);
}
}
public class CustomSaveOrUpdateListener : DefaultSaveOrUpdateEventListener {
public override void OnSaveOrUpdate(SaveOrUpdateEvent @event) {
var entity = @event.Entity as Referenced;
if(entity != null && entity.Id == MD5Hash.Empty) {
return;
}
base.OnSaveOrUpdate(@event);
}
}
然后,您必须通过hibernate.cfg.xml在会话工厂中配置这些侦听器:
<session-factory>
<!-- various properties -->
<listener type="load" class="NhHacking.CustomLoadListener, NhHacking"/>
<listener type="save-update" class="NhHacking.CustomSaveOrUpdateListener, NhHacking"/>
</session-factory>
如果有人对如何做到这一点有了更好的了解,我很乐意听到。