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

发布于 2024-12-22 00:51:08 字数 872 浏览 2 评论 0原文

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

LINQ to Entities 无法识别该方法 'System.Linq.IQueryable<代码>1[DAL.MyDOClass] 独特[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;
        }
    }

那么如何正确编写自己的 IEqualityComparer 呢?

I have a L2E query that returns some data that contains duplicate objects. I need to remove those duplicate objects. Basically I should assume that if their IDs are the same then the objects are duplicate. I've tried q.Distinct(), but that still returned duplicate objects. Then I've tried implementing my own IEqualityComparer and passing it to the Distinct() method. The method failed with following text:

LINQ to Entities does not recognize the method
'System.Linq.IQueryable1[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.

And here is the implementation of 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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

梦晓ヶ微光ヅ倾城 2024-12-29 00:51:08

EqualityComparer 不是正确的方法 - 它只能过滤内存中的结果集,例如:

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

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

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

An EqualityComparer is not the way to go - it can only filter your result set in memory eg:

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:

var objects = yourResults.GroupBy(o => o.Id).Select(g => g.First());
风铃鹿 2024-12-29 00:51:08

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

他们的答案都涉及这样一个事实:IEqualityComparer 的方法不会转换为 SQL。

我认为值得研究一下每种方法的优缺点,这需要的不仅仅是评论。

rich 的方法将查询重写为具有相同最终结果的不同查询。他们的代码或多或少应该会告诉您如何使用手动编码的 SQL 有效地完成此操作。

拉迪斯拉夫(Ladislav)在不同之前将其从数据库中取出,然后内存中的方法将起作用。

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

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

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

总而言之,我认为 Rich 的答案最有可能是这里的最佳选择,但与 Rich 相比,Ladislav 的不同优点和缺点也使其非常值得研究和考虑。

rich.okelly and Ladislav Mrnka are both correct in different ways.

Both their answers deal with the fact that the IEqualityComparer<T>'s methods won't be translated to 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. Their code should result in more or less how you would efficiently do this with hand-coded SQL.

Ladislav's pulls it out of the database at the point before the distinct, and then an in-memory approach will work.

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.

Generally grouping is more expensive than distinct on in-memory cases (especially if you bring it into memory with AsList() rather than 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.

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.

断桥再见 2024-12-29 00:51:08

你不会的。在数据库上调用 Distinct 运算符,因此您在应用程序中编写的任何代码都无法使用(您无法将相等比较器逻辑移动到 SQL),除非您愿意加载所有非唯一值并使其不同在您的应用程序中进行过滤。

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

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.

var query = (from x in context.EntitySet where ...).ToList()
                                                   .Distinct(yourComparer);
对风讲故事 2024-12-29 00:51:08

迟到的答案,但你可以做得更好:
如果 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;
        }
    }

并且 unique 可以在没有任何重载的情况下工作。

如果没有,您可以像这样创建 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 而不进行任何重载

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:

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:

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文