Querying in memory objects using our Criteria API

In my The Evolution of a Query API and Our Query API Explained posts I detailed some of the work that we’ve been doing with our query api.  We moved from a loosely typed query that had hard coded string literals to something more strongly typed and LINQ-like.

before:
repository.FindAll(“Name = ‘Steve’ AND Age > 21”);

after:
repository.FindAll(Customer.Columns.Name == “Steve” && Customer.Columns.Age > 21);

We’ve also begun work on a CriteriaFormatter class that handles all the formatting of the criteria object into SQL.  The basic idea behind our CriteriaFormatter is that a custom ICriteriaFormatter could be written for whatever your O/R Mapper uses for querying objects: HQL, OPath, NPath.

In addition to being able to query our database using our criteria objects I also thought it would be nice to have support for querying in memory objects within a generic List<T>.  The generic List class in .NET 2.0 has several very nice methods available on it that allow you to perform operations on the items in the list.  The two methods that I was most interested in was .Find(Predicate<T> predicate) and .FindAll(Predicate<T> predicate).  Find returns the first item in the list matching your predicate and FindAll returns a list with all the items in the list that match the given predicate.

The question became..how do we turn our Criteria objects into Predicates?  After a little bit of research I started experimenting with Lightweight code generation and implicit operator overloads.  Our PropertyCriteria objects, which is what results from the Customer.Columns.Name == “Steve” code, was a pretty good place to start. 

In order for our criteria objects to turn into predicates I needed to figure what IL was emitted when the following operators were used: ==, >, >=, <, <=, != within C#.  With the help of Reflector I was able to figure out how each operator is emitted in IL.  With that knowledge in hand I was able to write an AsPredicate method on our criteria objects that used lightweight code generation to dynamically construct a Predicate<T> from our criteria object.  The only other things that was needed was an implicit operator overload to turn our Criteria<T> into a Predicate<T>.

public static implicit operator Predicate<T>(Criteria<T> criteria) {
   return criteria.AsPredicate();
}

As a result we can now do:

List<Customer> customers = List<Customer>();
// load up with customers

List<Customer> matches = customers.FindAll(Customer.Columns.Name == "Steve");

matches = customers.FindAll(Customer.Columns.Name == "Steve" && Customer.Columns.Age > 27);

matches = customers.FindAll(Customer.Columns.Name.StartsWith("S") | Customer.Columns.LastName.StartsWith("E"));

And I can of course use the same criterias against our repositories that hit our database:

List<Customer> matches = customerRepository.FindAll(Customer.Columns.Name == "Steve");

matches = customerRepository.FindAll(Customer.Columns.Name == "Steve" && Customer.Columns.Age > 27);

matches = customerRepository.FindAll(Customer.Columns.Name.StartsWith("S") | Customer.Columns.LastName.StartsWith("E"));

Note: As you may have noticed I still haven’t figured out how to get a || to work so I’m stuck with a single pipe for “or”.  Anyone have any ideas?

Post a Comment

 
 
Prove you're not a spammer: 
8 + 8 =