简体   繁体   English

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

[英]How to implement IEqualityComparer to return distinct values?

I have a L2E query that returns some data that contains duplicate objects. 我有一个L2E查询返回一些包含重复对象的数据。 I need to remove those duplicate objects. 我需要删除那些重复的对象。 Basically I should assume that if their IDs are the same then the objects are duplicate. 基本上我应该假设如果他们的ID是相同的,那么对象是重复的。 I've tried q.Distinct() , but that still returned duplicate objects. 我已经尝试过q.Distinct() ,但仍然返回了重复的对象。 Then I've tried implementing my own IEqualityComparer and passing it to the Distinct() method. 然后我尝试实现自己的IEqualityComparer并将其传递给Distinct()方法。 The method failed with following text: 该方法失败,并带有以下文字:

LINQ to Entities does not recognize the method 'System.Linq.IQueryable 1[DAL.MyDOClass] Distinct[MyDOClass](System.Linq.IQueryable 1[DAL.MyDOClass], System.Collections.Generic.IEqualityComparer`1[DAL.MyDOClass])' method, and this method cannot be translated into a store expression. LINQ to Entities无法识别方法'System.Linq.IQueryable 1[DAL.MyDOClass] Distinct[MyDOClass](System.Linq.IQueryable 1 [DAL.MyDOClass],System.Collections.Generic.IEqualityComparer`1 [DAL.MyDOClass ])'方法,并且此方法无法转换为商店表达式。

And here is the implementation of EqualityComparer: 这是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;
        }
    }

So how do I write my own IEqualityComparer properly? 那么如何正确编写自己的IEqualityComparer

An EqualityComparer is not the way to go - it can only filter your result set in memory eg: EqualityComparer不是要走的路 - 它只能过滤你在内存中的结果集,例如:

var objects = yourResults.ToEnumerable().Distinct(yourEqualityComparer);

You can use the GroupBy method to group by IDs and the First method to let your database only retrieve a unique entry per ID eg: 您可以使用GroupBy方法按ID分组,使用First方法让数据库仅检索每个ID的唯一条目,例如:

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

rich.okelly and Ladislav Mrnka are both correct in different ways. rich.okelly和Ladislav Mrnka在不同方面都是正确的。

Both their answers deal with the fact that the IEqualityComparer<T> 's methods won't be translated to SQL. 他们的答案都解决了IEqualityComparer<T>的方法不会被转换为SQL的事实。

I think it's worth looking at the pros and cons of each, which will take a bit more than a comment. 我认为值得看看每个的利弊,这将不仅仅是一个评论。

rich's approach re-writes the query to a different query with the same ultimate result. rich的方法将查询重写为具有相同最终结果的不同查询。 Their code should result in more or less how you would efficiently do this with hand-coded SQL. 他们的代码应该或多或少地导致您使用手工编写的SQL有效地执行此操作。

Ladislav's pulls it out of the database at the point before the distinct, and then an in-memory approach will work. Ladislav在不同之前将其从数据库中拉出来,然后内存中的方法将起作用。

Since the database is great at doing the sort of grouping and filtering rich's depends upon, it will likely be the most performant in this case. 由于数据库非常适合进行丰富的分组和过滤,因此在这种情况下它可能是性能最高的。 You could though find that the complexity of what's going on prior to this grouping is such that Linq-to-entities doesn't nicely generate a single query but rather produces a bunch of queries and then does some of the work in-memory, which could be pretty nasty. 你可以发现,在这个分组之前发生的事情的复杂性使得Linq-to-entities不能很好地生成单个查询,而是产生一堆查询,然后在内存中完成一些工作,可能很讨厌。

Generally grouping is more expensive than distinct on in-memory cases (especially if you bring it into memory with AsList() rather than AsEnumerable() ). 通常,在内存中的情况下分组比AsList()特别是如果你使用AsList()而不是AsEnumerable()将它带入内存)。 So if either you were already going to bring it into memory at this stage due to some other requirement, it would be more performant. 因此,如果您在此阶段由于某些其他要求而已经将其带入内存,那么它将更具性能。

It would also be the only choice if your equality definition was something that didn't relate well to what is available just in the database, and of course it allows you to switch equality definitions if you wanted to do so based on an IEqualityComparer<T> passed as a parameter. 如果您的等式定义与数据库中的可用内容不相关,那么它也是唯一的选择,当然,如果您希望基于IEqualityComparer<T> ,则可以切换相等定义。 IEqualityComparer<T>作为参数传递。

In all, rich's is the answer I'd say would be most-likely to be the best choice here, but the different pros and cons to Ladislav's compared to rich's makes it also well worth studying and considering. 总而言之,富人的答案我认为最有可能是这里的最佳选择,但与富人相比,拉迪斯拉夫的不同利弊使其值得研究和考虑。

You will not. 你不会。 Distinct operator is called on the database so any code you write in your application cannot be used (you cannot move your equality comparator logic to SQL) unless you are happy with loading all non-distinct values and make distinct filtering in your application. 在数据库上调用了Distinct运算符,因此您在应用程序中编写的任何代码都无法使用(您无法将相等比较器逻辑移动到SQL),除非您对加载所有非不同值并在应用程序中进行不同的过滤感到满意。

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

GroupBy()可能是比Distinct()更好的解决方案 - 就像在这个问题 的最高评级答案中提到的那样

Late answer but you can do better: if the DAL object is partial (usually is if it is a DB object), you can extend it like this: 迟到的答案,但你可以做得更好:如果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;
        }
    }

And the distinct will work without any overload in it. 并且独特的将在没有任何过载的情况下工作。

If not, you can create the IEqualityComparer class like this: 如果没有,您可以像这样创建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;
        }
    }

And again, use the Distinct without any overload 再次,使用Distinct没有任何过载

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM