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 }
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.
@Haacked,
Anonymous types are only good for method boundaries.
So this is basically useless in those scenarios.
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 ?
@#3, perhaps a future extension to nhibernate hql would allow anonymous types in the query result/structure
Comments on this entry are closed.