KDani
KDani

Reputation: 458

Build predicate for EfCore IQueryable.Where<T>

I'd like to make a Search<T> extension method for IQueryable, but getting a LINQ expression runtime error, which cannot be translated.

public static IQueryable<T> Search<T>(this IQueryable<T> source, Func<T, string> expression, string search)
{
    if (source == null)
        throw new ArgumentNullException(nameof(source));

    if (string.IsNullOrWhiteSpace(search))
        return source;

    return source.Where(x => EF.Functions.Like(expression(x), $"%{search}%"));
}

Usage:

dbContext.People.Search(x => x.Name, "Joe").ToList();

I guess the wrong line is the expression(x), but what is the correct way?

Upvotes: 0

Views: 1211

Answers (2)

KDani
KDani

Reputation: 458

It is a tested and working solution.

public static IQueryable<T> Search<T>(this IQueryable<T> source, Expression<Func<T, string>> expression, string search)
{
    if (source == null)
        throw new ArgumentNullException(nameof(source));

    if (string.IsNullOrWhiteSpace(search))
        return source;

    var property = typeof(T).GetProperty(((MemberExpression)expression.Body).Member.Name);
    var parameter = Expression.Parameter(typeof(T));
    var functions = Expression.Property(null, typeof(EF).GetProperty(nameof(EF.Functions)));
    var like = typeof(DbFunctionsExtensions).GetMethod(nameof(DbFunctionsExtensions.Like),
        new[] { functions.Type, typeof(string), typeof(string) });

    Expression expressionProperty = Expression.Property(parameter, property.Name);

    var body = Expression.Call(null, like, functions, expressionProperty, Expression.Constant($"%{search}%"));

    return source.Where(Expression.Lambda<Func<T, bool>>(body, parameter));
}

Upvotes: 0

benuto
benuto

Reputation: 292

It doesn't use the Like function, but works fine as an extension.

var result = dbContex.Currencies.Search(x=>x.Symbol, "EUR").ToList();


public static IQueryable<T> Search<T>(this IQueryable<T> source,  Expression<Func<T,string>> expression, string search)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));

            if (string.IsNullOrWhiteSpace(search))
                return source;
            var method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
            var value = Expression.Constant(search, typeof(string));
            var exp1 = Expression.Call(expression.Body, method, value);
            var ex2 = Expression.Lambda<Func<T, bool>>(exp1, expression.Parameters[0]);
            

            return source.Where(ex2);
        }

Upvotes: 1

Related Questions