2011-04-16 86 views
10

在NHibernate 3.0 Cookbook中,有一個基本實體類型的示例實現。等號被實現這樣的:等價實現NHibernate實體,unproxy問題

public abstract class Entity<TId> 
{ 
    public virtual TId Id { get; protected set; } 

    public override bool Equals(object obj) 
    { 
    return Equals(obj as Entity<TId>); 
    } 

    private static bool IsTransient(Entity<TId> obj) 
    { 
    return obj != null && Equals(obj.Id, default(TId)); 
    } 

    private Type GetUnproxiedType() 
    { 
    return GetType(); 
    } 

    public virtual bool Equals(Entity<TId> other) 
    { 
    if (other == null) return false;    
    if (ReferenceEquals(this, other)) return true; 

    if (!IsTransient(this) && !IsTransient(this) && Equals(Id, other.Id)) 
    { 
     var otherType = other.GetUnproxiedType(); 
     var thisType = GetUnproxiedType(); 
     return thisType.IsAssignableFrom(otherType) || 
     otherType.IsAssignableFrom(thisType); 
    } 
    return false; 
    }  
} 

的原因GetUnproxiedType()方法是這樣的:有一個抽象基類產品,混凝土類Book從產品和由NHibernate的使用動態代理類ProductProxy繼承用於延遲加載。如果代表書籍和具體書籍的ProductProxy具有相同的ID,則應該將它們視爲相等。但是我不明白爲什麼在ProductProxy實例上調用GetType()應該返回Product,以及它如何提供幫助。有任何想法嗎?

+0

你可以附加'GetType()'方法的實現嗎?沒有看到它,我猜測它返回「具體」的基礎類(例如 - 對於ProductProxy我想它應該返回'Product' – 2011-04-17 08:42:23

+0

productProxy是由NHibernate創建的動態代理類,所以我不知道實現..我想也許有人洞察到如何NHibernate創建代理將能夠幫助..也GetType()不是虛擬的,所以似乎是實現它作爲「新」在代理類不會有任何影響所有.. – 2011-04-17 09:23:56

+0

我有同樣的書,我不是所提供的Equals實現的大粉絲,它太依賴於對象的持久性了,本書指出如果任何一個實體都是瞬態的(不保存到db),那麼這個Equals總是返回false,首先我相信這打破了持久性無知規則,其次,如果我想在保存到數據庫之前檢查相等性,我實現了部分效率(ch eids),但我仍然實施一個「邏輯」等於所有我的對象。 – brainimus 2011-04-20 19:31:22

回答

6

我實際上繼續寫這本書的作者關於此代碼。事實證明,這是由於代理包裝的工作原理。下面是他的回答:

「如果你不明白的代理框架是如何工作的,這個想法似乎不可思議

當NHibernate的返回延遲加載的目的的代理,它返回繼承了代理實例。我們可以在不強制加載數據庫的情況下訪問一些成員,其中包括代理的Id屬性或字段GetType(),以及某些情況下Equals()GetHashCode()。訪問任何其他成員將強制從數據庫

當發生這種情況時,代理創建一個內部實例,例如,一個懶加載CustomerCustomerProxy102987098721340978)的實例在加載時將內部創建一個包含數據庫中所有數據的新實例Customer。然後,代理做這樣的事情:

public overrides string Name 
{ 
    get { 
     return _loadedInstance.Name; 
    } 
    set { _loadedInstance.Name = value; } 
} 

順便說一句,它是實現這個首要要求一切是在實體上,允許延遲加載虛擬。

因此,所有對代理上的Name屬性的調用都會中繼到具有實際數據的內部Customer實例。

GetUnproxiedType()利用了這一點。在代理上簡單呼叫GetType()將返回typeof(CustomerProxy02139487509812340)GetUnproxiedType()的呼叫將被中繼到內部客戶實例,並且內部客戶實例將返回typeof(Customer)。「

+0

這是否意味着僅僅比較兩個代理實體會導致它們從數據庫中加載,即使在比較中沒有使用數據庫字段,除了Id--我們已經知道了? – 2011-07-14 17:05:50

+1

@Victor,是的,它確實如此。您可以通過使用Session.Load()加載對象來測試它。我進行了一些測試,並且在您寫入Equals()的任何重載時儘快執行此操作,即使它只是返回true/false。 – 2011-07-14 22:16:27

2

我們使用NH 2並且此示例對我們不起作用(它未能取消對代理類型的處理並留下代理類型,見下文) 它說,2個實體使用相同的ID是不相等的,當其中一個是COrganization的代理()和其他不(DOrganization) 當我們有一個層次:。

class Organization 
class AOrganization : Organization 
class COrganization : Organization 
{ 
    public virtual COrganization GetConcrete() 
    { 
    return null; 
    } 
} 

class DOrganization : COrganization 
{ 
    public virtual COrganization GetConcrete() 
    { 
    return this; 
    } 
} 

AOrganization aOrganization; 
COrganization cOrganization; 
contract = new CContract(aOrganization, cOrganization as COrganization); //(COrganization)(cOrganization.GetConcrete()), 

所以CContract有一個組織類型的領域。使用一個設置器

public class Contract: Entity <short> 
{ 
    public virtual COrganization COrganization 
    { 
     get { return cOrganization; } 
     protected internal set 
     { 
      if (cOrganization != null && value != cOrganization) // != calls ==, which calls Equals, which calls GetUnproxiedType() 
        throw new Exception("Changing organization is not allowed."); 
      } 
      cOrganization = value; 
     } 
    } 
    private COrganization cOrganization; 
} 

我們構造了新的Contract,它的構造函數設置了指向某個組織的COrganization字段。然後我們調用UnitOfWork.Commit,NH嘗試再次設置COrganization字段(使用相同的ID),GetUnproxiedType工作不正確,新舊值被認爲是不相等的,並拋出異常...

這裏是錯誤露面的地方:

  var otherType = other.GetUnproxiedType(); 
      var thisType = GetUnproxiedType(); 

      return thisType.IsAssignableFrom(otherType) || 
      otherType.IsAssignableFrom(thisType); 

在調試器:OTHERTYPE == COrganizationProxy - GetUnproxiedType失敗... thisType == DOrganization

COrganizationProxy和DOrganization既繼承COrganization 。 所以他們不是IsAssignableFrom彼此...

爲什麼這個例子適合你?

也許是因爲我們有NH 2.0或2.1?

因爲簡單「cOrganization爲COrganization」,而不是「(COrganization)(cOrganization.GetConcrete())」還是?

或者因爲我們實現了==,!=並且不僅在實體中,而且在組織中也是這樣?

public abstract class Organization : Entity<int> 
{ 
    public override bool Equals(object obj) 
    { 
     return base.Equals(obj); 
    } 

    public override int GetHashCode() 
    { 
     return base.GetHashCode(); 
    } 

    public static bool operator ==(Organization object1, Organization object2) 
    { 
     return AreEqual(object1, object2); 
    } 

    public static bool operator !=(Organization object1, Organization object2) 
    { 
     return AreNotEqual(object1, object2); 
    } 
} 

public abstract class Entity<TId> 
{ 
    public virtual TId Id { get; /*protected*/ set; } 

    public override bool Equals(object obj) 
    { 
     return Equals(obj as Entity<TId>); 
    } 

    private static bool IsTransient(Entity<TId> obj) 
    { 
     return obj != null && 
     Equals(obj.Id, default(TId)); 
    } 

    private Type GetUnproxiedType() 
    { 
     return GetType(); 
    } 

    public virtual bool Equals(Entity<TId> other) 
    { 
     if (other == null) 
      return false; 
     if (ReferenceEquals(this, other)) 
      return true; 
     if (!IsTransient(this) && 
     !IsTransient(other) && 
     Equals(Id, other.Id)) 
     { 
      var otherType = other.GetUnproxiedType(); 
      var thisType = GetUnproxiedType(); 
      return thisType.IsAssignableFrom(otherType) || 
      otherType.IsAssignableFrom(thisType); 
     } 
     return false; 
    } 

    public override int GetHashCode() 
    { 
     if (Equals(Id, default(TId))) 
      return base.GetHashCode(); 
     return Id.GetHashCode(); 
    } 

    /// This method added by me 
    /// For == overloading 
    protected static bool AreEqual<TEntity>(TEntity entity1, TEntity entity2) 
    { 
     if ((object)entity1 == null) 
     { 
      return ((object)entity2 == null); 
     } 
     else 
     { 
      return entity1.Equals(entity2); 
     } 
    } 

    /// This method added by me 
    /// For != overloading 
    protected static bool AreNotEqual<TEntity>(TEntity entity1, TEntity entity2) 
    { 
     return !AreEqual(entity1, entity2); 
    } 
} 
+1

奇怪的是,當我將「private Type GetUnproxiedType()」從書中改爲「public virtual Type GetUnproxiedType()」時,所有人都開始工作(我們有NH 2.0或2.1)。爲什麼我們在書中有「私人」的方法? – NHusser 2011-06-10 14:18:00

+0

是否可以代理公共方法?我還沒有測試過這個代碼,現在還沒有,所以無法確認它是否適用於NH 3.0(這是本書的目標所在)。 – 2011-06-10 21:12:18

+0

這取決於我們正在使用的代理工廠。 http://groups.google.com/group/nhusers/browse_thread/thread/54777dd6249e7639 – NHusser 2011-08-27 09:47:40