2011-10-07 35 views
3

我看到DDD域實現,其中實體在構建Equals/GetHashCode方法時依賴主鍵ID。 我明白爲什麼這是一個好主意,因爲主鍵可能是唯一不可變的成員。但是,也有可能出現這樣的情況,即它不是個好主意。DDD:GetHashCode和主ID

想想一個字典,它包含剛剛實例化的實體。主鍵(假設它是自動遞增值,而不是「業務相關」值)尚未分配,它可能在每個實體上都爲0。 現在,詞典中的實體將被保存。這意味着主鍵的改變導致不同的哈希碼。

我的問題:如果沒有與業務相關的主鍵可用且所有成員都可變,應該使用關於GetHashCode的模式?

預先感謝您。

+0

,爲什麼你想'在首位實體Equals'方法 – driushkin

+1

謝謝你的評論。有很多情況我需要比較它們,特別是在客戶端/服務器數據同步的情況下。我將舊的非DDD代碼移植到域驅動的體系結構中,因此我可以估計我需要身份比較。 – mbue

+0

實體的整體思想是它有自己的身份,即它與所有其他實體不同。不要讓克隆人統治世界! – driushkin

回答

5

在這種情況下,我只能依靠標識在EqualsGetHashcode方法,如果ID已被分配。 在其他情況下,我比較使用參考的平等。 GetHashCode實現也是如此;我只創建一個基於'Id'的哈希碼,如果Id已被分配。 (也就是說,如果實體不是瞬態的)。

下面的代碼是我使用的。這有點基於夏普架構中的實體:

 public override bool Equals(object obj) 
     { 
      Entity<TId> other = obj as Entity<TId>; 

      if(other == null || this.GetType() != other.GetType()) 
      { 
       return false; 
      } 

      bool otherIsTransient = Equals (other.Id, default(TId)); 
      bool thisIsTransient = Equals (this.Id, default (TId)); 

      if(otherIsTransient && thisIsTransient) 
      { 
       return ReferenceEquals (this, other); 
      } 

      return Id.Equals (other.Id); 

     } 

首先,我檢查兩個實體是否屬於同一類型。 (實際上,你也可以通過實現正確的接口來創建這種方法的打字版本(IEquality<T>) 當兩個實體屬於同一類型時,我檢查它們中的一個是否是瞬態的,我只是通過檢查它們的ID屬性:如果它包含默認值,表示它們還沒有被保存在數據庫中 如果它們都是瞬態的,我將它們的引用進行比較,否則我可以在它們的ID上比較它們。

GetHashCode方法,我使用,可確保多數民衆贊成返回的值,永遠不會改變:

 private int? _oldHashCode; 

     public override int GetHashCode() 
     { 
      // Once we have a hash code we'll never change it 
      if(_oldHashCode.HasValue) 
      { 
       return _oldHashCode.Value; 
      } 

      bool thisIsTransient = Equals (Id, default(TId)); 


      // When this instance is transient, we use the base GetHashCode() 
      // and remember it, so an instance can NEVER change its hash code. 

      if(thisIsTransient) 
      { 
       _oldHashCode = base.GetHashCode(); 

       return _oldHashCode.Value; 
      } 

      return Id.GetHashCode(); 
     } 
1

您可以檢查,假設它是一個自動遞增的身份,並且只在Id上進行比較,如果它們不是0或default(int)。喜歡的東西:

public virtual bool Equals(MyClass other) 
     { 
      if (ReferenceEquals(null, other)) return false; 
      if (ReferenceEquals(this, other)) return true; 
      if (Id != default(int) && other.Id != default(int)) 
       return Equals(Id, other.Id); 
      return Equals(other.ChildProp, ChildProp); 
     } 
1

好吧,如果你不能告訴一個對象等於另一個你不應該這樣做:)

這是確定的,如果等於返回false所有的時間(給定的另一個對象比自己)。如果不同的本地對象(沒有ID)在插入到db/storage時得到不同的ID,這是完全理智的。

如果你能分辨出兩個對象是相同的,並且它們都將被映射到同一個表格行中,因此在存儲器上得到相同的ID等於應該直觀地返回true。但是,使用可變值確定相等性(特別是哈希碼)可能會很危險。當你這樣做時,收藏通常會感到困惑。 在這種情況下,最好使用Equals和GetHashCode之外的其他方法來實現這種相等性,因爲它隨着時間的推移而變化。

所以基本上你需要確定你真正需要哪種平等的目的。

1

我期望您看到的系統使用NHibernate for ORM。 NHibernate的準則(據說可以讓你實體成爲POCO,並且對它們沒有要求)推薦這種做法。問題是NH有時難以識別實體與另一實例相同,特別是在會話已關閉並重新打開的情況下,或者如果兩個代理表示相同的對象。如果你不這樣做,你可以在一個集合中兩次使用同一個實體。請點擊此鏈接瞭解更多信息:

http://community.jboss.org/wiki/EqualsAndHashCode