【问题标题】:Nhibernate iesicollection contains returns falseNhibernate iesicollection 包含返回 false
【发布时间】:2011-08-25 13:06:55
【问题描述】:

Nhibernate 强制您使用 Iesi Set,而不是 net 4 ISet 接口。在下面的代码 sn-p 中,我检查 iesi 集是否包含项目:

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

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

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

变量_substances 引用了一个HashedSet。我添加了测试变量只是为了检查代码作为临时措施。 我像这样重写了 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

显然,_substances.Remove(substance) 也不起作用。经过一些额外的研究,我发现 NH 用它自己的 Persistent Generic 集替换了该集合。使用此套件时会出现问题。如果我从该集合中检索一个项目并在同一集合上调用包含,它总是返回 false。我已经覆盖了GetHashCode和Equals,甚至在Equals方法中加入了return true。

【问题讨论】:

  • 您是否也覆盖了 Equals 方法?喜欢blog.visualt4.com/2009/03/…
  • 是的,我什至在里面放了return true,没有任何结果。这是我在使用 Nhibernate 时遇到的另一个令人头疼的问题。

标签: nhibernate contains nhibernate-collections


【解决方案1】:

您的 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));
    }

}

【讨论】:

  • 成功了,非常感谢。在我的基类中,我实现了哈希码的缓存。
  • 一个重要的警告是,您的解决方案暗示标识符是由应用程序设置的,而不是由 NH 设置的。不幸的是,您错过了 NH 这样做的一些重要功能。
猜你喜欢
  • 2017-04-16
  • 2018-12-02
  • 2011-06-09
  • 2020-10-12
  • 2021-09-23
  • 1970-01-01
  • 2013-06-22
  • 2015-09-11
相关资源
最近更新 更多