Подтвердить что ты не робот

Как реализовать IEqualityComparer для возврата отдельных значений?

У меня есть запрос 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?

4b9b3361

Ответ 1

An EqualityComparer не подходит - он может только фильтровать ваш результирующий набор в памяти, например:

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

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

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

Ответ 2

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

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

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

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

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

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

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

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

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

Ответ 3

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

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

Ответ 5

Поздний ответ, но вы можете сделать лучше: если объект 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;
        }
    }

И отдельный будет работать без перегрузки в нем.

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