Waseem Sabjee

my tech and dev blog

LINQ Expression – how to append to an expression at a later stage

July 23rd, 2013

The cool thing with LINQ and Entity framework is that if you pass an expression through to you context, it creates an SQL Query based of that expression to fetch the data you need, instead of fetching loads of data down and having to sift through them in C#.

A recurring issue that plagued me was that if I needed to pass through a slightly different LINQ expressions based on certain conditions it looked like I was repeating code, and I don’t like repeating code.

after a few hours of playing around with different techniques I devised a little way to append to an expression.

first we need to make use of something known as an expression visitor. so what is an expression visitor ?
well, it’s cool object that allows you to visit expression trees and rewrite expressions/
for more information on expression visitors please go here

alright to the code!
here is an implementation of the Expression Visitor implementation

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Text;
    using System.Threading.Tasks;

    /// <summary>
    /// The Expression re-binder
    /// </summary>
    public class ExpressionRebinder : ExpressionVisitor
    {
        /// <summary>
        /// The map
        /// </summary>
        private readonly Dictionary<ParameterExpression, ParameterExpression> map;

        /// <summary>
        /// Initializes a new instance of the <see cref="ExpressionRebinder"/> class.
        /// </summary>
        /// <param name="map">The map.</param>
        public ExpressionRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
        {
            this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
        }

        /// <summary>
        /// Replacements the expression.
        /// </summary>
        /// <param name="map">The map.</param>
        /// <param name="exp">The exp.</param>
        /// <returns>Returns replaced expression</returns>
        public static Expression ReplacementExpression(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
        {
            return new ExpressionRebinder(map).Visit(exp);
        }

        /// <summary>
        /// Visits the <see cref="T:System.Linq.Expressions.ParameterExpression" />.
        /// </summary>
        /// <param name="node">The expression to visit.</param>
        /// <returns>
        /// The modified expression, if it or any subexpression was modified; otherwise, returns the original expression.
        /// </returns>
        protected override Expression VisitParameter(ParameterExpression node)
        {
            ParameterExpression replacement;
            if (this.map.TryGetValue(node, out replacement))
            {
                node = replacement;
            }

            return base.VisitParameter(node);
        }
    }

the concept is very simple, we have two LINQ Expressions and we’d like to combine them. The Expression visitor is a middle piece that makes it really easy for us to do this, however so far we’ve only won half the battle – we have only set up a sort of mediator to help us achieve expression concatenation. now lets put this mediator to good use.

The following extension methods allow us to apply an AND to the two linq expressions, I’ve thrown in a little extra just to show what else you can achieve, I’ve also given you an OR implementation.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Text;
    using System.Threading.Tasks;
    using Jonsson.WMS.Core.Helpers;

    /// <summary>
    /// Lambda expression extensions
    /// </summary>
    public static class LambdaExtensions
    {
        /// <summary>
        /// Composes the specified left expression.
        /// </summary>
        /// <typeparam name="T">Param Type</typeparam>
        /// <param name="leftExpression">The left expression.</param>
        /// <param name="rightExpression">The right expression.</param>
        /// <param name="merge">The merge.</param>
        /// <returns>Returns the expression</returns>
        public static Expression<T> Compose<T>(this Expression<T> leftExpression, Expression<T> rightExpression, Func<Expression, Expression, Expression> merge)
        {
            var map = leftExpression.Parameters.Select((left, i) => new
            {
                left,
                right = rightExpression.Parameters[i]
            }).ToDictionary(p => p.right, p => p.left);

            var rightBody = ExpressionRebinder.ReplacementExpression(map, rightExpression.Body);

            return Expression.Lambda<T>(merge(leftExpression.Body, rightBody), leftExpression.Parameters);
        }

        /// <summary>
        /// Performs an "AND" operation
        /// </summary>
        /// <typeparam name="T">Param Type</typeparam>
        /// <param name="left">The left.</param>
        /// <param name="right">The right.</param>
        /// <returns>Returns the expression</returns>
        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
        {
            return left.Compose(right, Expression.And);
        }

        /// <summary>
        /// Performs an "OR" operation
        /// </summary>
        /// <typeparam name="T">Param Type</typeparam>
        /// <param name="left">The left.</param>
        /// <param name="right">The right.</param>
        /// <returns>Returns the expression</returns>
        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
        {
            return left.Compose(right, Expression.Or);
        }
    }

So what’s going here ?
all the magic happens in the compose method, it basically takes the parameters from the left expression and the right expression, places them into what we can call a parameter dictionary or a map and attempts to join them based on the type of merge supplied.

the other 2 static methods make it really simple for you to do this in fluid while working with LINQ expressions I will demonstrate.

            Expression<Func<Ticket, bool>> predicate = x => !x.IsDeleted;
            if (isSearch)
            {
                predicate = predicate.And(x => x.SearchString.Contains(searchParam));
            }

Does that not look elegant ?

Comments

33 Comments

RSS

Leave a Reply

%d bloggers like this: