Creating NHibernate Linq Query from arbitrary Criteria

30. October 2009

I just added this gem to NHibernate.Linq (of the NHContrib variety – not to be confused with that other one).  I’ll let the test speak for itself:

[Test]
public void can_create_linq_query_from_arbitrary_criteria_query()
{
    var criteria = session.CreateCriteria<User>();
    criteria.Add(Restrictions.Le("RegisteredAt", new DateTime(2000, 1, 1)));

    var query = session.Linq<User>(criteria)
        .Where(u => u.Name == "nhibernate" || u.Name == "ayende");

    var list = query.ToList();
    Assert.AreEqual(1, list.Count);
    Assert.AreEqual("nhibernate", list.Single().Name);
}

My specific reason for adding this has to do with a custom ICriterion (more on that later) I needed to use in one of our criteria queries that I wanted to expose as an IQueryable.  This method of being able to build a linq query from an arbitrary criteria seemed to solve my problem rather elegantly.

Let me know if you think this is crazy or if you like.

As of now, it is available in the trunk r1099.

Currently rated 3.6 by 7 people

  • Currently 3.571429/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Programming ,

The Many Pitfalls of NHibernate.Linq

26. August 2009

Working with the recently released NHibernate.Linq is not without its (many) pitfalls.  In one of my current projects, we are using the specification pattern to build dynamic linq queries based off of persistable specification objects.  This has led to more than one hair-pulling session on the limitations of the current NH Linq provider.  For instance, did you know that this query will work just fine:

(from p in db.Patients 
from r in p.PatientRecords 
where r.Type.TypeCode == 7 
select r).Sum(r => r.Amount);

yet, this query (which is equivalent) will blow up in your face:

db.Patients.SelectMany(p => p.PatientRecords) 
    .Where(r => r.Type.TypeCode == 7).Sum(r => r.Amount);

The reason it fails is very implementation-specific.  Under the covers, the C# compiler will convert that first query into an expression looking something like this:

db.Patients.SelectMany(p => p.PatientRecords, (p, r) => new { p = p, r = r }) 
    .Where(pr => pr.r.Type.TypeCode == 7).Sum(pr => pr.r.Amount);

Our second query which uses the overload of SelectMany that doesn’t specify the result selector fails because NHibernate.Linq specifically requires that result selector in order to know which alias to use for that subcriteria for the rest of the query (yes, this is a really bad implementation and requires that you use the same lambda parameter names throughout your query).

Simple enough, let’s just drive around the pothole and explicitly state our parameter name by changing our second query to look like this:

db.Patients.SelectMany(p => p.PatientRecords, (p, r) => r) 
    .Where(r => r.Type.TypeCode == 7).Sum(r => r.Amount);

Ahh, explosions still abound.  That query will generate an error similar to: “Type is not an association” or “Could not resolve property: Type”.

It turns out, that NHibernate.Linq’s SelectMany support depends on that anonymous type leading the entity types in the query (pr.r in the compiled first query).  Without this leading anonymous type, the parser does not assign a subcriteria to that many-to-one traversal through r.Type.  That is highly upsetting because the query that we are writing is actually more like this:

db.Patients.SelectMany(p => p.PatientRecords, (p, r) => r) 
    .Where(specification.IsSatisfied()).Sum(r => r.Amount);

where specification.IsSatisfied() returns a dynamically generated Expression<Func<PatientRecord, bool>>.  I can’t rewrite those specifications to use some query-specific anonymous type and I shouldn’t have to.

On a mission, I fired up the NHibernate.Linq source code and hacked away at it until this specific scenario was supported.  There is still the limitation that you must use the second overload of SelectMany that accepts the result selector.  There is no way of easily getting around that with the current implementation based off of the Criteria API.  However, despite all of NHibernate.Linq’s shortcomings, my specifications now work just fine (running some pretty complex queries).

If you are having similar frustrations, go grab the latest trunk r1010.

…until the next bug

P.S. I am very much looking forward to NH.Linq 2.0 using relinq and the new AST parser.

Currently rated 3.4 by 7 people

  • Currently 3.428571/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Programming

NHibernate.Linq 1.0 is out and about!

27. July 2009

In case you haven’t heard, NHibernate.Linq v1.0 has been released.  This has been a long time in the making and Tuna has put the final touches on it to make it possible.  You can get the binaries from the NHibernate download area.

Here are some informal release notes:

The implementation uses the NHibernate Criteria API under the covers, so anything it doesn’t support, NH.Linq won’t support.  Other things were just too hard to implement using the Criteria API.  Some major things this includes are group joins, subqueries in select clauses, and groupby.

However, despite that, limited subqueries are supported in the where clause.  Namely, using Count, Sum, Max, Min, Avg, and Any methods.  You can also use SelectMany queries (rather than GroupJoin) to do queries like this:

from o in db.Orders
from ol in o.LineItems
where ol.Price > 10
select o;

or this:

from o in db.Orders
where o.LineItems.Any(ol => ol.Price > 10)
select o;

All in all, it is a tried and tested implementation.  It has been used on number of projects in production for two years now.  As long as you know where the pitfalls are and how to avoid them, it is a useful addition to your NH toolkit.

Now, if you are saying to yourself, “well, that sucks, I really wanted to use GroupJoin and I want to nest other queries in my select clause.”  Fear not, this is where Steve Strong and the re-linq guys come in to save the day.  Steve has done a lot of all of the work on porting over the current HQL parser that is active in NHibernate 2.1.  The new linq implementation will interact with it natively (rather than use the Criteria API as an intermediary).  The current implementation has pretty much reached its upper limit on what it can do – limited by what the Criteria API can do.  The new implementation has much more grandiose plans than the current release.  It is the future, but, as most of us live in the present, this release is now.

For all intensive purposes, consider this a stopgap release.  Again, that doesn’t mean its not capable – just don’t expect major new features from this implementation.

Thanks to Tuna and Oren for pushing this release out the door.

Currently rated 3.0 by 5 people

  • Currently 3/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Programming

Dependency Injection with NHibernate and Autofac

28. May 2009

Fabio just recently committed changes to NHibernate which centralize all of NHibernate’s Activator.CreateInstance calls to an IObjectsFactory instance.  This is exciting because this gives us an opportunity to provide dependency injection services to all of those NHibernate-specific infrastructure types (IUserType, etc.).

To give you a concrete example of what I am talking about, check back to Ayende’s EncryptedStringUserType.  Rather than getting an instance of ICryptoProvider via a singleton accessor, it is now possible to have NHibernate inject the ICryptoProvider dependency through the constructor.  This is very valuable in cases where the ICryptoProvider has other dependencies or has some parameters that need to come from configuration.  You get full support from the container for that.

Fabio wrote up a post describing the implementations for Castle and Spring.  So, I thought I’d follow suit with an implementation for my new favorite container, Autofac (why it is my new favorite container will be a topic for another post).

To use it, make sure you are using the latest trunk of NHibernate.  Then, just add a reference in your project to Autofac.Integration.NHibernate.  At this time, the only way to configure the BytecodeProvider is programmatically.  So, BEFORE you call new NHibernate.Cfg.Configuration().Configure(), you need to set the BytecodeProvider to the AutofacBytecodeProvider.

containerProvider = new ContainerProvider(builder.Build());

Environment.BytecodeProvider = new AutofacBytecodeProvider(
    containerProvider.ApplicationContainer, new ProxyFactoryFactory());

Then just enjoy the DI goodness integrated with NH.  If any type NHibernate needs is not registered with the container, it will fall back to creating the type with Activator.CreateInstance.

The code can be found here.  I am going to contact the Autofac guys and see about putting this into the Autofac.Contrib project.

Update: This is now included in the Autofac.Contrib projectcheck it out here.

Currently rated 3.6 by 7 people

  • Currently 3.571429/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Programming ,