EF Core - Expression Tree Equivalent for IQueryable Search

.net-core c# entity-framework-core

Accepted Answer

You must definitely create an expression tree, or more generally, aor (C# || ) predicate statement for all (nested)string properties.

(Expression version of your code) Something like this:

public static class FilterExpression
{
    public static IQueryable<T> ApplySearch<T>(this IQueryable<T> source, string search)
    {
        if (source == null) throw new ArgumentNullException(nameof(source));
        if (string.IsNullOrWhiteSpace(search)) return source;

        var parameter = Expression.Parameter(typeof(T), "e");
        // The following simulates closure to let EF Core create parameter rather than constant value (in case you use `Expresssion.Constant(search)`)
        var value = Expression.Property(Expression.Constant(new { search }), nameof(search));
        var body = SearchStrings(parameter, value);
        if (body == null) return source;

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

    static Expression SearchStrings(Expression target, Expression search)
    {
        Expression result = null;

        var properties = target.Type
          .GetProperties()
          .Where(x => x.CanRead);

        foreach (var prop in properties)
        {
            Expression condition = null;
            var propValue = Expression.MakeMemberAccess(target, prop);
            if (prop.PropertyType == typeof(string))
            {
                var comparand = Expression.Call(propValue, nameof(string.ToLower), Type.EmptyTypes);
                condition = Expression.Call(comparand, nameof(string.Contains), Type.EmptyTypes, search);
            }
            else if (!prop.PropertyType.Namespace.StartsWith("System."))
            {
                condition = SearchStrings(propValue, search);
            }
            if (condition != null)
                result = result == null ? condition : Expression.OrElse(result, condition);
        }

        return result;
    }
}

The difference between the generic and non-generic versions is minimal:Where You must create a "call" to the extension method in the query expression tree:

public static IQueryable ApplySearch(this IQueryable source, string search)
{
    if (source == null) throw new ArgumentNullException(nameof(source));
    if (string.IsNullOrWhiteSpace(search)) return source;

    var parameter = Expression.Parameter(source.ElementType, "e");
    var value = Expression.Property(Expression.Constant(new { search }), nameof(search));
    var body = SearchStrings(parameter, value);
    if (body == null) return source;

    var predicate = Expression.Lambda(body, parameter);
    var filtered = Expression.Call(
        typeof(Queryable), nameof(Queryable.Where), new[] { source.ElementType },
        source.Expression, Expression.Quote(predicate));
    return source.Provider.CreateQuery(filtered);
}

Despite the fact that this functions, it is not very helpful since all LINQ extensions methods (includingAsEnumerable(), ToList()' and similar functions provide generic interface.

In both situations, the type of the query element must also be known beforehand, for example.T The generic form ofquery.ElementType in its non-generic form. This is because expression trees are processed in advance, therefore they cannot be used when there are no "objects."item.GetType() . Due to the same factors,IQueryable Translation professionals dislike EF CoreCast inside the query expression tree, "calls."

2
1/16/2020 10:26:57 AM






Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow