Thursday, June 18, 2009

Compounded filter predicates using Delegates

image






















The ListCollectionView of WPF replaces the DataView of Winforms as the primary object for filtering cached data. Unlike the DataView which encapsulates only DataTable, ListCollectionView encapsulates IList; making it a very flexible solution to your filtering needs. Another big change is the switch from string to delegate for the predicate. A predicate is a Boolean statement used to test if the record qualifies for the filter. In SQL, this is the condition you put in the WHERE section. At first you may think that this is an overkill for a simple task such as filtering but as you will see in this post, delegate improves the maintainability of the codes because it allows easy modularization.

In our simple application, we display the list of all Contacts from the AdventureWorks database using Entity Framework. The user should be able to filter the list using any combination of FirstName, LastName and MiddleName. Three delegates are needed to handle the predicate for each field. Everytime the user types in the text boxes of the search field, the filter is triggered. The predicate set to the ListCollectionView.Filter property is the compounded (AND’ed) result of the 3 delegates. You will see later on that this is not always the case. A result of true means the record is a match.

Before we filter, we fetch the data first:

image

We leverage on generics for a delegate that can handle any record.

image

To make the filter much more efficient, we only apply a predicate when it’s required. For example, if the user did not type anything for the last name, we will not include the last name predicate to the final filter. The filter is now modular and in order to achieve this, we use a list to contain only the needed predicates.

image

Each predicate is initialized with a lambda that is a straightforward comparison between the record field and the content of the control. Note the middle name logic is slightly different because the record property is nullable. Failure to check that would have resulted to an exception.

image

On every TextChanged event of the text boxes, we determine if the predicate mapped to the text box is needed or not. After this, we apply the filter.

image

The SetFilter method determines the logic on when to add or remove the predicate from the list.

image

The ApplyFilter sets the final filter. Filtering can be very taxing. For efficiency, we check if the predicate list is not empty prior to triggering the filter. If the list is empty, we should still check if there’s really a need to filter again.

image

I could have used lambda for the final predicate but having it as a named method makes the logic prominent. I like it this way because it shows how integral the logic really is for the application. Notice that we simply iterate thru all the predicates in the list and compound the result.

image

Here’s our predicates in action.

image

This usage of delegate may be a novelty to you. What’s not novel though is the decision on when to use it. It operates on cached data so it presupposes that you fetch everything from the database. Usually this is not an ideal approach if you have high volume of data for a less manipulative user.

No comments:

Post a Comment