2011-08-25 3 views
2

Nhibernate는 net 4 ISet 인터페이스 대신 Iesi Set를 사용하도록합니다.Nhibernate iesicollection contains에 false가 반환됩니다.

public virtual void Remove(Substance substance) 
    { 
     var test = _substances.First() == substance; 

     if (!_substances.Contains(substance)) return; 

     _substances.Remove(substance); 
     substance.SubstanceGroup = null; 
    } 

변수 _substances는 HashedSet을 참조 : 다음 코드 조각에서 나는 iesi 세트 항목이 포함되어 있는지 확인합니다. 임시 변수로 코드를 검사하기 위해 test var를 추가했습니다.

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

이 해시로 아이디 (GUID)를 반환 할 항목을 발생합니다 나는 이런 식으로 무시 Equals 메서드가 있습니다. 나는 다음과 같은 결과를 얻을 디버거에서 확인하는 경우 :

test 
true 
_substances.Contains(substance) 
false 
_substances.First().GetHashCode() 
-2974953 
substance.GetHashCode() 
-2974953 

가 어떻게 정확히 같은 객체가 그 수집의 방법을 포함하여 컬렉션에서 발견되지 않는다는 될 수 있습니다 ?? 심지어 디버거에서이 작업을 수행 할 수 있습니다.

_substances.Contains(_substances.First()) 
false 

분명히 _substances.Remove (substance)도 작동하지 않습니다. 추가 연구가 끝난 후 NH가 컬렉션을 자체 퍼시스턴트 일반 세트로 대체한다는 사실을 알게되었습니다. 이 세트가 사용될 때 문제가 발생합니다. 해당 집합에서 항목을 검색하고 같은 집합에 대해 Contains를 호출하면 항상 false를 반환합니다. GetHashCode 및 Equals를 재정의하고 Equals 메서드에서 true를 반환합니다.

+0

또한 Equals 메서드를 재정의 하시겠습니까? http://blog.visualt4.com/2009/03/nhibernate-why-override-gethashcode-and.html – Bas

+0

예, 결과가 없어도 반환 할 수 있습니다. 이것은 Nhibernate와 관련된 또 하나의 문제입니다. – halcwb

답변

3

Iesi ISet 컬렉션이 올바르게 작동한다는 점을 확신하기 때문에 Equals 및 GetHashCode 구현에 문제가 있습니다. PersistentGenericSet으로 대체 된 이유는 ISet이 인터페이스 일 뿐이며 컬렉션을 구체적인 유형으로 대체해야하기 때문입니다. 더 많은 코드가 없으면 문제가 어디에 있는지 알기가 어렵습니다. 아래에서 더 나은 평등 구현을 붙여 넣었습니다. 내가 볼 수있는 한 가지 문제점은 ID가 할당 된 후 해시 코드가 변경되고 해시 코드를 캐싱하여 버전이 처리된다는 것입니다.

public class Substance 
{ 
    private int? _cachedHashCode; 

    public Substance() 
    { 
     Id = Guid.Empty; 
    } 

    public Substance(Guid id) 
    { 
     Id = id; 
    } 

    public Guid Id { get; set; } 

    public bool IsTransient 
    { 
     get { return Id == Guid.Empty; } 
    } 

    public bool Equals(Substance other) 
    { 
     if (IsTransient^other.IsTransient) 
     { 
      return false; 
     } 
     if (IsTransient && other.IsTransient) 
     { 
      return ReferenceEquals(this, other); 
     } 
     return other.Id.Equals(Id); 
    } 

    public override bool Equals(object obj) 
    { 
     if (obj == null || obj.GetType() != GetType()) 
     { 
      return false; 
     } 
     var other = (Substance)obj; 
     return Equals(other); 
    } 

    public override int GetHashCode() 
    { 
     if (!_cachedHashCode.HasValue) 
     { 
      _cachedHashCode = IsTransient ? base.GetHashCode() : Id.GetHashCode(); 
     } 
     return _cachedHashCode.Value; 
    } 
} 

public class Mixture 
{ 
    public Mixture() 
    { 
     Substances = new HashedSet<Substance>(); 
    } 

    public ISet<Substance> Substances { get; set; } 
} 

public class Tests 
{ 
    [Test] 
    public void set_contains_transient_substance() 
    { 
     var mixture = new Mixture(); 
     var s1 = new Substance(); 
     mixture.Substances.Add(s1); 
     Assert.IsTrue(mixture.Substances.Contains(s1)); 
    } 

    [Test] 
    public void set_contains_persistent_substance() 
    { 
     var id = Guid.NewGuid(); 
     var mixture = new Mixture(); 

     var s1 = new Substance(id); 
     mixture.Substances.Add(s1); 

     var s2 = new Substance(id); 
     // these were created with the same id so hash code is not cached 
     // and id equality is used 
     Assert.IsTrue(mixture.Substances.Contains(s2)); 
    } 

    [Test] 
    public void remove_substance() 
    { 
     var id = Guid.NewGuid(); 
     var mixture = new Mixture(); 

     var s1 = new Substance(id); 
     mixture.Substances.Add(s1); 

     var s2 = new Substance(id); 
     mixture.Substances.Remove(s2); 
     Assert.IsTrue(mixture.Substances.Count() == 0); 
    } 

    [Test] 
    public void hash_code_is_cached() 
    { 
     var s1 = new Substance(Guid.NewGuid()); 
     var s2 = new Substance(Guid.NewGuid()); 

     var mixture = new Mixture(); 
     mixture.Substances.Add(s1); 

     Assert.IsFalse(mixture.Substances.Contains(s2)); 
     // assign s1 id to s2, s2 hashcode is cached so they are not equal 
     s2.Id = s1.Id; 
     Assert.IsFalse(mixture.Substances.Contains(s2)); 
    } 

} 
+0

그 트릭을 했어, 정말 고마워. 내 기본 클래스에서 나는 hashcode의 캐싱을 구현했다. – halcwb

+1

하나의 중요한주의 사항이지만, 솔루션은 식별자가 NH에 의해 설정되는 것이 아니라 애플리케이션에 의해 설정된다는 것을 의미합니다. 불행히도 NH의 이런 중요한 기능을 놓칠 수 있습니다. – halcwb

관련 문제