Nhibernate iesicollection包含返回false

Nhibernate iesicollection包含返回false,nhibernate,contains,nhibernate-collections,Nhibernate,Contains,Nhibernate Collections,Nhibernate强制您使用Iesi集,而不是NET4ISET接口。在以下代码片段中,我检查iesi集合是否包含项: public virtual void Remove(Substance substance) { var test = _substances.First() == substance; if (!_substances.Contains(substance)) return; _substances.Rem

Nhibernate强制您使用Iesi集,而不是NET4ISET接口。在以下代码片段中,我检查iesi集合是否包含项:

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

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

        _substances.Remove(substance);
        substance.SubstanceGroup = null;
    }
变量_引用哈希集。我添加了测试变量只是为了检查代码作为一个临时度量。 我将像这样重写Equals方法:

    public override int GetHashCode()
    {
        return Equals(Id, default(TId)) ? base.GetHashCode() : Id.GetHashCode();
    }
这将导致该项以哈希形式返回Id(Guid)。 如果我签入调试器,会得到以下结果:

test
true
_substances.Contains(substance)
false
_substances.First().GetHashCode()
-2974953
substance.GetHashCode()
-2974953
使用集合的contains方法在集合中找不到完全相同的对象,这怎么可能呢??我甚至可以在调试器中执行此操作:

_substances.Contains(_substances.First())
false

显然,去除物质也不起作用。经过一些额外的研究,我发现NH用自己的持久泛型集替换了集合。使用此集合时会出现问题。如果我从该集合中检索一个项,并在同一集合中调用Contains,它总是返回false。我已经重写了GetHashCode和Equals,甚至在Equals方法中设置了return true。

Equals和GetHashCode实现有问题,因为我向您保证Iesi ISet集合工作正常。它被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));
    }

}
公共类物质
{
私有int?_cachedHashCode;
公共物质()
{
Id=Guid.Empty;
}
公共物质(Guid id)
{
Id=Id;
}
公共Guid Id{get;set;}
公众舆论是暂时的
{
获取{return Id==Guid.Empty;}
}
公共布尔等于(其他物质)
{
if(IsTransient^其他.IsTransient)
{
返回false;
}
如果(IsTransient&&other.IsTransient)
{
返回ReferenceEquals(这个,其他);
}
返回other.Id.Equals(Id);
}
公共覆盖布尔等于(对象对象对象)
{
if(obj==null | | obj.GetType()!=GetType())
{
返回false;
}
var其他=(物质)obj;
回报等于(其他);
}
公共覆盖int GetHashCode()
{
if(!\u cachedHashCode.HasValue)
{
_cachedHashCode=IsTransient?base.GetHashCode():Id.GetHashCode();
}
返回_cachedHashCode.Value;
}
}
公共阶层混合
{
公共混合物()
{
物质=新的HashedSet();
}
公共ISet物质{get;set;}
}
公开课考试
{
[测试]
公共无效集\u包含\u瞬态\u实体()
{
var混合物=新混合物();
var s1=新物质();
混合物。物质。添加(s1);
断言.IsTrue(混合物.物质.包含(s1));
}
[测试]
公共无效集\u包含\u持久性\u物质()
{
var id=Guid.NewGuid();
var混合物=新混合物();
var s1=新物质(id);
混合物。物质。添加(s1);
var s2=新物质(id);
//它们是使用相同的id创建的,因此不会缓存哈希代码
//并且使用id相等
断言.IsTrue(混合物.物质.包含(s2));
}
[测试]
公共物品
{
var id=Guid.NewGuid();
var混合物=新混合物();
var s1=新物质(id);
混合物。物质。添加(s1);
var s2=新物质(id);
混合物。物质。去除(s2);
Assert.IsTrue(mixed.Substances.Count()==0);
}
[测试]
public void散列\u代码\u被\u缓存()
{
var s1=新物质(Guid.NewGuid());
var s2=新物质(Guid.NewGuid());
var混合物=新混合物();
混合物。物质。添加(s1);
Assert.IsFalse(mixed.Substances.Contains(s2));
//将s1 id分配给s2,s2哈希代码被缓存,因此它们不相等
s2.Id=s1.Id;
Assert.IsFalse(mixed.Substances.Contains(s2));
}
}

您是否也覆盖了Equals方法?像是的,我甚至把return true放在里面,没有任何结果。这是我和Nhibernate之间的另一个头发拉扯问题。这解决了问题,非常感谢。在我的基类中,我实现了hashcode的缓存。不过,一个重要的警告是,您的解决方案意味着标识符是由应用程序设置的,而不是由NH设置的。不幸的是,您错过了NH这样做的一些重要特性。