How to: Modify Expression Trees

This topic shows you how to modify an expression tree. Expression trees are immutable, which means that they cannot be modified directly. To change an expression tree, you must create a copy of an existing expression tree and when you create the copy, make the required changes. You can use an expression tree visitor to traverse an existing expression tree and to copy each node that it visits.

To modify an expression tree

  1. In Visual Studio, create a new Console Application project.

  2. Add a reference to System.Core.dll, if it is not already referenced.

  3. Add the ExpressionVisitor class to your project.

    This code is available in How to: Implement an Expression Tree Visitor.

    Add using directives (or Imports statements in Visual Basic) to the file for the following namespaces: System.Collections.Generic, System.Collections.ObjectModel, and System.Linq.Expressions.

  4. Add the AndAlsoModifier class to your project.

    This class inherits the ExpressionVisitor class and is specialized to modify expressions that represent conditional AND operations. It changes these operations from a conditional AND to a conditional OR. To do this, the class overrides the VisitBinary method of the base type, because conditional AND expressions are represented as binary expressions. In the VisitBinary method, if the expression that is passed to it represents a conditional AND operation, the code constructs a new expression that contains the conditional OR operator instead of the conditional AND operator. If the expression that is passed to VisitBinary does not represent a conditional AND operation, the method defers to the base class implementation. The base class methods construct nodes that are like the expression trees that are passed in, but the nodes have their sub trees replaced with the expression trees that are produced recursively by the visitor.

    Public Class AndAlsoModifier
        Inherits ExpressionVisitor
    
        Public Function Modify(ByVal expr As Expression) As Expression
            Return Visit(expr)
        End Function 
    
        Protected Overrides Function VisitBinary(ByVal b As BinaryExpression) As Expression
            If b.NodeType = ExpressionType.AndAlso Then 
                Dim left = Me.Visit(b.Left)
                Dim right = Me.Visit(b.Right)
    
                ' Make this binary expression an OrElse operation instead  
                ' of an AndAlso operation. 
                Return Expression.MakeBinary(ExpressionType.OrElse, left, right, _
                                             b.IsLiftedToNull, b.Method)
            End If 
    
            Return MyBase.VisitBinary(b)
        End Function 
    End Class
    
    public class AndAlsoModifier : ExpressionVisitor
    {
        public Expression Modify(Expression expression)
        {
            return Visit(expression);
        }
    
        protected override Expression VisitBinary(BinaryExpression b)
        {
            if (b.NodeType == ExpressionType.AndAlso)
            {
                Expression left = this.Visit(b.Left);
                Expression right = this.Visit(b.Right);
    
                // Make this binary expression an OrElse operation instead of an AndAlso operation. 
                return Expression.MakeBinary(ExpressionType.OrElse, left, right, b.IsLiftedToNull, b.Method);
            }
    
            return base.VisitBinary(b);
        }
    }
    

    Add a using directive (or Imports statement in Visual Basic) to the file for the System.Linq.Expressions namespace.

  5. Add code to the Main method in the Program.cs (Module1.vb in Visual Basic) file to create an expression tree and pass it to the method that will modify it.

    The following code creates an expression that contains a conditional AND operation. It then creates an instance of the AndAlsoModifier class and passes the expression to the Modify method of this class. Both the original and the modified expression trees are outputted to show the change.

    Dim expr As Expression(Of Func(Of String, Boolean)) = _
        Function(name) name.Length > 10 AndAlso name.StartsWith("G")
    
    Console.WriteLine(expr)
    
    Dim modifier As New AndAlsoModifier()
    Dim modifiedExpr = modifier.Modify(CType(expr, Expression))
    
    Console.WriteLine(modifiedExpr)
    
    ' This code produces the following output: 
    ' name => ((name.Length > 10) && name.StartsWith("G")) 
    ' name => ((name.Length > 10) || name.StartsWith("G"))
    
    Expression<Func<string, bool>> expr = name => name.Length > 10 && name.StartsWith("G");
    Console.WriteLine(expr);
    
    AndAlsoModifier treeModifier = new AndAlsoModifier();
    Expression modifiedExpr = treeModifier.Modify((Expression) expr);
    
    Console.WriteLine(modifiedExpr);
    
    /*  This code produces the following output:
    
        name => ((name.Length > 10) && name.StartsWith("G"))
        name => ((name.Length > 10) || name.StartsWith("G"))
    */
    

    Add a using directive (or Imports statement in Visual Basic) to the file for the System.Linq.Expressions namespace.

  6. Compile and run the application.

See Also

Tasks

How to: Execute Expression Trees

Walkthrough: Creating an IQueryable LINQ Provider

How to: Implement an Expression Tree Visitor

Concepts

Expression Trees