Search code examples
c#expression-treesef-core-3.0

How to link two expressions with Any to formulate an EF Core query


I'm trying to formulate an Expression for a condition of an EF Core query. What I have so far is an expression that transforms my query result type to an IEnumerable and a predicate for the type of the IEnumerable that transforms it into a bool. Now I want to link them with an Any condition. What I was trying so far is this:

public static Expression<Func<TIn, bool>> Any<TIn, T>(
        Expression<Func<TIn, IEnumerable<T>>> valueFunction,
        Expression<Func<T, bool>> predicate)
{
    var call = Expression.Call(typeof(Queryable), nameof(Queryable.Any), new[] { typeof(T) }, value, predicate);
    return Expression.Lambda<Func<TIn, bool>>(call);
}

This throws the following exception:

System.InvalidOperationException : No generic method 'Any' on type 'System.Linq.Queryable' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic.

I would imagine that this happens because I try to use the Expression of the Func and not a ParameterExpression to call the Any method.

So my question is, is it even possible to do that and if yes how? Thank you in advance!


Solution

  • Try this:

    public static Expression<Func<TIn, bool>> Any<TIn, T>(
               Expression<Func<TIn, IEnumerable<T>>> valueFunction,
               Expression<Func<T, bool>> predicate)
    {
        var method = typeof(Enumerable).GetMethods()
            .Where(mi => mi.Name =="Any" && mi.GetParameters().Length == 2)
            .Single()
            .MakeGenericMethod(typeof(T));
        var call = Expression.Call(method, valueFunction.Body, predicate);
        return Expression.Lambda<Func<TIn, bool>>(call, valueFunction.Parameters);
    }
    

    So you had few issues here. First of all your selector expression returns IEnumerable so you need to get method from Enumerable (if you will need to handle IQueryable's a little more work will be needed, like checking valueFunction.Body.Type to implement IQueryable). Then you need to select correct method and since Any is generic one - you need to set specific type to the type parameters, thus creating a MethodInfo object that represents a particular constructed method (MakeGenericMethod). And lastly use valueFunction.Body for one of the Call arguments.