Linq To Sql Where 不调用被覆盖的等于



我目前正在做一个项目,我将对类似的非数据库(本讨论的服务层对象)对象和通过 LinqToSql 从数据库中检索的对象进行大量比较。 为了便于讨论,假设我有一个服务层产品对象,其中包含在数据库中表示的字符串字段。 但是,在数据库中,还有一个主键 ID 未在服务层中表示。

因此(就像我经常在单元测试等中所做的那样),我覆盖了 Equals(Object)、Equals(Product) 和 GetHashCode 并实现了 IEquatable,期望我能够编写这样的代码:

myContext.Products.Where(p => p.Equals(passedInProduct).SingleOrDefault();

等等。

"等于"覆盖已经过测试并有效。 这些对象是可变的,因此通常的警告适用于 GetHashCode 覆盖。 但是,出于此示例的目的,除了 LtS 之外,不会修改对象,并且可以将其设置为只读。

下面是一个简单的测试:

  • 在内存中创建测试对象并提交到 LtS 上下文。 通过提交,测试对象将填充一些自动生成的字段。
  • 在内存中创建另一个相同的测试对象(单独的引用)
  • 尝试使用第二个对象作为条件从数据库中检索第一个对象。(请参阅上面的代码行)。

        // Setup
        string productDesc = "12A";
        Product testProduct1 = _CreateTestProductInDatabase(productDesc);
        Product testProduct2 = _CreateTestProduct(productDesc);
        // check setup
        Product retrievedProduct1 = ProductRepo.Retrieve(testProduct1);
        //Assert.IsNotNull(retrievedProduct1);
        // execute - try to retrieve the 'equivalent' product object
        Product retrievedProduct2 = ProductRepo.Retrieve(testProduct2);
    

检索的简化版本(删除的垃圾只是参数检查等):

using (var dbContext = new ProductDataContext()) {
    Product retrievedProduct = dbContext.Products
         .Where(p => p.Equals(product)).SingleOrDefault();

注意:被覆盖的 Equals 方法知道不关心数据库中自动生成的字段,只查看服务层中表示的字符串。

以下是我观察到的:在测试产品 1 上检索成功(不足为奇,引用等于)在测试产品 2 上检索失败 (空)在检索方法中调用的重写的等于方法在检索调用期间永远不会命中但是,重写的 Equals 方法由 SubmitChanges 上的上下文多次调用(在数据库中创建第一个测试对象时调用)(按预期工作)。

静态地,编译器知道正在发出的对象的类型,并且能够解析该类型。

所以我的具体问题:

  • 我是不是想做一些不明智的事情? 似乎是平等的直接使用。
  • 第一个问题的推论:处理 LINQ 到 SQL 相等性检查的替代建议,同时将比较详细信息保留在对象而不是存储库中
  • 为什么我可能会观察到 Equals 方法在提交更改中解析,而不是在 Where 子句中解析?
  • 我对理解的兴趣与使我的 Equals 呼叫工作一样多。 但我也想学习如何使这种"模式"工作,而不仅仅是理解为什么它在 LtS 和 C# 的竞争中似乎是一种"反模式"。

请不要建议我直接使用 Where 语句过滤上下文。 显然,我可以删除 Equals 调用并执行此操作。 但是,其他一些对象(此处未介绍)很大且有点复杂。 为了维护和清晰起见,我想在一个地方保留如何将自己与另一种类型进行比较的知识,理想情况下作为所讨论对象的一部分。

我尝试的其他一些事情并没有改变行为:

  • 重载并使用 == 代替
  • 将 lambda 变量转换为类型 p => (产品)p
  • 首先获取一个 IQueryable 对象并在 Where 子句中调用 Equals

我尝试的其他一些不起作用的事情:

  • 创建静态 ProductEquals(产品第一,产品第二)方法:System.NotSupportedException:不支持转换为 SQL。

感谢 StackOverflow 贡献者!

重新可能的重复:我已经阅读了~10个其他问题。 我希望有一个指向精确副本的指针,但大多数似乎并没有直接解决 LinqToSql 的一个奇怪之处。

我是不是想做一些不明智的事情?

绝对。考虑一下 LINQ to SQL 的作用:它创建查询的 SQL 表示形式。它不知道您重写的Equals方法的作用,因此无法将该逻辑转换为 SQL。

第一个问题的推论:处理 LINQ 到 SQL 相等性检查的替代建议,同时将比较详细信息保留在对象而不是存储库中

您需要对表达式树执行一些操作以这种方式表示相等性 - 然后将这些表达式树构建为完整查询。这不会很有趣,但应该是可能的。不过,它会影响您构建所有查询的方式。

不过,我本来希望大多数数据库表示都是基于 ID 的,因此您应该能够比较 ID 的相等性。通常,当我看到尝试以OO方式对数据进行真正建模但将其存储在数据库中时,抽象的泄漏造成了很多痛苦。

为什么我可能会观察到 Equals 方法在提交更改中解析,而不是在 Where 子句中解析?

据推测,SubmitChanges正在处理一组内存中的对象来找出更改的内容 - 它不必进行任何转换为SQL即可完成该部分。

最新更新