如何实现IEqualityComparer返回不同的值?

Bog*_*ets 44 c# entity-framework distinct iequalitycomparer

我有一个L2E查询返回一些包含重复对象的数据.我需要删除那些重复的对象.基本上我应该假设如果他们的ID是相同的,那么对象是重复的.我试过了q.Distinct(),但仍然返回了重复的对象.然后我尝试实现自己的IEqualityComparer并将其传递给Distinct()方法.该方法失败,并带有以下文字:

LINQ to Entities无法识别方法'System.Linq.IQueryable 1[DAL.MyDOClass] Distinct[MyDOClass](System.Linq.IQueryable1 [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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

那我该怎么写自己的IEqualityComparer呢?

Ric*_*lly 120

一个EqualityComparer不是要走的路 - 它只能过滤你在内存中的结果集,例如:

var objects = yourResults.ToEnumerable().Distinct(yourEqualityComparer);
Run Code Online (Sandbox Code Playgroud)

您可以使用该GroupBy方法按ID和方法进行分组,First以使您的数据库仅检索每个ID的唯一条目,例如:

var objects = yourResults.GroupBy(o => o.Id).Select(g => g.First());
Run Code Online (Sandbox Code Playgroud)

  • +1这是一个救生员,但请注意,你不能使用.First()代替你必须使用.FirstOrDefault() (9认同)
  • @yoelhalb GroupBy 不保证返回的分组都不为空吗?返回的分组之一不可能为空,因为分组是通过分离元素形成的 (2认同)
  • @vijrox我相信@yoelhalb所指的LINQ to SQL提供程序不支持`IQueryable.First`方法 - 但它确实支持`IQueryable.FirstOrDefault`方法.在这种情况下,正如您所说,逻辑上都会返回相同的结果(但在提供程序中只实现了两种方法中的一种). (2认同)

Jon*_*nna 16

rich.okelly和Ladislav Mrnka在不同方面都是正确的.

他们的答案都涉及这样一个事实:这些IEqualityComparer<T>方法不会被翻译成SQL.

我认为值得看看每个的利弊,这将不仅仅是一个评论.

rich的方法将查询重写为具有相同最终结果的不同查询.他们的代码应该或多或少地导致您使用手工编写的SQL有效地执行此操作.

Ladislav在不同之前将其从数据库中拉出来,然后内存中的方法将起作用.

由于数据库非常适合进行丰富的分组和过滤,因此在这种情况下它可能是性能最高的.你可以发现,在这个分组之前发生的事情的复杂性使得Linq-to-entities不能很好地生成单个查询,而是产生一堆查询,然后在内存中完成一些工作,可能很讨厌.

一般情况下,在内存情况下分组比分辨率更高(特别是如果你将它带入内存AsList()而不是AsEnumerable()).因此,如果您在此阶段由于某些其他要求而已经将其带入内存,那么它将更具性能.

如果您的等式定义与数据库中的可用内容不相关,那么它也是唯一的选择,当然,如果您希望在基于IEqualityComparer<T>传递为参数.

总而言之,富人的答案我认为最有可能是这里的最佳选择,但与富人相比,拉迪斯拉夫的不同利弊使其值得研究和考虑.


Lad*_*nka 7

你不会.Distinct在数据库上调用operator,因此无法使用在应用程序中编写的任何代码(不能将等式比较器逻辑移动到SQL),除非您对加载所有非不同值并在应用程序中进行不同的过滤感到满意.

var query = (from x in context.EntitySet where ...).ToList()
                                                   .Distinct(yourComparer);
Run Code Online (Sandbox Code Playgroud)

  • 为什么`ToList()`而不是`ToEnumerable()`? (4认同)
  • @Jon:你是对的."ToEnumerable"就足够了. (2认同)

gil*_* kr 6

迟到的答案,但您可以做得更好:如果 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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

并且不同的将在没有任何过载的情况下工作。

如果没有,您可以像这样创建 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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

再一次,在没有任何过载的情况下使用 Distinct

  • 而不是`return Id == 0?0 : Id;` 可能只是 `return Id;` (2认同)