Как реализовать 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
?
Ответы
Ответ 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);
Ответ 4
GroupBy() может быть лучшим решением, чем Distinct() - как здесь, в рейтинге:
Как удалить дубликаты из коллекции с помощью IEqualityComparer, LinQ Distinct
Ответ 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 без перегрузки