Lambda Expressions with NHibernate.Linq

Tags: Knockout, pubsub, observer, MVC, jQuery, Ajax, EF, Validation, FluentValidation, Visual Studio 2010, ASP.NET, JSON, FullCalendar, Silverlight, Architecture, Vista, IIS, Generics, NHibernate, WCF, RIA Services, Visual Studio 2008, SQL, STORM!, Nullable, ChannelFactory, netTCPBinding, VSPAT, responsive, design, HTML5, CSS3, MVC WebAPI, MVC 4, WebAPI, JQuery Mobile, ScheduleWidget, recurring events, Ninject, Pluggable, CQRS DDD, Windows

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;
So I'm going to extend my DataService class below to support LINQ queries along these lines. Kudos to Ryan Lanciaux who blogged on this before me. To support Linq queries we first must modify the IDataService interface to support lamdba expressions:
   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();

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?

Add a Comment