Дерево выражений, несколько строк. Предикат содержит не может быть переведен - PullRequest
1 голос
/ 05 августа 2020

Я пытаюсь создать динамический c поиск, который ищет строку в определенном c наборе свойств. Нет никаких проблем в том, чтобы сделать это для одного свойства, но когда я добавляю более одного, возникает следующая ошибка:

InvalidOperationException: выражение LINQ 'DbSet <Person> .Where (a => a.Id.ToString (). Contains ("test") || a.Name.ToString (). Contains ("test"))

не может быть переведено. ** Перепишите запрос в форме, которая может быть переведена, или явно переключиться на оценку клиента, вставив вызов в AsEnumerable (), AsAsyncEnumerable (), ToList () или ToListAsyn c ().

    public class Person {
     public string Name {get; set;}
     public int Id {get; set;}
    }   

    private static Expression GetMemberExpression(ParameterExpression parameter, string propertyName)
    {
        Expression body = parameter;
        foreach (var member in propertyName.Split('.'))
        {
            body = Expression.PropertyOrField(body, member);
        }
        return body;
    }


    public static IQueryable<T> Search<T>(this IQueryable<T> source, string searchCriteria, string searchQuery)
    {
        if (string.IsNullOrWhiteSpace(searchQuery) || string.IsNullOrWhiteSpace(searchCriteria))
        {
            return source;
        }

        ParameterExpression parameter = Expression.Parameter(source.ElementType);
        List<Expression> expTree = new List<Expression>();

        foreach (var propertyName in searchCriteria.Split(","))
        {
            Expression memberExpression = GetMemberExpression(parameter, propertyName);

            MethodCallExpression toStringExpression = Expression.Call(memberExpression, typeof(object).GetMethod("ToString"));

            ConstantExpression constantExpression = Expression.Constant(searchQuery.ToLower(), typeof(string));

            MethodCallExpression containsExpression = Expression.Call(toStringExpression, typeof(string)
                .GetMethod("Contains", new[] { typeof(string) }), constantExpression);

            expTree.Add(containsExpression);
        }

        Expression body = expTree
            .Skip(1)
            .Aggregate(expTree.FirstOrDefault(),
                (current, exp) => Expression.OrElse(current, exp));

        var lambda = Expression.Lambda(body, parameter);

        Expression whereCallExpression = Expression.Call(typeof(Queryable),
            "Where",
            new Type[] { source.ElementType },
            source.Expression, lambda);

        return source.Provider.CreateQuery<T>(whereCallExpression);
    }

public async Task Test() {
  await dbContext.People.Search("Name","test").ToListAsync(); // this is fine
  await dbContext.People.Search("Id,Name","test").ToListAsync(); // Throws an exception

}
...