dnoxs
dnoxs

Reputation: 1541

Using an existing IQueryable to create a new dynamic IQueryable

I have a query as follows:

var query = from x in context.Employees
    where (x.Salary > 0 && x.DeptId == 5) || x.DeptId == 2
    order by x.Surname
    select x;

The above is the original query and returns let's say 1000 employee entities.

I would now like to use the first query to deconstruct it and recreate a new query that would look like this:

var query = from x in context.Employees
    where ((x.Salary > 0 && x.DeptId == 5) || x.DeptId == 2) && (x,i) i % 10 == 0
    order by x.Surname
    select x.Surname;

This query would return 100 surnames.

The syntax is probably incorrect, but what I need to do is attach an additional where clause and modify the select to a single field.

I've been looking into the ExpressionVisitor but I'm not entirely sure how to create a new query based on an existing query.

Any guidance would be appreciated. Thanks you.

Upvotes: 0

Views: 1153

Answers (1)

Double Down
Double Down

Reputation: 908

In an expression visitor you would override the method call. Check if the method is Queryable.Where, and if so, the methods second parameter is a quoted expression of type lambda expression. Fish it out and you can screw with it.

    static void Main()
    {
        IQueryable<int> queryable = new List<int>(Enumerable.Range(0, 10)).AsQueryable();
        IQueryable<string> queryable2 = queryable
            .Where(integer => integer % 2 == 0)
            .OrderBy(x => x)
            .Select(x => x.ToString());
        var expression = Rewrite(queryable2.Expression);
    }

    private static Expression Rewrite(Expression expression)
    {
        var visitor = new AddToWhere();
        return visitor.Visit(expression);
    }

    class AddToWhere : ExpressionVisitor
    {
        protected override Expression VisitMethodCall(MethodCallExpression node)
        {
            ParameterExpression parameter;
            LambdaExpression lambdaExpression;
            if (node.Method.DeclaringType != typeof(Queryable) ||
                node.Method.Name != "Where" ||
                (lambdaExpression = ((UnaryExpression)node.Arguments[1]).Operand as LambdaExpression).Parameters.Count != 1 ||
                (parameter = lambdaExpression.Parameters[0]).Type != typeof(int))
            {
                return base.VisitMethodCall(node);
            }
            return Expression.Call(
                node.Object,
                node.Method,
                this.Visit(node.Arguments[0]),
                Expression.Quote(
                    Expression.Lambda(
                        lambdaExpression.Type,
                        Expression.AndAlso(
                            lambdaExpression.Body,
                            Expression.Equal(
                                Expression.Modulo(
                                    parameter,
                                    Expression.Constant(
                                        4
                                    )
                                ),
                                Expression.Constant(
                                    0
                                )
                            )
                        ),
                        lambdaExpression.Parameters
                    )
                )
            );
        }
    }
}

Upvotes: 1

Related Questions