Dynamically composing predicates
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); workItems.Where(predicate.Compile());
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); workItems.Where(condition.Compile());
This is a great post, I think I saw work very similar to that with http://abdullin.com/journal/2009/1/3/strongly-typed-reflection-in-lokad-shared.html
Your weblog is very useful. Thank you so significantly for offering a lot of helpful content. I’ve bookmarked your weblog internet site and are going to be without doubt coming back. When once more, I enjoy all your work and also supplying a great deal crucial tricks for the readers.