Как реализовать IEqualityComparer для возврата различных значений? - PullRequest
42 голосов
/ 19 декабря 2011

У меня есть запрос L2E, который возвращает некоторые данные, которые содержат дубликаты объектов. Мне нужно удалить эти дубликаты объектов. По сути, я должен предположить, что если их идентификаторы совпадают, то объекты дублируются. Я пробовал 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 правильно?

Ответы [ 5 ]

114 голосов
/ 19 декабря 2011

EqualityComparer - это не то, что нужно - оно может фильтровать только ваш набор результатов в памяти, например:

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

Вы можете использовать метод GroupBy для группировки по идентификаторам и метод First, чтобы позволить вашей базе данных получать уникальную запись только по идентификатору, например:

var objects = yourResults.GroupBy(o => o.Id).Select(g => g.First());
16 голосов
/ 19 декабря 2011

rich.okelly и Ladislav Mrnka по-разному правы.

Оба их ответа касаются того факта, что методы IEqualityComparer<T> не будут переведены в SQL.

Я думаю, что стоит взглянуть на плюсы и минусы каждого, что займет немного больше, чем комментарий.

Подход rich переписывает запрос в другой запрос с тем же конечным результатом.Их код должен привести к более или менее эффективному выполнению этого действия с SQL-кодом, написанным вручную.

Ладислав вытаскивает его из базы данных в точке, предшествующей отдельному, и тогда будет работать подход в памяти.

Поскольку база данных отлично справляется с определенными задачами группировки и фильтрации, она, вероятно, будет наиболее производительной в этом случае.Тем не менее, вы можете обнаружить, что сложность того, что происходит до этой группировки, такова, что Linq-to-entity не генерирует ни одного отдельного запроса, а генерирует несколько запросов, а затем выполняет некоторую работу в памяти, котораяможет быть довольно неприятным.

Как правило, группировка обходится дороже, чем в случае с оперативной памятью (особенно если вы вносите ее в память с помощью AsList(), а не AsEnumerable()).Так что, если вы уже собирались внести это в память на этом этапе из-за какого-то другого требования, это было бы более производительным.

Это также был бы единственный выбор, если бы ваше определение равенства было чем-то, что нехорошо соотносятся с тем, что доступно только в базе данных, и, конечно, это позволяет вам переключать определения равенства, если вы хотите сделать это, основываясь на IEqualityComparer<T>, передаваемом в качестве параметра.

Вообще, rich этоЯ бы сказал, что ответ, скорее всего, будет лучшим выбором здесь, но различные плюсы и минусы Ладислава по сравнению с богатыми делают его также достойным изучения и рассмотрения.

7 голосов
/ 19 декабря 2011

Вы не будете.Оператор Distinct вызывается для базы данных, поэтому любой код, который вы пишете в своем приложении, не может быть использован (вы не можете переместить логику компаратора равенства в SQL), если вы не довольны загрузкой всех неделимых значений и не выполняете отдельную фильтрацию в своем приложении.

var query = (from x in context.EntitySet where ...).ToList()
                                                   .Distinct(yourComparer);
2 голосов
/ 15 ноября 2015

Поздний ответ, но вы можете сделать лучше: если объект DAL является частичным (обычно, если это объект БД), вы можете расширить его следующим образом:

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;
        }
    }

И отличное будет работать без перегрузки.

Если нет, вы можете создать класс 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 без перегрузки

2 голосов
/ 12 января 2015

GroupBy() может быть лучшим решением, чем Distinct() - как упомянуто в топовом ответе на этот вопрос .

...