为 POCO 实施 IEquatable

2024-04-02

我注意到 EF 的 DbSet.Add() 非常慢。谷歌搜索了一下,找到了一个 SO 答案,承诺性能提升高达 180 倍:

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

但是,我不明白具体如何实施IEquatable<T>正如答案中所建议的。

根据 MSDN http://msdn.microsoft.com/en-us/library/ms131187%28v=vs.100%29.aspx,如果我实施IEquatable<T>,我也应该覆盖Equals() and GetHashCode().

与许多 POCO 一样,我的对象是mutable。在提交到数据库之前(SaveChanges()),新对象的 Id 为 0。After对象已保存,Id 作为实现 IEquatable、Equals() 和 GetHashCode() 的理想基础。

在哈希码中包含任何可变属性是不明智的,因为根据MSDN http://msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx

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

我应该实施IEquatable<T>作为逐个属性的比较(例如this.FirstName == other.FirstName)而不覆盖 Equals() 和 GetHashCode()?

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


我在寻找同一问题的解决方案时遇到了您的问题。这是我正在尝试的解决方案,看看它是否满足您的需求:

首先,我所有的 POCO 都源自这个抽象类:

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();
    }
}

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

这将是抽象类的实现:

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() 上设置为唯一的正整数。所以在重写的 Equals(Species other) 方法中,null 对象显然不相等,相同的引用显然相等,那么我们只需要检查 ID == 0 即可。如果是,我们会说两个相同类型的对象两者的 ID 均为 0 并不相等。否则,如果它们的属性都相同,我们就会说它们相等。

我认为这涵盖了所有相关情况,但如果我不正确,请指出。希望这可以帮助。

===编辑1

我认为我的 GetHashCode() 不正确,然后我看了这个https://stackoverflow.com/a/371348/213169 https://stackoverflow.com/a/371348/213169回答有关该主题的问题。上面的实现违反了返回 Equals() == true 的对象必须具有相同哈希码的约束。

这是我的第二次尝试:

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();
}

以及实施:

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移到了实现中。我将 Resharper 的 GetHashCode 实现与除 ID 之外的所有属性一起使用,因为 ID 可能会更改(不希望出现孤儿)。这将满足上面链接答案中对平等的约束。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为 POCO 实施 IEquatable 的相关文章

  • 无法定义两个对象之间的关系,因为它们附加到不同的 ObjectContext 对象

    我已经阅读了一些与此特定错误消息有关的问题 答案 但我不太了解适当的解决方案 我多次读到您应该创建 EF4 上下文 使用它 然后处理它 在我的应用程序中 我使用不同的上下文对象到处加载实体 然后最终希望将这些实体关联在一起 我创建了一个简单
  • 存储过程和实体框架的性能

    是否有任何明显的原因可以解释为什么通过实体模型调用存储过程会导致性能比直接调用慢得多 首先 我不希望 SP 运行在exactly相同的速度 我知道 EF 必须做的许多事情在直接访问 SP 时不会被调用 除此之外 我有一个返回三列字符串的查询
  • 有什么办法可以加快实体框架中的 CreateIfNotExists 速度?

    我在 SQL Server 2008 上使用 EF 4 3 Code First 我运行了几个测试套件 这些测试套件使用 CreateIfNotExists 删除并重新创建数据库 这工作正常 但速度很慢 第一次调用时创建数据库最多可能需要
  • 将 MySql 与 Entity Framework 4 和代码优先开发 CTP 结合使用

    我想我应该尝试一下 Scott Guthrie 的最新的帖子 http weblogs asp net scottgu archive 2010 07 16 code first development with entity framew
  • 实体框架 - 将字段值更新为当前数据库服务器时间

    我已经从数据库中提取了一个实体对象 并且需要将日期更新为数据库服务器的日期 时间 通常您可以通过使用 SQL 设置它来完成此操作getDate 功能 在以下场景中我该如何实现这一点 var client context client Whe
  • 检测变化的多对多关系

    我试图理解为什么 DbContext 没有检测到多对多关系的变化 这是我在模型配置中设置的 this Configuration ValidateOnSaveEnabled false this Configuration ProxyCre
  • 指定输出程序集中 .csdl / .ssdl / .msl 元数据文件的位置

    我有一个 EF 项目 其中包含我已成功使用的数据模型 元数据工件处理 选项设置为 嵌入输出程序集中 由于 edmx 文件位于项目的根文件夹中 EntityConnectionStringBuilder 中使用的元数据字符串设置为 res m
  • 在 Entity Framework 4.0 中批处理 DB 命令

    我当前的项目需要每天与外部系统同步 同步基于复杂的导入文件结构 该结构通过广泛的业务逻辑进行解析和处理 由于业务逻辑的原因 我们决定在 NET 代码中进行此操作并重用现有的 BL 组件 而不是在存储过程或集成服务中编写相同的逻辑 BL 层位
  • EF4 中的并发 - 如何有条件地创建实体

    我需要能够创建一个新的用户实体only if提供的电子邮件是唯一的 我以前总是通过执行一个简单的操作来处理这个问题if UserSet Any 在我之前AddToUserSet 然而 这不是一个并发解决方案 并且会在重负载下崩溃 我一直在研
  • 使用 EF4 将 Int32 转换为 Oracle number(5)

    我正在将 EF 4 数据库优先 完全由它生成的模型 与 Oracle 10g 数据库一起使用 并且我在一个字段上遇到问题 我的字段定义为NUMBER 5 在我的数据库中 在我的模型中 EF 将其定义为short 我的问题是我有一些值大于 3
  • 实体框架代码优先:如何确定运行时用于导航属性的外键属性?

    我有一个实体框架 Code First DbContext 配置了以下实体 在此示例中 Bar 类是 Foo 类的子类 public class Foo public Guid Id get set public virtual IColl
  • 实体框架插入具有相关对象的对象

    我是实体框架的新手 我需要插入一个对象Comment具有相关的 FK 对象User进入数据库 public Class Comment public int CommentID get set public string CommentCo
  • 实体框架 4 Single() vs First() vs FirstOrDefault()

    我花了很长时间寻找查询单个项目的不同方法的比较 以及何时使用每种方法 有谁有一个比较所有这些的链接 或者一个关于为什么你会使用其中一个而不是另一个的快速解释 还有更多我不知道的运营商吗 谢谢 以下是不同方法的概述 Find 当您想通过主键获
  • 如何在没有 Nuget 的情况下获取 Entity Framework 4.2?

    我们需要使用 Entity Framework 4 2 来测试 4 2 中的 SQL 生成 不幸的是 我们的开发环境不允许我们使用Nuget 有什么方法可以获取 EF4 2 二进制文件以便我们对其进行测试吗 您可以使用 NuGet Pack
  • DisplayFormat 数据注释不起作用

    我的模型类中有以下数据注释 Required ErrorMessage Required DisplayFormat ApplyFormatInEditMode true DataFormatString 0 MM dd yyyy publ
  • 无法返回json数据,WCF Restful Service .NET 4.0

    我最近使用 Entity Framework 4 0 设置了 WCF Restful 服务 它与 XML 完美配合 但是当我尝试以 json 格式返回它时 我得到了 HTTP 1 1 504 Fiddler Receive Failure
  • 实体框架 4 的 System.Reflection.ReflectionTypeLoadException

    我在 Windows 窗体应用程序中使用 EF4 每当我在未安装 Visual Studio 2010 的计算机中运行发布文件时 我都会遇到问题 我总是收到此错误 System Reflection ReflectionTypeLoadEx
  • 使用 EF CPT5 进行域建模和映射

    我正在尝试制作一个包含照片集的相册的模型 每个相册都会有一组照片和一张拇指照片 这是我拥有的 但 EF 似乎不喜欢它 我正在使用 EF CPT5 该模型 public class Album IEntity private DateTime
  • 实体框架代码首先保存后不延迟加载

    我的数据库中有一个查找表和一个数据表 我将使用性别和人物作为例子 假设性别表如下所示 Id Code 1 Male 2 Female 人员表如下所示 Id Name GenderId 1 Bob 1 2 Jane 2 我首先在 EF 代码中
  • 将对象列表添加到 ef 中的上下文

    是否可以在不使用 foreach addObject 的情况下将对象列表添加到实体框架中的 Context 感谢帮助 从 EntityFramework 6 开始 您可以使用DbSet AddRange 方法 IEnumerable htt

随机推荐