使用IEqualityComparer与LINQ to Entities Except子句

5

我有一个实体,想要将其与子集进行比较,并确定选择除子集之外的所有内容。

因此,我的查询如下所示:

Products.Except(ProductsToRemove(), new ProductComparer())

ProductsToRemove() 方法执行几个任务后,返回一个 List<Product>。所以在最简单的形式中,代码如上所示。

ProductComparer() 类如下:

public class ProductComparer : IEqualityComparer<Product>
{
    public bool Equals(Product a, Product b)
    {
        if (ReferenceEquals(a, b)) return true;

        if (ReferenceEquals(a, null) || ReferenceEquals(b, null))
            return false;

        return a.Id == b.Id;
    }

    public int GetHashCode(Product product)
    {
        if (ReferenceEquals(product, null)) return 0;
        var hashProductId = product.Id.GetHashCode();
        return hashProductId;
    }
}

然而,我不断收到以下异常:

LINQ to Entities 不认识方法 'System.Linq.IQueryable1[UnitedOne.Data.Sql.Product] Except[Product](System.Linq.IQueryable1[UnitedOne.Data.Sql.Product], System.Collections.Generic.IEnumerable1[UnitedOne.Data.Sql.Product], System.Collections.Generic.IEqualityComparer1[UnitedOne.Data.Sql.Product])',这个方法无法转换成存储表达式。

请注意,本文中的 html 标签已保留。
3个回答

8
Linq to Entities实际上并没有执行您的查询,它是解释您的代码,将其转换为TSQL,然后在服务器上执行。在底层,它编码了运算符和常见函数的操作方式以及它们与TSQL的关系。问题在于,L2E的开发人员不知道您如何实现IEqualityComparer。因此,他们无法确定当您说“Class A == Class B”时,您的意思是(例如)“Where Person.FirstName == FirstName AND Person.LastName == LastName”。因此,当L2E解释器遇到它不认识的方法时,会抛出此异常。有两种方法可以解决这个问题。首先,开发一个满足您相等性要求但不依赖任何自定义方法的“Where()”。换句话说,测试实例属性的相等性而不是在类上定义的“Equals”方法。其次,您可以触发查询的执行,然后在内存中进行比较。例如:
var notThisItem = new Item{Id = "HurrDurr"};
var items = Db.Items.ToArray(); // Sql query executed here
var except = items.Except(notThisItem); // performed in memory

显然,这将带来更多的数据传输和更高的内存需求。第一种选择通常是最佳的。


1
谢谢你的建议。在这种情况下,我们只谈论不到100行,所以我认为第二个选项还不错。我可能会缓存结果以加速未来的查找。谢谢! - Jason N. Gaylord
你可以使用 AsEnumerable 代替 ToArray,它也会在内存中执行,但在开始处理之前不需要将所有内容从数据库加载到内存中。 这可能会产生巨大的差异。 - Tim Schmelter

4
您正在尝试使用自定义的IEqualityComparerExcept调用转换为实体SQL。

显然,您的类无法转换为SQL。

您需要编写Products.AsEnumerable().Except(ProductsToRemove(), new ProductComparer())来强制在客户端上执行。请注意,这将从服务器下载所有产品。


顺便说一下,您的ProductComparer类应该是一个单例,像这样:

public class ProductComparer : IEqualityComparer<Product> {
    private ProductComparer() { }
    public static ProductComparer Instance = new ProductComparer();

    ...
}

嘿Slaks,你能详细说明一下如何使用IEqualityComparer作为单例吗?是出于性能方面的考虑还是其他原因?因为我在MSDN文档中甚至都找不到使用IEqualityComparer作为单例的示例。 - mare
2
@Mare:由于个别实例之间没有区别,因此拥有多个实例是没有意义的。 - SLaks

3
IEqualityComparer<T> 只能在本地执行,无法转换为 SQL 命令,因此会出现错误。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接