2009-07-27 89 views
2

我有一個遞歸表中的SQL Server數據庫:映射空字符串爲NULL NHibernate的

MyTable: 
ID : string PrimaryKey 
Parent: string references MyTable - NOTNULL !! 

,並用流利的NHibernate映射到

class MyTable 
{ 
    public virtual string ID {get; set;} 
    public virtual MyTable Parent {get; set;} 
} 

我的問題是,家長應該在空我的C#應用​​程序如果列Parent是數據庫中的「」(空字符串),反之亦然。不幸的是我不能改變列的類型來接受NULL!

我試圖使用IEmptyInterceptor,但我不明白它的工作原理。

由於提前, forki

回答

5

您需要有一個IUserType作爲主鍵列,它執行特殊的NULL值處理。

public MyTableMap() 
{ 
    Id(x => x.EntryNo) 
     // Since the PK is a string, it must be assigned by the application. 
     .GeneratedBy.Assigned() 
     .SetAttribute("type", typeof(SpecialNullValueStringType).AssemblyQualifiedName); 

    References(x => x.Parent); 
} 

public class SpecialNullValueStringType : IUserType 
{ 
    #region IUserType Members 
    public bool IsMutable 
    { 
     get { return false; } 
    } 

    public Type ReturnedType 
    { 
     get { return typeof(string); } 
    } 

    public SqlType[] SqlTypes 
    { 
     get { return new[] { NHibernateUtil.String.SqlType }; } 
    } 

    public object NullSafeGet(IDataReader rs, string[] names, object owner) 
    { 
     var obj = NHibernateUtil.String.NullSafeGet(rs, names[0]); 

     if (obj == null) 
     { 
      return null; 
     } 

     var value = (string) obj; 
     if (String.IsNullOrEmpty(value)) 
     { 
      return null; 
     } 

     return value; 
    } 

    public void NullSafeSet(IDbCommand cmd, object value, int index) 
    { 
     if (value == null) 
     { 
      ((IDataParameter) cmd.Parameters[index]).Value = String.Empty; 
     } 
     else 
     { 
      ((IDataParameter) cmd.Parameters[index]).Value = value; 
     } 
    } 

    public object DeepCopy(object value) 
    { 
     return value; 
    } 

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

    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 ? typeof(string).GetHashCode() + 473 : x.GetHashCode(); 
    } 
    #endregion 
} 
1

我會去一個IUserType這將空字符串轉換爲null S和反之亦然。需要注意的兩種方法是NullSafeGetNullSafeSet

不確定自定義類型如何與流利NHibernate集成。

+0

我試過了。但不幸的是,我沒有得到這個工作。 我是否必須爲MyTable類實現IUserType? – forki23 2009-07-27 12:47:43

+0

您的域類不應該實現IUserType接口,而應該在數據訪問層中創建一個單獨的用戶類型類,然後在映射中使用它。這樣您的域層現在仍然不需要關於數據接入層。要在Fluent NHibernate映射中指定用戶類型,請使用.CustomTypeIs ()方法。 – 2009-07-28 08:25:05

0

我試圖執行IUserType我的映射:

public class MyCustomString : IUserType 
{ 
    public Type ReturnedType 
    { 
     get { return typeof (MyTable); } 
    } 

    public SqlType[] SqlTypes 
    { 
     get { return new[] {NHibernateUtil.String.SqlType}; } 
    }  

    public object NullSafeGet(IDataReader rs, string[] names, object owner) 
    { 
     object obj = NHibernateUtil.String.NullSafeGet(rs, names[0]); 

     if (obj == null) return null; 

     var s = (string) obj; 

     if (s == "") 
      return null; 
     using (ISession session = SessionHelper.OpenSession()) 
     { 
      using (session.BeginTransaction()) 
      { 
       return MyTable.Get(session, s); 
      } 
     } 
    } 

    public void NullSafeSet(IDbCommand cmd, object value, int index) 
    { 
     ((IDataParameter) cmd.Parameters[index]).Value = value == null ? 0 : ((MyTable) value).EntryNo; 
    } 

    ... 
} 

,改變了映射到

public MyTableMap() 
    { 
     Id(x => x.EntryNo); 

     Map(x => x.Parent).CustomTypeIs<MyCustomString>(); 

     // References() doesn't allow CustomTypeIs() 
     // References(x => x.Parent).CustomTypeIs<MyCustomString>(); 
    } 

這似乎爲我的根的工作 - 但它總是會打開一個會話,以獲得正確的家長。 它不是懶惰 - 所以它總是檢索所有的父母直到根:-(

這不可能是正確的方式。我不想打開一個新的會話 - 但除此之外,我返回一個。串並獲得運行時類型錯誤

2

我找到了(亂)的方式來得到這個工作?

public class NullEventListener : IPreUpdateEventListener, IPreInsertEventListener, IPreLoadEventListener 
{ 
    #region IPreInsertEventListener Members 

    public bool OnPreInsert(PreInsertEvent preInsertEvent) 
    { 
     var instance = preInsertEvent.Entity as MyTable; 
     if (instance == null) 
      return false; 

     if (instance.Parent == null) 
      Set(preInsertEvent.Persister, preInsertEvent.State, "Parent", string.Empty);  

     return false; 
    } 

    #endregion 

    #region IPreLoadEventListener Members 

    public void OnPreLoad(PreLoadEvent preLoadEvent) 
    { 
     var instance = preLoadEvent.Entity as MyTable; 
     if (instance == null) 
      return; 

     try 
     { 
      // this is really messy!! 
      var parent = Get(preLoadEvent.Persister, preLoadEvent.State, "Parent") as MyTable; 
      if (parent == null || parent.ID == "") 
       throw new Exception("Set to null"); 
     } 
     catch (Exception) 
     { 
      Set(preLoadEvent.Persister, preLoadEvent.State, "Parent", null); 
     } 

     return; 
    } 

    #endregion 

    #region IPreUpdateEventListener Members 

    public bool OnPreUpdate(PreUpdateEvent preUpdateEvent) 
    { 
     var instance = preUpdateEvent.Entity as MyTable; 
     if (instance == null) 
      return false; 

     if (instance.Parent == null) 
      Set(preUpdateEvent.Persister, preUpdateEvent.State, "Parent", string.Empty);  

     return false; 
    } 

    #endregion 

    private static void Set(IEntityPersister persister, object[] state, string propertyName, object value) 
    { 
     int index = Array.IndexOf(persister.PropertyNames, propertyName); 
     if (index == -1) 
      return; 
     state[index] = value; 
    } 

    private static object Get(IEntityPersister persister, object[] state, string propertyName) 
    { 
     int index = Array.IndexOf(persister.PropertyNames, propertyName); 
     if (index == -1) 
      return null; 
     return state[index]; 
    } 
} 

感謝和問候, forki