Search code examples
c#.netlinqexpression-trees

Getting projections from MethodCallExpression


I have a Linq query with a select, from my Linq query provider it I get an expression tree containing a MethodCallExpression, but just how can I get the select projections from the MethodCallExpression?

    internal static object Execute(Expression expression, bool isEnumerable)
    {
        var whereExpression = expression as MethodCallExpression;
        if (whereExpression == null) throw new InvalidProgramException("Error");

        foreach (var arg in whereExpression.Arguments)
        {
            if (arg is UnaryExpression)
            {
                var unaryExpression = arg as UnaryExpression;

                var lambdaExpression = unaryExpression.Operand as LambdaExpression;
                if (lambdaExpression == null) continue;

                // Here I would like to get the select projections, in this example the "word" projection ...

Query may look like:

var queryable = new MyQueriableClass();

var query = from thing in queryable 
            where thing.id == 1
            select word;

Solution

  • It isn't exactly clear what you are doing, but

    // note the AsQueryable! Otherwise there is no
    // Expression tree!
    var words = new List<string>() { "an", "apple", "a", "day" }.AsQueryable();
    
    // Note that even IQueryable<string> query = words; 
    // is a perfectly good query without a projection!
    // The query 
    // from word in words where word.Length > 0 select word
    // doesn't have a select too (try looking at the 
    // expression tree... The select has been elided)
    // The projection if not present is implicit, the
    // whole object.
    var query = from word in words
                select word;
    
    var exp = query.Expression;
    var methodCallExpression = exp as MethodCallExpression;
    
    if (methodCallExpression != null)
    {
        MethodInfo method = methodCallExpression.Method;
    
        if (method.DeclaringType == typeof(Queryable) && method.Name == "Select")
        {
            var source = methodCallExpression.Arguments[0];
            var selector = methodCallExpression.Arguments[1];
    
            // The selector parameter passed to Select is an
            // Expression.Quote(subexpression),
            // where subexpression is the lambda expression
            // word => word here
            if (selector.NodeType != ExpressionType.Quote)
            {
                throw new NotSupportedException();
            }
    
            UnaryExpression unary = (UnaryExpression)selector;
            Expression operand = unary.Operand;
    
            if (operand.NodeType != ExpressionType.Lambda)
            {
                throw new NotSupportedException();
            }
    
            LambdaExpression lambda = (LambdaExpression)operand;
    
            // This is the "thing" that returns the result
            Expression body = lambda.Body; 
        }
    }
    

    The body at the end should be what you want (or perhaps the lambda just before the end). Note the comments at the beginning of the code block.