Monty’s Gush

A universal PredicateBuilder

Posted by: Pete Montgomery on: February 10, 2011

I don’t use the famous Albahari PredicateBuilder because it doesn’t work so well with Entity Framework or the latest version of NHibernate.

This is because it relies on InvocationExpressions, which aren’t supported by many query translators, and the workaround requires an unnatural call to TomasAsExpandable method in all your queries.

A couple of years ago, Colin Meek on the EF team showed me how to compose predicates without using InvocationExpressions and since then I’ve used my own synthesized version of PredicateBuilder, with the same API as the awesome Albahari original.

The same API also allows a lovely point-free style of composition. Say you have a list of predicates you want to “or” together, use the standard Aggregate function like so:

var predicate = predicates.Aggregate(PredicateBuilder.Or);

If you need a PredicateBuilder that works as you’d expect with Entity Framework, NHibernate… as well as Linq to SQL, here it is. (It ought to work in Silverlight too. Let me know…!)

    /// <summary>
    /// Enables the efficient, dynamic composition of query predicates.
    /// </summary>
    public static class PredicateBuilder
    {
        /// <summary>
        /// Creates a predicate that evaluates to true.
        /// </summary>
        public static Expression<Func<T, bool>> True<T>() { return param => true; }

        /// <summary>
        /// Creates a predicate that evaluates to false.
        /// </summary>
        public static Expression<Func<T, bool>> False<T>() { return param => false; }

        /// <summary>
        /// Creates a predicate expression from the specified lambda expression.
        /// </summary>
        public static Expression<Func<T, bool>> Create<T>(Expression<Func<T, bool>> predicate) { return predicate; }

        /// <summary>
        /// Combines the first predicate with the second using the logical "and".
        /// </summary>
        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
        {
            return first.Compose(second, Expression.AndAlso);
        }

        /// <summary>
        /// Combines the first predicate with the second using the logical "or".
        /// </summary>
        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
        {
            return first.Compose(second, Expression.OrElse);
        }

        /// <summary>
        /// Negates the predicate.
        /// </summary>
        public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> expression)
        {
            var negated = Expression.Not(expression.Body);
            return Expression.Lambda<Func<T, bool>>(negated, expression.Parameters);
        }

        /// <summary>
        /// Combines the first expression with the second using the specified merge function.
        /// </summary>
        static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
        {
            // zip parameters (map from parameters of second to parameters of first)
            var map = first.Parameters
                .Select((f, i) => new { f, s = second.Parameters[i] })
                .ToDictionary(p => p.s, p => p.f);

            // replace parameters in the second lambda expression with the parameters in the first
            var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);

            // create a merged lambda expression with parameters from the first expression
            return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
        }

        class ParameterRebinder : ExpressionVisitor
        {
            readonly Dictionary<ParameterExpression, ParameterExpression> map;

            ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
            {
                this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
            }

            public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
            {
                return new ParameterRebinder(map).Visit(exp);
            }

            protected override Expression VisitParameter(ParameterExpression p)
            {
                ParameterExpression replacement;

                if (map.TryGetValue(p, out replacement))
                {
                    p = replacement;
                }

                return base.VisitParameter(p);
            }
        }
    }

You’ll also need the ExpressionVisitor if you’re not on .NET 4.

15 Responses to "A universal PredicateBuilder"

Please, a pratical sample.

Something like a method for return a list of “QualquerCoisa” (entity set QuaisquerCoisas) with manually paramaters passing by Dictionary.

Thanks.

I’ve been using Albahari’s PredicateBuilder for some time, and was disappointed when it broke with Entity Framework. Thanks for this universal version. Good stuff.

This is absolutely amazing!! You’ve saved me A LOT of time…and head-pounding!! Thank you very much!

How easy would it be to add NOT as a method?

Hi Marco

public static Expression<Func<T, bool>> Not(this Expression<Func<T, bool>> expression)
{
var negated = Expression.Not(expression.Body);
return Expression.Lambda<Func<T, bool>>(negated, expression.Parameters);
}

Good idea – I’ve added it to the source code!
Pete

good stuff! I’m running into an issue where i assign a predicate value based on a method call, but I receive a not supported exception from EF. Is there a way around this through the helper? If not, I can just capture it in a closure…no biggie!

Works in SL4 for me with Entity Framework stuff.

[...] where I am constructing a dynamic LINQ query using PredicateBuilder (aside: check out this link for the best EF PredicateBuilder implementation). The problem is that this query takes a long time [...]

[...] LINQ query using PredicateBuilder based on user-specified filter criteria (aside: check out this link for the best EF PredicateBuilder implementation). The problem is that this query usually takes a [...]

Thanks alot. Its a silver bullet for our problem.

Does anyone have example code that works in Silverlight 4 to demostrate how to use this code for Oring expressions?

I was finally able to get a working prototype in Silverlight 4 with WCF RIA services. I am planning on creating a small test project to illustrate this functionality. Let me know if you need some code examples… I know that a small working example would have saved me many hours of trial and error.

There are several links in the article – the first is to a detailed page on the PredicateBuilder with examples of how to use it written by its authors Joseph and Ben Albahari. They also wrote an excellent accompanying book on C# which I can really recommend.

Pete.

Pete,

As a follow-up to your last email, I wanted to let you know that your PredicateBuilder works great with LINQPad as a dll. I tweaked a couple of methods for my own applcation, but otherwise the three code blocks are identical as downloaded from referenced sites.

The following is short description of what I did to link your code into LINQPad.

1. Added dll namespace PB by pressing F4 in LINQPad and selecting Additional Namespace Imports tab. The dll was added similarly, but I selected the Additional References and browsed to the source.

2. The added PB.dll contains a tweaked version of public static class PredicateBuilder. It also contains public class ParameterRebinder : ExpressionVisitor and public abstract class ExpressionVisitor.

3. I wrote two lambda expressions for the Items table ItemTitle field, then Ored the two expressions. The Ored expression was used to query my MySQL database.

4. The localQuery.Dump() results contained the correctly identified rows (10) from the table.

Expression<Func> result1 = r1 => r1.ItemTitle.Contains(“oil”);
Expression<Func> result2 = r2 => r2.ItemTitle.Contains(“painting”);
Expression<Func> result;
result = PB.PredicateBuilder.Or(result1, result2);
IQueryable localQuery = from i in Items.Where(result) select i;
localQuery.Dump();

Working with EF 4.1 Update 1.
Great work Pete!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.