Question

i try to query (linq to entities EF Core) a navigation properties collection, so i use any() like this :

var query = context.MyTable.Where(x => x.mycollectionproperties.Any(p => p.myprop == myvar );

It's work perfectly but now i want to construct the predicate and not defined it directly in the query. so i do :

Func<T, bool> mypredicate = (p => p.myprop == myvar);
var query = context.MyTable.Where(x => x.mycollectionproperties.Any(mypredicate);

(I have replace T by my entity name)

but this generate an error : Object of type 'System.Linq.Expressions.TypedParameterExpression' cannot be converted to type 'System.Linq.Expressions.LambdaExpression'.

How can i construct my predicate to use it on Any() collection ? Thank's

1
1
10/30/2018 10:42:38 AM

Popular Answer

This line for example:

var query = context.MyTable.Where(x => x.mycollectionproperties.Any(p => p.myprop == 1));

When compiled will be compiled to something like this:

var xParameter = Expression.Parameter(typeof(Entity1), "x");

var pParameter = Expression.Parameter(typeof(Entity2), "p");

var anyMethod =
    typeof(Enumerable)
        .GetMethods()
        .Single(x => x.Name == "Any" && x.GetParameters().Length == 2)
        .MakeGenericMethod(typeof(Entity2));

var anyCondition = Expression.Lambda<Func<Entity2, bool>>(
    Expression.Equal(
        Expression.Property(
            pParameter,
            typeof(Entity2).GetProperty("myprop").GetMethod),
        Expression.Constant(1, typeof(int))),
    pParameter);

var query = context.MyTable.Where(
    Expression.Lambda<Func<Entity1, bool>>(
        Expression.Call(
            null,
            anyMethod,
            new Expression[] {
                Expression.Property(
                    xParameter,
                    typeof(Entity1).GetProperty("mycollectionproperties").GetMethod),
                anyCondition
            }),
        xParameter));

This is called an expression tree. See this reference for more details: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/expression-trees/

Although the Any method takes a Func, when constructing the expression tree, notice that an expression (Expression<Func<Entity2, bool>>) is given to the Any method.

There doesn't seem to be a way from C# to give the Any method an expression instead of a Func even if the whole thing is an expression tree (I mean in a parameterized way like you want to achieve).

The most obvious way to achieve what you want is to use the code from this post and replace the anyCondition variable with whatever expression you want to use for the condition inside Any.

Another way is to construct part of the expression tree "normally" and pass null to the Any method and then use an expression visitor to replace the null with your expression. Here is how such visitor would look like:

public class AnyMethodArgumentReplacingVisitor : ExpressionVisitor
{
    private readonly Expression expression;

    public AnyMethodArgumentReplacingVisitor(Expression expression)
    {
        this.expression = expression;
    }

    protected override Expression VisitMethodCall(MethodCallExpression node)
    {
        if (node.Method.Name == "Any")
        {
            return Expression.Call(node.Object, node.Method, node.Arguments[0], expression);
        }

        return base.VisitMethodCall(node);
    }
}

Here is how you would use it:

Expression<Func<Entity2, bool>> predicate =
    a => a.myprop == 2;

Expression<Func<Entity1, bool>> expression =
    b => b.mycollectionproperties.Any(null);

var expression2 =
    (Expression<Func<Entity1, bool>>)
        new AnyMethodArgumentReplacingVisitor(predicate).Visit(expression);

Please note that such visitor would replace the call to any Any method. It also assumes that only the overload of Any that takes a predicate is used. There is another overload of Any that does not take a predicate. If you need to use that, you need to adjust the code.

0
10/31/2018 10:04:52 AM


Related Questions





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