Thursday 3 April 2008

nHibernate - Filter and Criteria for applying dynamic where clause

Over the last few days I've been getting my hands dirty trying to get dynamic filters working with nHibernate. As a future reference I've decided to post my notes.

The final solution makes use of Filters and SpringFramework, IMHO both tools that you cannot do without when working in Java or .Net.

Requirement
The system has complex WebServices exposed which provide data intensive processing based upon parameters provided in complex XML. The .Net framework deals with serialization of the XML into objects, the requirement is to apply filter conditions only when a parameter is supplied in the XML. It is essential that maximum reuse of code is maintained.

Options



  1. Write a data access object with lots of if statements. The code would need to generate a string that forms the where clause of the select statement.

  2. Use nHibernate ICriteria or IFilter objects to apply the conditions to the SQL.

Option 2 was the method of choice with little thought for option 1. It was felt that option 2 would provide a more modular approach. Now to the point of this blog entry, there are subtle differences between ICriteria and IFilter, and I would recommend using IFilter which I feel is much more powerful mechanism for controlling the data returned in the object model.

ICriteria
produced cartesian joins so did not truly reflect the object model. For example if you have a Order object that can contain many OrderLines you expect the ORM to produce one Order object that contains many OrderLines. Using Criteria in nHibernate produced many Order's (1 per OrderLine) and each Order contained the correct number of OrderLines. The more Criteria applied to the query the bigger the result set became. Also, objects returned using Criteria that contain Bags, Sets or Maps had the collection object populated without applying any filtering.

It was at this point that I decided to focus my attention on the Filter functionality provided by nHibernate and it worked perfectly.

IFilter requires a little more effort in terms of code and producing the hibernate mapping documents but it is well worth the effort. The filter ensures that only the required objects are returned and that Bags, Maps, and Sets are correctly populated with filtered objects. A filter is applied to the session and the same filter defition can be used on many objects. Individual filters can be enabled or disabled as necessary.

Solution
The solution was to implement each filter condition in a object that determined if the filter should be applied. The filter class retrieved the parameter value originally passed in the XML and applies the filter condition, nHibernate dealt with correct SQL syntax for the where clause. Each possible filter condition is put into its own class and the chained together in a IList object which is iterated over by a control class. The configuration of control class and filter classes is done in SpringFramework so as to provide maximum flexibility.



SpringFramework provides a flexible means to add or remove filters from a control class.

foreach (AbstractParamFilter paramFilter in commonFilterList)
{
paramFilter.ApplyFilter(accountingParams, session);
}

The filter classes implement either a Interface or Abstract class thus providing the polymorphism.

Each implemented filter class is implemented to decide if the filter should be enabled or disabled based upon the content of the object passed to the filter.

The NHibernate configuration needs to have a filter definition provided for properties or collection objects as shown below.

Finally a filter defintion and the parameter for the filter need to be defined in the hibernate mapping files.
NHibernate chapter 14 provides the documented detail of what to do, I personally do not believe the chapter describes the full benefits.

No comments: