Question

On a project built with .NET 3.5, I am using LINQ expressions to dynamically generate code at runtime. The LINQ expressions are compiled using the Compile method and stored for later use as predicates with LINQ to objects.

The expressions are sometimes quite complicated and difficult to debug.

Below is an example of an expression viewed through the debugger visualizer in Visual Studio.

{request => (Invoke(workEnvelopeHead => (workEnvelopeHead.Method = value(Wombl.Scenarios.CannedResponses+<>c_DisplayClass58).pipeline), request.WorkEnvelope.Head) And Invoke(body => Invoke(value(Wombl.Scenarios.CannedResponses+<>c_DisplayClass78).isMatch, body.SingleOrDefault()),Convert(request.WorkEnvelope.Body.Any)))}

I would like to be able to optimize expressions like the above so that the value(Wombl.Scenarios.CannedResponses+<>c__DisplayClass58).pipeline expression is replaced with a constant that is the variable's value.

In this particular case, value(Wombl.Scenarios.CannedResponses+<>c__DisplayClass58).pipeline is a reference in the lambda to a variable in the parent scope. Something like:

var pipeline = "[My variable's value here]";
// My lambda expression here, which references pipeline
// Func<RequestType, bool> predicate = request => ........ workEnvelopeHead.Method == pipeline ..........

The original expression, optimized ought to look like:

{request => (Invoke(workEnvelopeHead => (workEnvelopeHead.Method = "[My variable's value here]", request.WorkEnvelope.Head) And Invoke(body => > Invoke(value(Wombl.Scenarios.CannedResponses+<>c__DisplayClass78).isMatch, body.SingleOrDefault()),Convert(request.WorkEnvelope.Body.Any)))}

How can I make such optimizations at runtime to the LINQ expression, before compiling?

Was it helpful?

Solution

So I went ahead and wrote an expression visitor that replaces the variable references with the actual value. It wasn't so hard to do after all.

Usage:

var simplifiedExpression = ExpressionOptimizer.Simplify(complexExpression);

The class:

It inherits from ExpressionVisitor which came from the code samples on this page because in .NET 3.0 it is internal. In .NET 4.0 the class is public but might require some changes to this class.

public sealed class ExpressionOptimizer : ExpressionVisitor
{
    private ExpressionOptimizer()
    {
    }

    #region Methods

    public static Expression<TDelegate> Simplify<TDelegate>(Expression<TDelegate> expression)
    {
        return expression == null
                   ? null
                   : (Expression<TDelegate>) new ExpressionOptimizer().Visit(expression);
    }

    private static bool IsPrimitive(Type type)
    {
        return type.IsPrimitive
               || type.IsEnum
               || type == typeof (string)
               || type == typeof (DateTime)
               || type == typeof (TimeSpan)
               || type == typeof (DateTimeOffset)
               || type == typeof (Decimal)
               || typeof(Delegate).IsAssignableFrom(type);
    }

    protected override Expression VisitMemberAccess(MemberExpression memberExpression)
    {
        var constantExpression = memberExpression.Expression as ConstantExpression;

        if (constantExpression == null || !IsPrimitive(memberExpression.Type))
            return base.VisitMemberAccess(memberExpression);

        // Replace the MemberExpression with a ConstantExpression
        var constantValue = constantExpression.Value;
        var propertyInfo = memberExpression.Member as PropertyInfo;
        var value = propertyInfo == null
                        ? ((FieldInfo) memberExpression.Member).GetValue(constantValue)
                        : propertyInfo.GetValue(constantValue, null);

        return Expression.Constant(value);
    }

    #endregion
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top