Refactoring: Using object constructors in HQL with NHibernate

by javery on October 22, 2006

We are using NHibernate on a big project at work and one of the optimizations we often find ourselves making is creating a “lighter” object and returning that instead of our full objects for searches and other operations where we return a large number of the same object. This also applies to searches that display fields from a hand-full of objects, for instance you might show a Customer’s first name, last name,  their primary address line 1, address zip code, and the date of their last order. You wouldn’t want to return all of these objects and display just the pertinent information, you want to just get the information you are going to show. Especially on search screens that are heavily used throughout the system.

The other day I noticed that in many of our data access calls we were doing something like this: (mocked up code)

    1 public List<CustomerResult> GetCustomerSearchResults(CustomerCriteria cc)

    2 {

    3     using (Repository repository = new Repository())

    4     {

    5         StringBuilder hql = new StringBuilder();

    6         hql.Append(“SELECT c.Name, c.Phone,

                  c.PrimaryAddress.Line1, c.LastOrderDate “);

    7         hql.Append(“FROM Customer AS c”);

    8 

    9         IList result = repository.Session.CreateQuery(hql.ToString())

   10             .List();

   11 

   12         List<CustomerResult> results = new List<CustomerResult>();

   13         foreach (object[] row in tempList)

   14         {

   15             CustomerResult result = new CustomerResult();

   16             result.Name = row[0].ToString();

   17             result.Phone = row[1].ToString();

   18             result.Line1 = row[2].ToString();

   19             result.LastOrderDate = row[3].ToString();

   20             results.Add(result);

   21         }

   22     }

   23 

   24     return results;

   25 }

Technically there isn’t anything wrong with this code (I have omitted null checks, etc for brevity) but it could be much more elegant. A little known feature of HQL is the ability to construct and return a new object as part of the statement, the above HQL becomes “Select new CustomerResult(c.Name, c.Phone, c.PrimaryAddress.Line1, c.LastOrderDate)”. The only requirements for this are:

1) The CustomerResult object needs to have a constructor that matches what is contained in your HQL.

2) If the class you are creating is un-mapped (like our CustomerResult object would be) then you need to Import the class in one of your mapping files. I created a ImportClasses.hbm.xml file for all of these imports:

    1 <?xml version=1.0 encoding=utf-8 ?>

    2 <hibernate-mapping xmlns=urn:nhibernate-mapping-2.0>

    3   <import class=CustomerResult, MyAssembly/>

    4 </hibernate-mapping>

My above code can now be refactored to the following:

    1 public List<CustomerResult> GetCustomerSearchResults(CustomerCriteria cc)

    2 {

    3     using (Repository repository = new Repository())

    4     {

    5         StringBuilder hql = new StringBuilder();

    6         hql.Append(“SELECT new CustomerResult(c.Name, c.Phone,

                 c.PrimaryAddress.Line1, c.LastOrderDate) “);

    7         hql.Append(“FROM Customer AS c”);

    8 

    9         IList result = repository.Session.CreateQuery(hql.ToString())

   10             .List();

   11 

   12         return ConvertToGenericList(result);

   13     }

   14 }

Sure some of the code has just been moved to the constructor, but I find it much more readable and maintainable.

-James

{ 4 comments }

Haacked October 22, 2006 at 5:49 am

This is where anonymous types will come in handy with C# 3.0, not having to explicitely create a class for CustomerResult, instead duck typing it.

Ayende Rahien October 22, 2006 at 6:44 am

@Haacked,
Anonymous types are only good for method boundaries.
So this is basically useless in those scenarios.

Frederik Gheysels September 20, 2008 at 1:04 pm

A very late comment, I know, so i’m aware of the fact that it will maybe never be read :)

Haacked: I don’t think you could use an anonymous type in this situation, since, for this to get working, you’ll have to ‘import’ the anonymous type so that NHibernate is aware of it.

But how are you going to import it, if you do not know the name of the type ?

even later March 26, 2009 at 11:55 pm

@#3, perhaps a future extension to nhibernate hql would allow anonymous types in the query result/structure

Comments on this entry are closed.

Previous post: Screwturn Wiki

Next post: Splash Screens in Windows Forms