Search code examples
.netlinqexpressionvisitor

How to call a function or property in a LINQ Query Provider (ExpressionVisitor)


I am creating a LINQ Provider. And the query could look like this:

customers.Where( (f) => f.Date < DateTime.Now )

In my Query provider I Execute an ExpressionVisitor that reads the query and creates MSSQL queries. But I cannot find out how to Invoke the DateTime.Now property, when I am visiting the ExpressionTree.

When parsing the expressiontree I get the Expression Convert(Datetime.Now). I want to somehow call that property. So I later can append it to the generated sql query. Above I had the query in C# syntax, although I code in VB.NET. The code is as below:

 Protected Overrides Function VisitBinary(expr As BinaryExpression) As Expression
    expr = ETH.ConvertVBStringCompare(expr)

    If (expr.Right.NodeType = ExpressionType.Convert) Then
        Dim a = ETH.CallIt(expr)
    End If

What it does is when I Expression.Right.NodeType is ExpresionType.Convert I step into an helper class:

Friend Shared Function CallIt(ByVal exp As BinaryExpression) As BinaryExpression

    If exp.Left.NodeType = ExpressionType.MemberAccess Then
        Dim compare = CType(exp.Left, MemberExpression)
        Dim compare1 = CType(exp.Right, UnaryExpression)

When I inspect the BinaryExpression I can see the f.Date on the Left side. And If I convert the exp.Right to Unary I can see it as an Convert(DateTime.Now). I need to invoke the DateTime.Now property somehow so I can append it later in the SQL Query. Like this:

select * from customers where Date < "2018-05..."

I just cannot figure out how to invoke the property :(

UPDATE: Okey, so I found out that the msdn article for creating an LINQ Provider has an example of an helper Class, a partial evaluator, that does this job for you. It goes trough the expression tree and evaluates function calls etc. So I use that class in my project now. It's easier and it returns the Expression tree when it's done.


Solution

  • Here is a helper extension method for evaluating Expressions to get their value - you have to specify the return type since Expression doesn't have one.

    public static T Evaluate<T>(this Expression e) {
        //A little optimization for constant expressions
        if (e.NodeType == ExpressionType.Constant)
            return (T)((ConstantExpression)e).Value;
        else
            return (T)Expression.Lambda(e).Compile().DynamicInvoke();
    }