2010-11-19 14 views
0

としてカスタム型を使用して - 他の列のそのMD5ハッシュ。 NHibernateは、Equalsを実装していないので、キーとしてbyte []を扱えないので、これをカスタム型でラップし、NHibernateにIUserTypeの実装を提供しました。 MD5Hashはクラスではなく構造体であることに注意してください。NHibernateは:私は、バイナリ型(16)の主キーを持つテーブルが含まれているレガシースキーマを持っている主キー

public struct MD5Hash : IComparable, IComparable<MD5Hash>, IEquatable<MD5Hash> { 
    private readonly byte[] contents; 
    ... 
} 

MD5Hashをキーとして多対1のマッピングを作成するまでは、すべてうまくいきました。

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

Iタイプリファのオブジェクトをロードしようとすると、NHibernateの行がNULL値を含む場合、それが参照される型 のオブジェクトをインスタンス化しようとするので、キーのNULL値を参照リファに割り当てません、データベース内のReferencerを更新します。 Referencedはnullableカラムに対応するNameプロパティを持っているので、NHibernateは例外を発生させます。私が欲しいのは、NHibernateがOtherプロパティをnullに設定することです。

構造体の代わりにクラスになるようにMD5Hashの定義を変更することができますが、MD5Hashがnullにならないと仮定したコードの場所が不明なため、別の解決策を探しています。

カスタムタイプのコード...

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

MD5ハッシュIUserTypeのコードは、あなたの質問に答えるのに非常に役立ちます。 –

+0

また、PKとしてのハッシュはひどい考えです。 –

+0

@jamesコードを追加しましたが、それが役立つとは確信していません。 NHibernateのコードを踏んで、NullSafeGetの結果をヌルに対してテストし、デフォルトではではないことがわかりました。また、「保存されていない値」の値に対してテストしません。 – Faron

答えて

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> 

誰かがこれを実現する方法の良いアイデアを持っている場合は、私が希望それを聞くのが大好きです。

+0

多くのありがとう。私はこれを試し、あなたに知らせるでしょう。 – Faron

関連する問題