Linq для объектов не распознает метод
У меня есть следующие методы:
public int count(
Guid companyId, Expression<Func<T, bool>> isMatch)
{
var filters = new Expression<Func<T, bool>>[]{
x => x.PriceDefinition.CompanyId == companyId,
isMatch
};
return GetCount(filters);
}
public virtual int GetCount(
IEnumerable<Expression<Func<T, bool>>> filters)
{
IQueryable<T> _query = ObjectSet;
if (filters != null)
{
foreach (var filter in filters)
{
_query = _query.Where(filter);
}
}
return _query.Count();
}
При использовании:
count(some_guid, x => x.IsMatch(entityId, inviterId, routeId, luggageTypeId));
Я получаю следующее исключение:
LINQ to Entities does not recognize the method 'Boolean IsMatch(System.Nullable`1[System.Int64], System.Nullable`1[System.Int64], System.Nullable`1[System.Int64], System.Nullable`1[System.Int64])' method, and this method cannot be translated into a store expression.
В чем причина этого?
Как я могу его решить?
Ответы
Ответ 1
При использовании linq-to-entities вы не можете использовать произвольные методы .NET в запросе. Каждый метод, используемый в запросе, должен быть переводимым в SQL. Это не поможет вам вернуть Expession<Func<entityType, bool>>
, потому что условие должно оцениваться для каждой записи на сервере базы данных.
Для EF ваш код означает что-то вроде:
SELECT COUNT(*)
FROM ...
LEFT JOIN ...
WHERE IsMatch(....)
Поскольку EF проверяет имена функций, переданные в запрос, он будет генерировать исключение, поскольку он не знает эквивалент IsMatch на сервере SQL.
Единственными возможными функциями, которые могут использоваться в Linq-to-entity, являются:
EdmFunctions - это методы, помеченные EdmFunctionAttribute
, которые сопоставляют функцию .NET с аналогией SQL. Эти функции обычно не могут выполняться в общем коде .NET, потому что они ничего не делают или не делают исключение. Они являются только владельцем места для Linq-to-entity. Доступные EdmFunctions:
- Предопределенные EdmFunctions в
System.Data.Objects.EntityFunctions
- Предопределенные EdmFunctions для SQL Server (не компактные) в
System.Data.Objects.SqlClient.SqlFunctions
- Пользовательские сопоставленные функции SQL - мастер импорта в дизайнере Entity позволяет импортировать SQL-функции (кроме табличных функций). После этого вы можете написать специальную статическую функцию .NET и сопоставить ее атрибутом
EdmFunction
с функцией SQL, импортированной в конструктор.
- Пользовательские определенные функции - это специальная функция, написанная вручную в файле EDMX (открытая как XML). Это пользовательская многократно используемая часть Entity SQL.
Я уже описал как создать определенную модель функции в другом ответе. Создание сопоставленного Функция SQL довольно похожа. Вместо того, чтобы вручную создавать элемент Function
в EDMX, вы сопоставляете свойства EdmFunctionAttribute
с импортированной функцией SQL.
Ответ 2
Вы передаете выражение, вызывающее функцию с именем IsMatch
.
LINQ to Entities не знает, что делать с этой функцией.
Ответ 3
Я имел дело с подобной проблемой. Рабочее решение использовало .AsEnumerable()
, прежде чем пытаться использовать мой собственный метод. Вы можете посмотреть здесь.
Ответ 4
Фактически, то, что вы просматриваете, выглядит так:
bool anonymous_delagate#123(T entity)
{
return entity.IsMatch(a,b,c,d)
}
Но для этого потребуется, чтобы EF знал, что действительно метод IsMatch
, который вызывается для этого объекта, означает.
Единственное, что я могу сейчас подумать о том, чтобы рекомендовать, - это использовать динамическую форму выражения для создания этого динамического выражения. Или переработайте свой дизайн до некоторого разного.
Фактически, есть более простой и нормальный метод, который требует нескольких шагов для выполнения.
- Сделать метод
IsMatch
static.
- Вернуть
Expression<{your entity here}, bool>
непосредственно из IsMatch
.
- Передайте его как:
({your entity here}.IsMatch({parameters}))
Отдых может оставаться таким же, как у вас сейчас.
Изменить: Пример
Это будет работать с конкретным сущностью, поэтому я хочу, чтобы ваша сущность была Order. Подставьте свою собственную сущность.
public static Expression<Func<Order, bool>> IsMatch(int id, ...) // static method, that returns filtering expression
{
return i => i.Id == id; // create the filtering criteria
}
Затем назовите его так:
count(some_guid, Order.IsMatch(entityId, inviterId, routeId, luggageTypeId));