Click here to Skip to main content
Licence CPOL
First Posted 18 Aug 2006
Views 30,358
Downloads 231
Bookmarked 24 times

Create Predicates from Expressions for Use with Generic Collections

By | 18 Aug 2006 | Article
Create Predicates from Expressions for use with Generic collections

Introduction

I'm really a SQL nut. I love working in terms of sets and try to avoid looping where possible in favor of set based operations. One thing that caught my eye is the predicates and actions that are now in the 2.0 Framework. They are part of generics.

Check out the MSDN Magazine article for more details on actions and predicates.

Searching a Collection

I often find myself wanting to pull out certain things from a collection based on criteria. Somewhat like a SQL select. Understand though that this is still a sequential search no matter if you do it by hand or using the predicate.

Sample of Searching by Hand vs Using Predicates

 //select out the Employee's whose last name is Brown:
 //By Hand
 List<Employee><employee /> matchesLastNameByHand = new List<Employee><employee />();
 foreach(Employee e in employeeList)
 {
     if (e.LastName == "Brown")
            matchesLastNameByHand.Add(e);
 }
           
 //Use a predicate
 List<employee /> matchesLastName = 
    employeeList.FindAll(PredicateBuilder<Employee>.Build<employee />("LastName = Brown"));

I believe the predicate is quite a bit more elegant. Based on doing a set of runs on various collections and criteria, I have found a slight performance improvement when searching with a predicate vs by hand. So you get more elegant code and a little better performance.

The PredicateBuilder Class and Expressions

You can easily code predicates by hand based on what you need. Just remember their signature returns bool and takes a single argument of the type stored in the collection you will use them on.

However, generating them dynamically seemed interesting to me as it would allow me to have a very simple SQL-like way to pull matches out of a collection.

To accomplish this, I needed to dynamically generate a predicate from an expression. For simplicity, I greatly simplified the expression grammar to only allow 3 tokens to be passed in: property operator value, i.e.

LastName = Smith

or

Salary >= 50000

There are several shortcomings here, especially with say string properties with a space in them. Future modifications might support such scenarios.

Examples of Finding in a Collection using PredicateBuilder

List<Employee> matches50K = 
    employeeList.FindAll(PredicateBuilder.Build<Employee>("Salary >= 50000"));

List<Employee> matchesLastName = 
    employeeList.FindAll(PredicateBuilder.Build<Employee>("LastName = Brown"));

Building the Predicate with CSharpCodeProvider

The technique for turning the expression into a runnable predicate is accomplished using the CSharpCodeProvider class.

Essentially we build a class that exposes a static GetPredicate method. After generating the assembly, we use reflection to invoke that method. We cast the invoke's result appropriately and return that to the caller.

Take a look at the PredicateBuilder class in the attached code for more.

Demo Application

The demo app builds up a generic list and then runs a couple of expressions on it to pull out matches.

static void Main(string[] args)
{
    //add some employees:
    List<Employee> employeeList = new List<Employee>();
    employeeList.Add(new Employee("John", "Doe", 55000));
    employeeList.Add(new Employee("Larry", "Jones", 65000));
    employeeList.Add(new Employee("Wayne", "Smith", 25000));
    employeeList.Add(new Employee("Sally", "Johnson", 35000));
    employeeList.Add(new Employee("Maggie", "Brown", 60000));
    employeeList.Add(new Employee("David", "Brown", 80000));
    Console.WriteLine("All Employees: \n");
    //output them in a manual loop:
    foreach (Employee e in employeeList)
        Console.WriteLine(e.ToString());
    //OR 
    //output them with an action and the ForEach method:
    employeeList.ForEach(new Action<Employee>(Employee.Print));
    Console.WriteLine("\n\n\n");
    //select out the Employee's whose last name is Brown:
    //By Hand
    List<Employee> matchesLastNameByHand = new List<Employee>();
    foreach(Employee e in employeeList)
    {
        if (e.LastName == "Brown")
            matchesLastNameByHand.Add(e);
    }
    //Use a predicate
    List<Employee> matchesLastName = 
        employeeList.FindAll(PredicateBuilder.Build<Employee>("LastName = Brown"));
    //output the matches
    Console.WriteLine("The Brown's:\n");
    matchesLastName.ForEach(new Action<Employee>(Employee.Print));
    Console.WriteLine("\n\n\n");
    //everybody making $50000 or more
    List<Employee> matches50K = 
        employeeList.FindAll(PredicateBuilder.Build<Employee>("Salary >= 50000"));
    //output the matches
    Console.WriteLine("People making 50k or more:\n");
    matches50K.ForEach(new Action<Employee>(Employee.Print));
    Console.WriteLine("\n\n\n");
}

Output of Demo

Summary

The attached solution contains both the predicate builder class as well as a simple demo application. I believe that using expressions to simulate set based operations on collections is an interesting concept and can certainly lead to more elegant and performant code.

To use the PredicateBuilder, reference the Expressions assembly and then use it to dynamically build your predicates.

History

  • 18th August, 2006: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Tim Kohler

Chief Technology Officer

United States United States

Member

Working to keep a technology company up to date. Wondering when Microsoft will hire a fresh, innovative guy to run the company.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
QuestionHow to pass multiple tokens Pinmemberpramodgupta241:06 28 May '08  
QuestionAny word on the dynamic methods? PinmemberLance May3:30 23 Jul '07  
GeneralStrings can be fetch from database or fiels PinmemberZAky2:15 24 Aug '06  
GeneralRe: Strings can be fetch from database or fiels PinmemberTim Kohler4:07 24 Aug '06  
GeneralI think you missed something PinmemberTestosteles17:02 21 Aug '06  
GeneralRe: I think you missed something PinmemberTim Kohler2:46 22 Aug '06  
GeneralRe: I think you missed something PinmemberTestosteles6:47 22 Aug '06  
GeneralDynamic Methods Pinmemberoverglo10:48 18 Aug '06  
GeneralRe: Dynamic Methods PinmemberTim Kohler16:33 18 Aug '06  
Generalassemblies... PinmemberChristian Klauser9:31 18 Aug '06  
GeneralRe: assemblies... PinmemberTim Kohler9:54 18 Aug '06  
GeneralRe: assemblies... PinmemberChristian Klauser11:37 18 Aug '06  
GeneralPerformance... PinmemberMarc Leger8:56 18 Aug '06  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web01 | 2.5.120517.1 | Last Updated 18 Aug 2006
Article Copyright 2006 by Tim Kohler
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid