2009-07-27 144 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