Linq To Sql哪里不调用被覆盖的Equals

时间:2012-08-08 18:32:40

标签: linq c#-4.0 linq-to-sql equals equals-operator

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

因此(正如我经常用于单元测试等),我覆盖了Equals(Object),Equals(Product)和GetHashCode并实现了IEquatable,期望我能够编写这样的代码:

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

等等。

Equals覆盖已经过测试并且有效。这些对象是可变的,因此通常的警告适用于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);
    

Retrieve的简化版本(删除了cruft只是参数检查等):

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

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

这是我观察到的: 检索testProduct1成功(毫不奇怪,等于引用) 检索testProduct2失败(null) 在Retrieve调用期间,在Retrieve方法中调用的重写的Equals方法永远不会被命中 但是,重写的Equals方法被SubmitChanges上下文多次调用(在数据库中创建第一个测试对象时调用)(按预期工作)。

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

所以我的具体问题:

  • 我是不是想做一些不明智的事情?似乎直接使用Equals。
  • 第一个问题的推论:处理linq到sql相等性检查的备用建议,同时保持对象内部的比较细节而不是存储库
  • 为什么我可以观察到在SubmitChanges中解析Equals方法但在Where子句中没有解决?
  • 我对理解感兴趣,因为我的Equals调用工作正常。但我也很想学习如何制作这种模式'工作,而不仅仅是理解为什么它似乎是一个反模式'在LtS和C#的比赛中。

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

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

  • 重载并使用==而不是
  • 将lambda变量强制转换为p =>类型; (产品)P
  • 首先获取IQueryable对象并在Where子句中调用Equals

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

  • 创建静态ProductEquals(Product first,Product second)方法:System.NotSupportedException:没有支持的SQL转换。

感谢StackOverflow贡献者!

重新可能的重复:我已经阅读了其他10个问题。我喜欢指向完全重复的指针,但大多数人似乎没有直接解决LinqToSql的奇怪之处。

1 个答案:

答案 0 :(得分:1)

  

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

绝对。考虑LINQ to SQL的作用:它创建查询的SQL表示。它知道被覆盖的Equals方法的作用,因此无法将该逻辑转换为SQL。

  

第一个问题的推论:处理linq到sql相等性检查的备用建议,同时在对象而不是存储库中保留比较细节

您需要使用表达式树来表示相同的方式 - 然后将这些表达式树构建为完整查询。它不会很有趣,但它应该是可能的。它会影响你构建所有查询的方式。

我希望期望大多数数据库表示都是基于ID的,所以你应该只能比较ID是否相等。通常当我看到尝试以OO方式真正建模数据但将其存储在数据库中时,抽象的漏洞已经引起了很大的痛苦。

  

为什么我可以观察到在SubmitChanges中解析Equals方法但在Where子句中没有解决?

据推测,SubmitChanges正在对一组内存中的对象进行处理以找出已更改的内容 - 它不必执行任何转换为​​SQL来执行此操作。