2011-12-19 4 views
32

У меня есть запрос L2E, который возвращает некоторые данные, содержащие повторяющиеся объекты. Мне нужно удалить эти повторяющиеся объекты. В принципе, я должен предположить, что если их идентификаторы одинаковы, объекты дублируются. Я пробовал q.Distinct(), но это все равно возвращало повторяющиеся объекты. Затем я попытался реализовать свой собственный IEqualityComparer и передать его методу Distinct(). Метод потерпел неудачу с текстом:Как реализовать IEqualityComparer для возврата отдельных значений?

LINQ к 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 правильно?

ответ

86

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

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

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

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

+1 Это спасатель, однако обратите внимание, что вы не можете использовать. Сначала() вместо этого вам придется использовать .FirstOrDefault() –

+0

Я должен вам образование! Один из тех ответов, на которые я хотел бы высказать свое мнение! – seebiscuit

+0

@yoelhalb не гарантирует GroupBy ни одна из возвращенных групп не пуста? Нет способа, чтобы одна из возвращаемых групп была пустой, так как группировки формируются путем выделения элементов – vijrox

7

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

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

Почему' ToList() ', а не' ToEnumerable() '? –

+2

@Jon: Вы правы. 'ToEnumerable' будет достаточно. –

14

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

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

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

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

Ladislav вытаскивает его из базы данных в точке перед отчетливым, а затем работает in-memory.

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

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

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

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

1

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

Смежные вопросы