Question

all

I try to build a dynamic linq query by expression tree, below is my code

        Expression<Func<User, bool>> filter = c => c.isAdmin == false;
        Expression<Func<User, bool>> filterForExistUser = c => (c.isfreezed == null ? false : c.isfreezed) != true;
        Expression<Func<User, bool>> finalFilter = Expression.Lambda<Func<User, bool>>(Expression.AndAlso(filter.Body, filterForExistUser.Body), filter.Parameters);

        IQueryable<User> myusers = db.Users.AsQueryable<User>();
        MethodCallExpression whereCallExpression = Expression.Call(
            typeof(Queryable),
            "Where",
            new Type[] { myusers.ElementType },
            myusers.Expression,
            finalFilter);
        IQueryable<User> results = myusers.Provider.CreateQuery<User>(whereCallExpression);
        foreach (User user in results)
            Console.WriteLine(user.UserName);

But system report "unding parameter c" error, how I can fix it?

Thanks

Was it helpful?

Solution

Although the parameters in the filter and filterForExistUser expressions share the same name, they both are referring to different symbols from different scopes. Your new expression uses the parameter from the filter expression, but you left out the parameter for the filterForExistUser. You must rewrite the expression so the same single parameter is used throughout the overall expression.

Here's a general purpose method you can use to combine the predicates:

Expression<Func<TSource, bool>> CombinePredicates<TSource>(
    Expression<Func<TSource, bool>> head,
    params Expression<Func<TSource, bool>>[] tail)
{
    var param = head.Parameters.Single();
    var body = tail.Aggregate(
        head.Body,
        (result, expr) => Expression.AndAlso(result,
            new SubstitutionVisitor
            {
                OldExpr = expr.Parameters.Single(),
                NewExpr = param,
            }.Visit(expr.Body)
        )
    );
    return Expression.Lambda<Func<TSource, bool>>(body, param);
}

public class SubstitutionVisitor : ExpressionVisitor
{
    public Expression OldExpr { get; set; }
    public Expression NewExpr { get; set; }

    public override Expression Visit(Expression node)
    {
        return (node == OldExpr) ? NewExpr : base.Visit(node);
    }
}

The key is the SubstitutionVisitor which is used to substitute the "tail" paramters using the "head" parameter.

So your final filter becomes this:

Expression<Func<User, bool>> finalFilter =
    CombinePredicates(filter, filterForExistUser);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top