Of all these, nHibernate 3.0 is definitely one of them. No doubt something better is on the horizon and when it looms into view I might take a look. Until then, this is working well for me.
In my humble opinion, NH3 is way better than Linq-to-Fail or infact any other model-based ORF (Object-Relational Failfactory).
- The fluent style works well; it's nice to use
- It's codegen-able like a crazy thing
- It's flexible and very extensible
- It performs - the generated SQL is lean and exactly what you need, it's measurably faster than L2S or Lightspeed
- It isn't Linq-to-SQL
- or a cabbage
That said, I'd like a nicer way of doing multi-field searches; maybe by using an Expression<ICriteria> as a parameter to my search method ?
Here is my search method, abbreviated so as not to cause boredom: -
public IListCustomerSearch(CustomerSearchCriteria criteria, int pageIndex, int pageSize) { IList list = null; using (var session = SessionFactory.OpenSession()) { var query = session.QueryOver (); if (!string.IsNullOrEmpty(criteria.CustomerName)) { MatchMode matchMode; if (criteria.CustomerSearch == CustomerSearchEnum.StartingWith) { matchMode = MatchMode.Start; } else if .. // etc query = query.WhereRestrictionOn(x => x.CustomerName.ToLower()).IsLike(criteria.CustomerName.ToLower(), matchMode); } if (criteria.ActiveOnly) { query = query.And(x => x.Active == true); } if (!string.IsNullOrEmpty(criteria.Address1)) { query = query.And(x => x.Address1.ToLower().StartsWith(criteria.Address1.ToLower())); } // etc
I'm working on a nicer way of doing this, so stay tuned. That said, the above is fairly readable; I'm just appending additional sections to the WHERE clause where needed.
Hmm.. Anyway, one thing NH3 gives us is database-level paging using the SQL Server ROW_NUMBER function, which is efficient (much more efficient than selecting the whole set into memory and doing the paging there..).
list = query.Skip(pageIndex * pageSize) .Take(pageSize) .OrderBy(x => x.CustomerName).Asc() .List();
This is nice, except it *only* returns the rows in the page. How do I know how many rows there are in total, so I can set up my pager accordingly ?
This is easy when done at stored procedure level, as I can just run an additional Count(*) SELECT and return an OUT parameter, all in one hit to the database. Am I going to need two hits here ?
As it turns out, the CriteriaTransformer has a "TransformToRowCount" method, which does what we need. This does issue one more SQL statement for the count, and it's submitted separately - so two round trips, not ideal.
list = query.Skip(pageIndex * pageSize) .Take(pageSize) .OrderBy(x => x.CustomerName).Asc() .List(); int count = CriteriaTransformer.TransformToRowCount(query.UnderlyingCriteria) .UniqueResult();
And after reading this, the process has been simplified even further with the addition of an extension method. So the previous example can be re-written as just ..
list = query.Skip(pageIndex * pageSize) .Take(pageSize) .OrderBy(x => x.CustomerName).Asc() .List(); int count = query.RowCount();
Nice ! But still two database hits. There are two ways to fix this: -
- Use a transaction
- Use a projection
Toodles.
thanks for article, looking for example of how to use projection to eliminate second call to DB. know of good example on net.
ReplyDelete