1

I'm reading code abour how to building an IQueryable Provider

Below is code fragment

internal class QueryTranslator : ExpressionVisitor
    {
        .....
        private static Expression StripQuotes(Expression e)
        {
            while (e.NodeType == ExpressionType.Quote)
            {
                e = ((UnaryExpression)e).Operand;
            }
            return e;
        }
        protected override Expression VisitMethodCall(MethodCallExpression m)
        {
            if (m.Method.DeclaringType == typeof(Queryable))
            {
                if (m.Method.Name == "Where")
                {
                    sb.Append("SELECT * FROM (");
                    this.Visit(m.Arguments[0]);
                    sb.Append(") AS T WHERE ");
                    LambdaExpression lambda = (LambdaExpression)StripQuotes(m.Arguments[1]);
                    this.Visit(lambda.Body);
                    return m;
                }
                else if (m.Method.Name == "Select")
                {
                    LambdaExpression lambda = (LambdaExpression)StripQuotes(m.Arguments[1]);
                    ColumnProjection projection = new ColumnProjector().ProjectColumns(lambda.Body, this.row);
                    sb.Append("SELECT ");
                    sb.Append(projection.Columns);
                    sb.Append(" FROM (");
                    this.Visit(m.Arguments[0]);
                    sb.Append(") AS T ");
                    this.projection = projection;
                    return m;
                }
            }
            throw new NotSupportedException(string.Format("The method '{0}' is not supported", m.Method.Name));
        }
        .....
    }

I can't understand method StripQuotes, What I get from e = ((UnaryExpression)e).Operand ?

user2155362
  • 1,657
  • 5
  • 18
  • 30
  • The operand is the value given to the operator so for example if i do `!false` the operand is `false` meanwhile the operator is `!` – nalka Jan 27 '19 at 14:28

1 Answers1

1

What does Expression.Quote() do that Expression.Constant() can’t already do? explains what Quote does.

The question now is when would the Quote operation be encountered in a real-world scenario by a LINQ provider. It appears when you invoke IQueryable operations that take expression trees as arguments. For example, bool Queryable.Where(IQueryable inputQuery, Expression<Func<..., bool>> predicate) takes an expression tree.

If you say myQueryable.Where(x => ...) the C# compiler will generate Expression.* invocations to build that expression tree and pass that to Where. No Quote here.

But if the expression tree generation is nested then Quote is used:

myQueryable.Where(x => x.SomeCollection.Any(y => ...))

Here, the y => ... part is of type Expression<..., bool>. This requires Quote.

I believe a simple LINQ provider can just unwrap/discard the Quote and generate code as if it didn't exist. If you want to be totally correct you'd need special handling of that case in the LINQ provider. The LINQ provider code in the question is "demo code" quality so it does not do that.

You can use https://sharplab.io/ to play with different kinds of query to see what the compiler generates. E.g.

using System;
using System.Linq;

public class C {
    public void M2() {
        var query =
            new SomeClass[0]
            .AsQueryable()
            .Where(x => x.SomeCollection.Any(y => y % 2 == 0));
    }
    static bool F(System.Linq.Expressions.Expression<Func<int, bool>> e) { return true; }

    class SomeClass {
        public IQueryable<int> SomeCollection { get; set; }
    }
}

https://sharplab.io/#v2:CYLg1APgAgDABFAjAbgLAChYMQOgDICWAdgI5roZQDMCATHAMJwDeGc7CNUALHALK0AFAEoWbDhIBuAQwBOcEgFcAprICecALziJuuEWUB3OAGUA9gFtlDADbSAzvYDaMALo69HHAEF7ARRV1aQAjG2URD084HAB1AAtVcIAPLQA+OCSccysGMxswgGMAFwIzIh8iNUENTXSNAFI4ek1NOBhhYXIJAF8PJAA2OGCzPLgAMUEkfGISHABRJIAHWWVHUqJ7eaWVtbKAHigAVj3iIoAaIZGbVPTlUWYEAHY4ItkVZDhe9Ak++mzrOyOMTfTzUOAASQCqjUITCJyIRXS/1y+WUxXWLDgAHNlEUPvZcR8vj0MF8gA

usr
  • 168,620
  • 35
  • 240
  • 369