We love Specifications!
Oh no, not another post about the Specification pattern! Oh yes, to add to the existing body of knowledge found here, here, here, here, here, here, and in many other places, I’d like to present my own take on the pattern.
My particular focus is on smooth integration with LINQ, and the approach described below manages this but does have some limitations.
Consider this a proof-of-concept if you will. It certainly hasn’t been exercised in production code with real world queries yet.
Wouldn’t it be nice…
…If we could write something like this and have NHibernate.Linq interpret the method call to SQL:
It is possible!
If we rewrite the embedded LINQ as a method chain we can see that IQueryable<T>.Where takes an Expression<Func<T,bool>> parameter, so our specification (e.g. IdEqual, HasInvolvementOf) needs to return this type.
Exploiting partial function application we can make the signature of our specification would look like:
This does look a little daunting, but we can ease the eyestrain by judicious use of a using alias:
What we have here is a field member (IdEqual) that returns a function that takes a long and returns an expression tree representing a function that takes a Shift and returns a boolean. (Did you get that?)
Linq is interested in the expression tree. In the examples above (Specifications in Linq) ‘shift.Has.IdEqual(100)’ is calling the function returned by IdEquals with the value 100. The Where extension method takes the return value.
This compiles fine but, as you’d expect, NHibernate.Linq doesn’t know what IdEqual means.
This leaves us with the challenge of transforming the method call into the expression returned by the method. Ideally NHibernate.Linq could do this for us, but given the current rework going on around Linq providers for NHibernate I’ve not even attempted this.
Instead we cheat and overload the Where method again to provide an implementation that does this translation.
There’s quite a bit going on here. Firstly we have the extension methods IQueryable<T>.Where and IEnumerable<T>.Where; these overload the existing extension methods in System.Core. The signature for the specification parameter causes the compiler to convert the specification field (e.g. IdEqual) to an expression tree. Satisfies and SatisfiedBy allow the specification to be used by assertions (for example validation of business rules).
Hopefully the comments in the method getPartiallyAppliedSpecification explain what is going on sufficiently clearly. This method is the heart of making specifications work ‘nicely’ with Linq. I’ve make a cursory attempt to make the caching of the reflection, instance activation and method call thread safe. Please correct and improve this code!
The methods CacheSize and ClearCache are there only for testing.
Putting it all together
The specification ‘InvolvementOf’ begins to reveal the power of the specification pattern in Linq. The following snippet show the specifications in use:
As can be seen in the third example, specifications can be combined in queries, however the somewhat more natural syntax of using ‘&’ between the constraints is not possible as the & operator is not defined on the Expression class (normally the & would be part of the expression tree). Other operators, like ‘!’ (not) also suffer a similar fate. Also using this style of specification as an assertion (the Satisfies method) is a little clunky. I haven’t yet tried to see how composable these specifications are, but I suspect this may be a further limitation.
Finally, I hope NHibernate.Linq will formally support specifications in the near future.