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));