如何实现 IEqualityComparer 以返回不同的值?
Posted
技术标签:
【中文标题】如何实现 IEqualityComparer 以返回不同的值?【英文标题】:How to implement IEqualityComparer to return distinct values? 【发布时间】:2012-01-23 13:21:56 【问题描述】:我有一个 L2E 查询,它返回一些包含重复对象的数据。我需要删除那些重复的对象。基本上我应该假设如果它们的 ID 相同,那么对象是重复的。我试过q.Distinct()
,但仍然返回重复的对象。然后我尝试实现自己的 IEqualityComparer 并将其传递给 Distinct()
方法。该方法失败并显示以下文本:
LINQ to Entities 无法识别该方法 'System.Linq.IQueryable
1[DAL.MyDOClass] Distinct[MyDOClass](System.Linq.IQueryable
1[DAL.MyDOClass], System.Collections.Generic.IEqualityComparer`1[DAL.MyDOClass])' 方法,并且该方法不能翻译成商店表达式。
这里是 EqualityComparer 的实现:
internal class MyDOClassComparer: EqualityComparer<MyDOClass>
public override bool Equals(MyDOClass x, MyDOClass y)
return x.Id == y.Id;
public override int GetHashCode(MyDOClass obj)
return obj == null ? 0 : obj.Id;
那么我该如何正确地写出我自己的IEqualityComparer
呢?
【问题讨论】:
GroupBy()
可能是比Distinct()
更好的解决方案——就像在this question 上提到的in the top rated answer。
【参考方案1】:
EqualityComparer
不是要走的路 - 它只能过滤内存中的结果集,例如:
var objects = yourResults.ToEnumerable().Distinct(yourEqualityComparer);
您可以使用GroupBy
方法按ID 分组,并使用First
方法让您的数据库只检索每个ID 的唯一条目,例如:
var objects = yourResults.GroupBy(o => o.Id).Select(g => g.First());
【讨论】:
+1 这是一个救命稻草,但请注意,您不能使用 .First() 而必须使用 .FirstOrDefault() 我欠你一个教育!我希望我能投票的答案之一! @yoelhalb GroupBy 不保证返回的分组都不为空吗?返回的分组之一不可能为空,因为分组是通过分离元素形成的 @vijrox 我相信@yoelhalb 所指的LINQ to SQL 提供程序不支持IQueryable.First
方法-但它确实支持IQueryable.FirstOrDefault
方法。在这种情况下,正如您所说,两者在逻辑上都会返回相同的结果(但在提供程序中仅实现了两种方法中的一种)。
如果您尝试执行此操作,则在您从数据库加载数据后,例如,如果您想在 ObservableCollectionasQueryable().GroupBy(o => o.Id).Select(c => c.FirstOrDefault())
rich.okelly 和 Ladislav Mrnka 在不同方面都是正确的。
他们的两个答案都涉及IEqualityComparer<T>
的方法不会被转换为 SQL 的事实。
我认为值得看看每个人的优缺点,这将需要更多的评论。
rich 的方法将查询重写为具有相同最终结果的不同查询。他们的代码应该或多或少地导致您如何使用手工编码的 SQL 有效地执行此操作。
Ladislav's 将它从数据库中提取出来,然后在内存中的方法将起作用。
由于数据库非常擅长对富人进行分组和过滤,因此在这种情况下它可能是性能最高的。您可能会发现在此分组之前发生的事情的复杂性使得 Linq-to-entities 不能很好地生成单个查询,而是生成一堆查询,然后在内存中完成一些工作,这可能很讨厌。
在内存中的情况下,通常分组比区分更昂贵(特别是如果您使用AsList()
而不是AsEnumerable()
将其放入内存)。因此,如果由于某些其他要求,您已经在此阶段将其放入内存中,那么它的性能会更高。
如果您的等式定义与数据库中可用的内容没有很好的相关性,这也是唯一的选择,当然,如果您想基于IEqualityComparer<T>
作为参数传递。
总而言之,富人是我想说的最有可能成为最佳选择的答案,但与富人相比,拉迪斯拉夫的优缺点各不相同,因此也值得研究和考虑。
【讨论】:
【参考方案3】:你不会的。 Distinct
运算符在数据库上调用,因此您在应用程序中编写的任何代码都不能使用(您不能将相等比较器逻辑移动到 SQL),除非您对加载所有非不同值并在应用程序中进行不同过滤感到满意。
var query = (from x in context.EntitySet where ...).ToList()
.Distinct(yourComparer);
【讨论】:
为什么是ToList()
而不是ToEnumerable()
?【参考方案4】:
迟到的答案,但你可以做得更好: 如果 DAL 对象是部分的(通常是 DB 对象),您可以像这样扩展它:
public partial class MyDOClass : IEquatable<MyDOClass>
public override int GetHashCode()
return Id == 0 ? 0 : Id;
public bool Equals(MyDOClass other)
return this.Id == other.Id;
而且 distinct 可以在没有任何过载的情况下工作。
如果没有,您可以像这样创建 IEqualityComparer 类:
internal class MyDOClassComparer : MyDOClass, IEquatable<MyDOClass>, IEqualityComparer<MyDOClass>
public override int GetHashCode()
return Id == 0 ? 0 : Id;
public bool Equals(MyDOClass other)
return this.Id == other.Id;
public bool Equals(MyDOClass x, MyDOClass y)
return x.Id == y.Id;
public int GetHashCode(MyDOClass obj)
return Id == 0 ? 0 : Id;
再一次,使用 Distinct 没有任何过载
【讨论】:
而不是return Id == 0 ? 0 : Id;
,它可能只是return Id;
以上是关于如何实现 IEqualityComparer 以返回不同的值?的主要内容,如果未能解决你的问题,请参考以下文章
如何在不可变的泛型 Pair 结构上实现 IEqualityComparer?
使用带有容差的 IEqualityComparer GetHashCode