2

When using LINQ Expressions, the C# compiler will conveniently translate C# lambdas into Expression objects:

//using System;
//using System.Linq.Expressions;

Expression<Func<int, bool>> lambda_expression = (int x) => x == 3;

This is convenient, and can save a lot of typing versus explicitly constructing the expression:

Expression<Func<int, bool>> explicit_expression_object;
{
    var x = Expression.Parameter(typeof(int), "x");
    explicit_expression =
        Expression.Lambda<Func<int, bool>>(Expression.Equal(x, Expression.Constant(3)), x);
}

However there are situations when it is necessary to use the "longhand" Expression object syntax, for example when dynamically creating expressions at run time. As such, I currently find myself using a mix of "expression lambdas" and dynamically generated "explicit" expression objects.

Is it possible to "include" or "embed" an Expression object into an expression lambda?

For example:

Expression inner_expression_object = Expression.Constant(3);

Expression<Func<int, bool>> wrapper_expression =
    (int x) => x == inner_expression_object.Embed();
Haddon CD.
  • 169
  • 8

1 Answers1

3

Using an ExpressionVisitor, you can replace calls to an Expression extension method with the Expression.

First, you need an ExpressionVisitor class to expand the Embed method calls to their values:

public class EmbedVisitor : ExpressionVisitor {
    public override Expression Visit(Expression node) {
        if (node?.NodeType == ExpressionType.Call) {
            var callnode = node as MethodCallExpression;
            if (callnode.Method.Name == "Embed" && callnode.Method.DeclaringType == typeof(ExpressionExt))
                return callnode.Arguments[0].Evaluate<Expression>();
        }

        return base.Visit(node);
    }
}

Then you need a static class for the extension methods needed:

public static class ExpressionExt {
    public static T Embed<T>(this Expression e) {
        return default(T);
    }

    public static Expression ExpandEmbed(this Expression orig) => new EmbedVisitor().Visit(orig);

    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();
    }
}

Now you can use these to expand embedded Expression valued sub-expressions:

var inner_expression_object = Expression.Constant(3);

Expression<Func<int, bool>> wrapper_expression =
    (int x) => x == inner_expression_object.Embed<int>();

var expanded = wrapper_expression.ExpandEmbed();
// Expression<Func<int,bool>> expanded == (int x) => x == 3;

You can also directly embed Expression expressions and expand them:

Expression<Func<int,bool>> wrap2 = x => x == Expression.Multiply(Expression.Constant(4), Expression.Constant(8)).Embed<int>();
var expanded2 = wrap2.ExpandEmbed();
// Expression<Func<int,bool>> expanded2 = x => x == 4*8;
NetMage
  • 26,163
  • 3
  • 34
  • 55
  • Liked your solution but I am not clear about various practical use cases, could you please elaborate few – Mrinal Kamboj Apr 20 '18 at 11:40
  • Also is there a good source to learn about Expression trees in depth – Mrinal Kamboj Apr 20 '18 at 11:42
  • Tried using your code like `expanded.Evaluate();`, but this leads to Invalid cast exception in the following line `return (T)Expression.Lambda(e).Compile().DynamicInvoke();` – Mrinal Kamboj Apr 20 '18 at 12:35
  • @MrinalKamboj and what was in `expanded` that would evaluate to an `int`? – NetMage Apr 20 '18 at 16:49
  • @MrinalKamboj [Here](https://blogs.msdn.microsoft.com/meek/2008/05/02/linq-to-entities-combining-predicates/) is an article on one use case using LINQ to Entities (or SQL). – NetMage Apr 20 '18 at 16:53
  • I have exactly copied your code and trying to make it work, no change from my side – Mrinal Kamboj Apr 21 '18 at 03:29
  • You don't use `Evaluate` on `expanded` - `Evaluate` is for internal use (unless you understand a need for it). What were you trying to accomplish with `expanded.Evaluate()`? – NetMage Apr 23 '18 at 17:53