Использование IEqualityComparer с предложением LINQ to Entities Except
-
21-09-2019 - |
Вопрос
У меня есть объект, который я хотел бы сравнить с подмножеством и определить, нужно ли выбирать все, кроме подмножества.
Итак, мой запрос выглядит следующим образом:
Products.Except(ProductsToRemove(), new ProductComparer())
В ProductsToRemove()
метод возвращает List<Product>
после этого он выполняет несколько задач.Итак, в самой простой форме это то, что описано выше.
В ProductComparer()
класс выглядит примерно так:
public class ProductComparer : IEqualityComparer<Product>
{
public bool Equals(Product a, Product b)
{
if (ReferenceEquals(a, b)) return true;
if (ReferenceEquals(a, null) || ReferenceEquals(b, null))
return false;
return a.Id == b.Id;
}
public int GetHashCode(Product product)
{
if (ReferenceEquals(product, null)) return 0;
var hashProductId = product.Id.GetHashCode();
return hashProductId;
}
}
Однако я постоянно получаю следующее исключение:
LINQ для объектов не распознает метод 'System.Linq.IQueryable
1[UnitedOne.Data.Sql.Product] Except[Product](System.Linq.IQueryable
1[UnitedOne.Data.Sql.Product], Система.Коллекции.Общий.IEnumerable1[UnitedOne.Data.Sql.Product], System.Collections.Generic.IEqualityComparer
1[UnitedOne.Data.Sql.Product])' метод, и этот метод не может быть переведен в выражение хранилища.
Решение
Linq to Entities на самом деле не выполняет ваш запрос, это устный перевод ваш код, преобразующий его в TSQL, затем выполняющий его на сервере.
Под прикрытием он закодирован со знанием того, как работают операторы и общие функции и как они связаны с TSQL.Проблема в том, что разработчики L2E понятия не имеют, как именно вы реализуете IEqualityComparer.Поэтому они не могут понять, что когда вы говорите Класс A == Класс B, вы имеете в виду (например) "Где Person.FirstName == Имя И персона.LastName == Фамилия".
Итак, когда интерпретатор L2E обращается к методу, который он не распознает, он выдает это исключение.
Есть два способа обойти это.Во-первых, разработайте Where(), который удовлетворяет вашим требованиям к равенству, но не зависит от какого-либо пользовательского метода.Другими словами, проверяйте на равенство свойства экземпляра, а не метод Equals, определенный в классе.
Во-вторых, вы можете запустить выполнение запроса, а затем выполнить свои сравнения в памяти.Например:
var notThisItem = new Item{Id = "HurrDurr"};
var items = Db.Items.ToArray(); // Sql query executed here
var except = items.Except(notThisItem); // performed in memory
Очевидно, что это приведет к передаче гораздо большего количества данных по проводам и более интенсивному расходованию памяти.Первый вариант, как правило, самый лучший.
Другие советы
Вы пытаетесь преобразовать Except
звоните с вашим заказом IEqualityComparer
в Entity SQL.
Очевидно, что ваш класс не может быть преобразован в SQL.
Вам нужно написать Products.AsEnumerable().Except(ProductsToRemove(), new ProductComparer())
чтобы заставить его выполняться на клиенте.Обратите внимание, что при этом будут загружены все продукты с сервера.
Кстати, ваш ProductComparer
класс должен быть одноэлементным, вот так:
public class ProductComparer : IEqualityComparer<Product> {
private ProductComparer() { }
public static ProductComparer Instance = new ProductComparer();
...
}
В IEqualityComparer<T>
может быть выполнен только локально, он не может быть преобразован в команду SQL, отсюда и ошибка