首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为POCO实现IEquatable

为POCO实现IEquatable
EN

Stack Overflow用户
提问于 2012-03-20 06:27:08
回答 3查看 2.4K关注 0票数 6

我注意到EF的DbSet.Add()非常慢。谷歌搜索得到了一个可以提高180倍性能的答案:

https://stackoverflow.com/a/7052504/141172

但是,我并不完全理解如何按照答案中的建议实现IEquatable<T>

根据MSDN,如果我实现了IEquatable<T>,我还应该重写Equals()GetHashCode()

和很多POCO一样,我的目标是可变的。在提交到数据库(SaveChanges())之前,新对象的Id为0。在保存对象之后,Id是实现IEquatable、Equals()和GetHashCode()的理想基础。

在哈希代码中包含任何可变属性是不明智的,因为根据MSDN

如果两个对象比较相等,则每个对象的GetHashCode方法必须返回相同的值。

我是否应该将IEquatable<T>实现为按属性进行属性比较(例如,this.FirstName == other.FirstName),而不是覆盖等于()和GetHashCode()?

考虑到我的POCO是在EntityFramework上下文中使用的,是否应该特别注意Id字段?

EN

回答 3

Stack Overflow用户

发布于 2012-11-17 05:51:09

我偶然发现你的问题是为了寻找解决同样问题的方法。下面是我正在尝试的解决方案,看看它是否符合您的需要:

首先,我的所有POCOs都来自于这个抽象类:

代码语言:javascript
复制
public abstract class BasePOCO <T> : IEquatable<T> where T : class
{
    private readonly Guid _guid = Guid.NewGuid();

    #region IEquatable<T> Members

    public abstract bool Equals(T other);

    #endregion

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj))
        {
            return false;
        }
        if (ReferenceEquals(this, obj))
        {
            return true;
        }
        if (obj.GetType() != typeof (T))
        {
            return false;
        }
        return Equals((T)obj);
    }

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

我创建了一个在GetHashCode()覆盖中使用的只读Guid字段。这将确保如果我将派生的POCO放入字典或其他使用哈希的东西中,如果我在过渡期间调用了.SaveChanges(),并且ID字段由基类更新--这是我不确定是否完全正确的部分,或者它是否比Base.GetHashCode()更好,那么我就不会孤立它。我抽象了相等(T其他)方法,以确保实现类必须以某种有意义的方式实现它,很可能是使用ID字段。我在这个基类中添加了相等(Object obj)覆盖,因为对于所有派生类,它可能也是相同的。

这将是抽象类的实现:

代码语言:javascript
复制
public class Species : BasePOCO<Species>
{
    public int ID { get; set; }
    public string LegacyCode { get; set; }
    public string Name { get; set; }

    public override bool Equals(Species other)
    {
        if (ReferenceEquals(null, other))
        {
            return false;
        }
        if (ReferenceEquals(this, other))
        {
            return true;
        }
        return ID != 0 && 
               ID == other.ID && 
               LegacyCode == other.LegacyCode &&
               Name == other.Name;
    }
}

ID属性被设置为数据库中的主键,EF知道这一点。在新创建的对象上ID为0,然后在.SaveChanges()上设置为唯一的正整数。因此,在重写的等于(物种)方法中,空对象显然是不相等的,同样的引用显然是相等的,那么我们只需要检查ID == 0。如果是的话,我们会说两个ID都是0的相同类型的对象不相等。否则,如果它们的属性都是相同的,我们会说它们是相等的。

我认为这涵盖了所有相关的情况,但如果我不正确,请加入。希望这能有所帮助。

===编辑1

我认为我的GetHashCode()是不对的,我查看了关于这个主题的https://stackoverflow.com/a/371348/213169答案。上面的实现违反了返回等于() ==真的对象必须具有相同的哈希代码的约束。

这是我第二次尝试:

代码语言:javascript
复制
public abstract class BasePOCO <T> : IEquatable<T> where T : class
{
    #region IEquatable<T> Members

    public abstract bool Equals(T other);

    #endregion

    public abstract override bool Equals(object obj);
    public abstract override int GetHashCode();
}

以及执行情况:

代码语言:javascript
复制
public class Species : BasePOCO<Species>
{
    public int ID { get; set; }
    public string LegacyCode { get; set; }
    public string Name { get; set; }

    public override bool Equals(Species other)
    {
        if (ReferenceEquals(null, other))
        {
            return false;
        }
        if (ReferenceEquals(this, other))
        {
            return true;
        }
        return ID != 0 && 
        ID == other.ID && 
        LegacyCode == other.LegacyCode && 
        Name == other.Name;
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj))
        {
            return false;
        }
        if (ReferenceEquals(this, obj))
        {
            return true;
        }
        return Equals(obj as Species);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            return ((LegacyCode != null ? LegacyCode.GetHashCode() : 0) * 397) ^ 
                   (Name != null ? Name.GetHashCode() : 0);
        }
    }

    public static bool operator ==(Species left, Species right)
    {
        return Equals(left, right);
    }

    public static bool operator !=(Species left, Species right)
    {
        return !Equals(left, right);
    }
}

因此,我去掉了基类中的Guid,并将GetHashCode移到实现中。除了ID之外,我使用了Resharper的GetHashCode实现,因为ID可能会更改(不要孤儿)。这将满足上述联系答案中对平等的限制。

票数 3
EN

Stack Overflow用户

发布于 2012-03-20 06:57:30

和许多POCO一样,我的对象是可变的

但是在作为主键的字段上,tehy不应该是可变的。每个定义,否则您是在一个痛苦数据库明智的世界无论如何以后。

只在主键的字段上生成HashCode。

Equals()必须返回真正的IFF参与对象具有相同的哈希代码

错误。

哈希码是双倍的。有可能有两个对象具有不同的值和smae哈希码。hsahsode是一个int (32位)。一个字符串可以有2gb长。您不能将每个可能的字符串都应用到单独的哈希代码中。

如果两个对象具有相同的哈希代码,它们可能是不同的。如果两个对象相同,则它们不能有不同的哈希码。

对于具有相同哈希码的对象,您从哪里得到了等于必须返回true的想法?

此外,无论是否PCO,映射到数据库并在关系中使用的对象必须具有稳定的主键(可用于运行哈希代码计算)。不具有此STIL l的对象应该具有主键(根据Server的要求),在这里使用序列/人工主键工作。同样,使用它来运行HashCode计算。

票数 1
EN

Stack Overflow用户

发布于 2012-04-27 14:14:59

第一件事:对不起我蹩脚的英语:)

正如TomTom所说,它们不应该仅仅因为它们还没有收到PK/Id而变.

在我们的EF:CF系统中,对于每个新的POCO,我们使用生成的负id (在基类ctor中分配,或者,如果您使用ProxyTracking,在ObjectMaterialized事件中)。它非常简单的想法:

代码语言:javascript
复制
public static class IdKeeper
{
  private static int m_Current = int.MinValue;
  private static Next()
  {
    return ++m_Current;
  }
}

MinValue和增量应该很重要,因为EF在对db进行更改之前将按PK对POCOs进行排序,并且当您使用"-1,-2,-3“时,POCOs会被翻转保存,在某些情况下(不是根据哪种类型)可能并不理想。

代码语言:javascript
复制
public abstract class IdBase
{
  public virtual int Id { get; set; }
  protected IdBase()
  {
    Id = IdKeeper.Next();
  }
}

如果POCO是从DB中实现的,那么他的Id将与实际的PK以及调用SaveChanges()时被覆盖。作为奖励,每一个“尚未保存”的POCO id都将是唯一的(总有一天会派上用场;)

然后,将两个POCO与IEquatable (为什么dbset工作这么慢?)进行比较很容易:

代码语言:javascript
复制
public class Person
  : IdBase, IEquatable<Person>
{
  public virtual string FirstName { get; set; }

  public bool Equals(Person other)
  {
    return Id == other.Id;
  }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9782235

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档