Archive for July, 2011

Dynamically composing predicates

July 12, 2011 2 comments

Typically when I’m using LINQ to filter collections, my conditions aren’t very complex and usually contain a single boolean condition. For example, if I wanted to filter a collection of WorkItems, I’ll use the Where extension method:

workItems.Where(x => x.Status == Status.Done);

I’ve come across a scenario where I needed to chain multiple “OR” expressions. It’s easy to chain multiple “OR” expressions during compile time:

workItems.Where(x => x.Status == Status.Done || x.Status == Status.Processing);

However, this is difficult to do if the conditions are determined at runtime. In an application, I’m asking the user to select from a list of statuses. They may select one or more statuses.

Since I still work a lot with stored procedures, I’ll typically use dynamic SQL to generate a WHERE clause if these status were stored in a database. But with a collection, this isn’t easy to do without some help.

After looking around, I stumbled across the PredicateBuilder class that ships as part of LINQKit.

The PredicateBuilder allows me to easily chain multiple “AND” and “OR” conditions together at runtime. I can append an “OR” condition to the predicate if the respective checkbox is checked:

var predicate = PredicateBuilder.False<WorkItem>();

if (cbIdle.Checked)
    predicate = predicate.Or(x => x.Status == Status.Idle);

if (cbProcessing.Checked)
    predicate = predicate.Or(x => x.Status == Status.Processing);

if (cbDone.Checked)
    predicate = predicate.Or(x => x.Status == Status.Done);


The PredicateBuilder also allows me to nest predicates and create complex conditions. For example, if I wanted to create a predicate that is equivalent to the following:

workItems.Where(x => 
    x.Status == Status.Done &&
    (x.Description.Contains("report") || x.Description.Contains("summary"))

… I can create two predicates and append them together.

var description = PredicateBuilder.False<WorkItem>();
description = description.Or(x => x.Description.Contains("report"));
description = description.Or(x => x.Description.Contains("summary"));

var condition = PredicateBuilder.True<WorkItem>();
condition = condition.And(x => x.Status == Status.Done);
condition = condition.And(description);