Lambda Expressions with NHibernate.Linq
In the last two posts below I talked about creating a templated DataService class and also mused about whether UI data queries really were a cross-cutting concern. I'm agnostic about the question really; however, I do have one practical concern. I don't want to expose methods that take any old HQL query that the client sends my way. I'd like to have a controlled way of extending database queries down to the UI.
The answer is LINQ. Thanks to Rahien and others NHibernate.Linq 1.0 RTM is available. (Eventually this will be merged into the NHibernate mainline but for now it's a separate download.) Once you reference the assembly and put a reference to NHibernate.Linq the extension methods are available in your ISession instance:
1: // grab an ISession from ISessionFactory
2: var query = from item in session.Linq()
3: select item;
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Linq.Expressions;
5: using System.Text;
6:
7: namespace ConcertMusic.DomainModel
8: {
9: public interface IDataService<T>
10: {
11: T Get(int id);
12: IList<T> Get(string hqlQueryString);
13: IList<T> Get();
14: void AddCriteria(Expression<Func<T, bool>> lambdaFunction);
15: T Save(T item);
16: bool Delete(T item);
17: }
18: }
Now to implement the interface in the DataService class:
1: public class DataService<T> : IDataService<T>
2: {
3: private IList<Expression<Func<T, bool>>> _criteria;
4:
5: public DataService()
6: {
7: _criteria = new List<Expression<Func<T, bool>>>();
8: }
9:
10: public void AddCriteria(Expression<Func<T, bool>> lambdaFunction)
11: {
12: _criteria.Add(lambdaFunction);
13: }
14:
15: public IList<T> Get()
16: {
17: using (ISession session = NHibernateHelper.OpenSession())
18: {
19: var query = from item in session.Linq<T>()
20: select item;
21:
22: foreach (var criterion in _criteria)
23: {
24: query = query.Where<T>(criterion);
25: }
26: return query.ToList();
27: }
28: }
29:
30: // other methods removed for clarity
31: }
32: }
That's pretty much it for the data access plumbing. Now the client code can fetch the IDataService instance and get back anything it wants using lambda expressions. Let's say I want to get every widget from the database with a name that starts with the letter "V". Here's my code:
1: IDataService<Widget> svc = DataServiceFactory<Widget>.Create();
2: svc.AddCriteria(item => item.Name.StartsWith("V"));
3: IList<Widget> list = svc.Get();
And these lambda expressions can be chained of course for very fine-grained search criteria. Suppose I want every widget with a name that starts with the letter "V", is round and blue, and has greater than 8 teeth:
1: IDataService<Widget> svc = DataServiceFactory<Widget>.Create();
2: svc.AddCriteria(item => item.Name.StartsWith("V"));
3: svc.AddCriteria(item => item.Color.Equals("Blue"));
4: svc.AddCriteria(item => item.Shape.Equals("Round"));
5: svc.AddCriteria(item => item.Teeth > 8));
6: IList<Widget> list = svc.Get();

My name is James Still and I'm a seasoned software developer living and working in Oregon USA. I'm an avid cyclist, backpacker, reader, stargazer, and I pick at the guitar from time to time.
1 Comment
OwenG said
Thank you for the example; I'm trying to accomplish the same thing in my own project. If I understand your code correctly, lines 19/20 return the entire result set from the datasource, which you then in turn filter using LINQ. Is there a way to get the lambda function passed to Nhibernate so that only the required records from the database are returned?