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

C# 如何实现IEqualityComparer以返回不同的值?,c#,entity-framework,distinct,iequalitycomparer,C#,Entity Framework,Distinct,Iequalitycomparer,我有一个L2E查询,它返回一些包含重复对象的数据。我需要删除那些重复的对象。基本上,我应该假设如果它们的ID相同,那么对象是重复的。我尝试了q.Distinct(),但仍然返回了重复的对象。然后,我尝试实现自己的IEqualityComparer,并将其传递给Distinct()方法。该方法失败,文本如下: LINQ to实体无法识别该方法 'System.Linq.IQueryable1[DAL.MyDOClass] 不同的[MyDOClass](System.Linq.IQueryable1

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

LINQ to实体无法识别该方法 'System.Linq.IQueryable
1[DAL.MyDOClass]
不同的[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;
        }
    }
内部类MyDOClassComparer:EqualityComparer
{
公共覆盖布尔等于(MyDOClass x,MyDOClass y)
{
返回x.Id==y.Id;
}
公共覆盖int GetHashCode(MyDOClass obj)
{
返回obj==null?0:obj.Id;
}
}

那么,我如何正确编写自己的
IEqualityComparer

一个
EqualityComparer
不是一个好办法-它只能过滤内存中的结果集,例如:

var objects = yourResults.ToEnumerable().Distinct(yourEqualityComparer);
您可以使用
GroupBy
方法按ID分组,也可以使用
First
方法让数据库仅检索每个ID的唯一条目,例如:

var objects = yourResults.GroupBy(o => o.Id).Select(g => g.First());

你不会的
Distinct
运算符在数据库上被调用,因此您在应用程序中编写的任何代码都不能使用(您不能将相等比较器逻辑移动到SQL),除非您乐于加载所有非Distinct值并在应用程序中进行Distinct筛选

var query = (from x in context.EntitySet where ...).ToList()
                                                   .Distinct(yourComparer);

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

他们的两个答案都涉及到这样一个事实:
IEqualityComparer
的方法不会被转换成SQL

我认为值得看一下每种方法的优缺点,这不仅仅是一个评论

rich的方法将查询重新写入另一个具有相同最终结果的查询。他们的代码或多或少会让您知道如何使用手工编码的SQL高效地实现这一点

Ladislav在distinct之前将其从数据库中取出,然后内存中的方法将起作用

由于数据库擅长于进行rich所依赖的分组和过滤,因此在这种情况下,它可能是性能最好的。不过,您可能会发现,在进行分组之前所发生的事情非常复杂,以至于LINQtoEntities不能很好地生成一个查询,而是生成一组查询,然后在内存中执行一些工作,这可能非常糟糕

通常,在内存中的情况下,分组比使用distinct更昂贵(特别是如果您使用
AsList()
而不是
AsEnumerable()
将其放入内存中)。因此,如果在这个阶段,由于其他一些需求,您已经将它放入内存,那么它的性能会更好

如果您的相等定义与数据库中的可用内容没有很好的关联,那么它也是唯一的选择。当然,如果您想根据作为参数传递的
IEqualityComparer
切换相等定义,它允许您切换相等定义


总之,里奇的答案很可能是这里的最佳选择,但与里奇的答案相比,拉迪斯拉夫的不同优点和缺点也很值得研究和考虑。

迟来的答案,但你可以做得更好: 如果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;
        }
    }
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;
        }
    }
public分部类MyDOClass:IEquatable
{
公共覆盖int GetHashCode()
{
返回Id==0?0:Id;
}
公共布尔等于(MyDOClass其他)
{
返回this.Id==other.Id;
}
}
而distinct将在没有任何过载的情况下工作

如果没有,您可以像下面这样创建IEqualityComparer类:

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;
        }
    }
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;
        }
    }
内部类MyDOClassComparer:MyDOClass、IEquatable、IEqualityComparer
{
公共覆盖int GetHashCode()
{
返回Id==0?0:Id;
}
公共布尔等于(MyDOClass其他)
{
返回this.Id==other.Id;
}
公共布尔等于(MyDOClass x,MyDOClass y)
{
返回x.Id==y.Id;
}
public int GetHashCode(MyDOClass obj)
{
返回Id==0?0:Id;
}
}

再次强调,使用Distinct而不过载

为什么
ToList()
而不是
ToEnumerable()
?+1这是一个救生圈,但是请注意,您不能使用。First()而必须使用。FirstOrDefault()我欠您一次教育!其中一个答案我希望我能投票@yoelhalb GroupBy不保证返回的分组都不是空的吗?返回的分组不能为空,因为分组是通过将elements@vijrox我相信@yoelhalb所指的LINQ to SQL提供程序不支持
IQueryable.First
方法,但它确实支持
IQueryable.FirstOrDefault
方法。在本例中,正如您所说,这两种方法在逻辑上都将返回相同的结果(但在提供程序中只实现了这两种方法中的一种)。如果您尝试执行此操作,则在从DB加载数据后,例如,如果您希望在ObservableCollection上执行此操作,则必须使用
asQueryable().GroupBy(o=>o.Id)。选择(c=>c.FirstOrDefault())
GroupBy()
可能是比
Distinct()
更好的解决方案,如上文所述。而不是
返回Id==0?0:Id;