Проблема с построением динамических запросов для Entity Framework Core 3.1.6

Я создаю запросы LINQ динамически. Я использую EF Core 3.1.6 (для SQL Server).

Я создаю IQueryable с предложением where(), используя предикат.

EF Core может переводить следующее выражение предиката, и запрос работает должным образом:

{p => ((p.Address != null) AndAlso p.Address.Contains(Convert("6152 Fames Ro", String)))}

Но EF Core не может перевести следующее выражение предиката:

{p => (((p.FirstName != null) AndAlso p.FirstName.Contains(Convert("fred", String))) OrElse ((p.MiddleName != null) AndAlso p.MiddleName.Contains(Convert("fred", String))))}

Это вызывает следующее исключение:

Выражение LINQ 'DbSet \ r \ n .Where (p = ›p.FirstName! = Null && p.FirstName.Contains (fred) || p.MiddleName! = Null && p.MiddleName.Contains (fred))' могло не переводится. Либо перепишите запрос в форме, которая может быть переведена, либо явно переключитесь на оценку клиента, вставив вызов AsEnumerable (), AsAsyncEnumerable (), ToList () или ToListAsync (). См. https://go.microsoft.com/fwlink/?linkid=2101038. для дополнительной информации.

Я рассмотрел следующие вопросы:

Как было предложено в сообщении об исключении, я попытался выполнить AsEnumerable(), ToList() и т. Д. На последнем IQueryable, но это не сработало. Я думаю, это исключит проблему оценки клиента.

Я уверен, что что-то делаю не так; не уверен что.

Может кто поможет? Если потребуется, могу предоставить дополнительную информацию.


person Humayun Khan    schedule 16.10.2020    source источник
comment
Я заметил, что выражение в сообщении об ошибке EF отличается от выражения вашего предиката.   -  person Eldar    schedule 16.10.2020
comment
Выражения предиката, которые я скопировал прямо из непосредственного окна отладчика в Visual Studio. Я предполагаю, что это необработанная форма, которую EF преобразует во что-то, что показано в сообщении об исключении. @Eldar Если мне что-то не хватает в вашем комментарии, дайте мне знать.   -  person Humayun Khan    schedule 16.10.2020


Ответы (1)


Я нашел решение своей проблемы.

Предикат должен быть построен следующим образом:

{p => (((p.FirstName != null) AndAlso p.FirstName.Contains(Convert("fred", String))) OrElse Invoke(p => ((p.MiddleName != null) AndAlso p.MiddleName.Contains(Convert("fred", String))), p))}

Приведенный выше предикат работает нормально.

Чтобы исправить это, я сделал 2 изменения:

  1. Преобразовал следующий метод расширения из этого:
    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1,
            Expression<Func<T, bool>> expr2)
        {
            return Expression.Lambda<Func<T, bool>>
                (Expression.OrElse(expr1.Body, expr2.Body), expr1.Parameters);
        }
    
    в этот:
    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1,
            Expression<Func<T, bool>> expr2)
        {
            var invokedExpr = Expression.Invoke(expr2, expr1.Parameters);
            return Expression.Lambda<Func<T, bool>>
                (Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
        }
    
  2. Изменив способ, я построил постоянное выражение из этого:
    var member = Expression.Property(parameter, propertyInfo);
                var filterValueConstant = Expression.Convert(Expression.Constant(convertedValue), propertyType);
    
    на это:
    var underlyingTypeConstExpr = Expression.Constant(convertedValue);
                var filterValueConstant = Expression.Convert(underlyingTypeConstExpr, propertyType);
    

Это устранило мою проблему. Но я до сих пор не знаю, почему старый предикат не сработал.

person Humayun Khan    schedule 16.10.2020
comment
Спасибо за ответ на ваш вопрос - это поможет другим, у кого есть такая же проблема. Я бы не стал заканчивать ваш ответ другим вопросом. Если вам нужен ответ на вопрос, я, возможно, создам новый вопрос, связав его для справки. - person Wai Ha Lee; 16.10.2020