Note: This post assumes you know the basics of the Specification Pattern. It would be too much to cover in it’s entirety in this post. I’ll leave that to others that can explain it better than me. The basics are that you turn business logic into boolean logic. You can chain several Specifications together and at the end the is_specified_by method is called and returns a bool that determines if the current object meets the specified criteria.
If you want to just download the source code then feel free to here
I have been using DDD with Stub Repositories on a project recently. Everything was going great, all my unit tests passed, my domain logic for my specifications was working as I wanted with an in memory collection…and then I tried to create a repository using NHibernate. The big question was how do I get NHibernate to take my specification logic and turn it into an NHibernate query?
In order to execute a Linq query you use Where. So I started with
result = session.Linq<Employee>().Where(specification.is_satisfied_by());
My initial thinking was that the Where would take the specification and execute the is_satisfied_by method with the employee object and it would be returned if it evaluated true. Nope my query just returned all the Employees. It executed the specification but the query itself didn’t have any where logic in it. Let’s take a look at the Linq.Where clause
So we can basically pass it 2 types of things:
- Func<T,bool>
- Expression<Func<T,bool>>
Func<T,bool> is any Function that takes a Type (T) and returns a bool. Expression<Func<T,bool>> on the surface may sound like the same thing but it is not. Expression is a placeholder for an expression tree. This means that Func<T,bool> is turned into an piece of expression data and passed on to the NHibernate Linq provider. Why would we want to do that you might ask? The expression is still in the raw data form, it will get passed to the NHibernate Linq provider where it can be translated into a NHibernate compatible form. Think of the Expression Tree as the generic form that all Linq providers can use to translate into something else. That took me awhile to understand but it makes sense when you think about it. Here is what the Expression Tree looks like:
You can see that we are calling x=>x.Name.Equals("Adam Aldrich") Which is a type Func<Employee,Boolean>
This is nasty looking stuff. I show so you can see what will get passed to the Linq Provider is not just a function it is an Expression Tree.
So the point is if we can write our specification pattern to return Expression<Func<T,bool>> then it can be passed to any Linq Provider! We can pass this to NHibernate, Linq to Sql, Entity Framework, etc. so we are building a reusable Specification Library.
Ok, so hopefully I have explained why we need to pass in Expression<Func<T,bool>> so lets figure out how to make our specification pattern actually work.
This is our ISpecification Interface.
public interface ISpecification<T>
{
bool is_satisfied_by(T condition);
Expression<Func<T, bool>> is_satisfied_by();
ISpecification<T> And(ISpecification<T> other);
ISpecification<T> Or(ISpecification<T> other);
ISpecification<T> Not();
}
The only thing I am doing here that is different than the normal specification pattern is the Expression<Func<T,bool>> is_satisfied_by(). This will be used to pass to our Where method on the Linq provider. I don’t want to show all the code but I do need to show how you implement the basic Binary/Unary Specifications (And, Or, Not).
Here is what the basic Specifications would look like:
public class OrSpecification<T> : BinarySpecification<T>
{
public OrSpecification(ISpecification<T> left, ISpecification<T> right) : base(left,right){}
public override bool is_satisfied_by(T condition)
{
return left_side.is_satisfied_by(condition) || right_side.is_satisfied_by(condition);
}
public override Expression<Func<T, bool>> is_satisfied_by()
{
var body = Expression.OrElse(left_side.is_satisfied_by().Body, right_side.is_satisfied_by().Body);
var lambda = Expression.Lambda<Func<T,bool>>(body, left_side.is_satisfied_by().Parameters);
return lambda;
}
}
public class AndSpecification<T> : BinarySpecification<T>
{
public AndSpecification(ISpecification<T> left, ISpecification<T> right) : base(left,right){}
public override bool is_satisfied_by(T condition)
{
return left_side.is_satisfied_by(condition) && right_side.is_satisfied_by(condition);
}
public override Expression<Func<T, bool>> is_satisfied_by()
{
var body = Expression.AndAlso(left_side.is_satisfied_by().Body, right_side.is_satisfied_by().Body);
var lambda = Expression.Lambda<Func<T,bool>>(body, left_side.is_satisfied_by().Parameters);
return lambda;
}
}
public class NotSpecification<T> : Specification<T>
{
readonly ISpecification<T> to_negate;
public NotSpecification(ISpecification<T> to_negate)
{
this.to_negate = to_negate;
}
public override bool is_satisfied_by(T condition)
{
return !to_negate.is_satisfied_by(condition);
}
public override Expression<Func<T, bool>> is_satisfied_by()
{
var body = Expression.Not(to_negate.is_satisfied_by().Body);
var lambda = Expression.Lambda<Func<T,bool>>(body, to_negate.is_satisfied_by().Parameters);
return lambda;
}
}
The key pieces to look at are the Expression<Func<T, bool>> is_satisfied_by() methods. Lets look at the Not.
var body = Expression.Not(to_negate.is_satisfied_by().Body);
var lambda = Expression.Lambda<Func<T,bool>>(body, to_negate.is_satisfied_by().Parameters);
Expression.Not is the expression version of the bitwise ! (Not) operator. We are passing it the Body of the Expression which if you look at the expression tree I showed earlier is just really the guts of our specification wrapped up into an expression.
Expression.Lambda just takes our body after it has been negated and wraps it back up into a lambda expression which will get passed to Linq. Lets look at the before and after to show you that all we are doing is modifying the expression tree before it is sent to Linq.
Before:
|
After:
|
|
|
You can see how the x=>False is now x=>Not(False). And and Or are similar.
var body = Expression.OrElse(left_side.is_satisfied_by().Body, right_side.is_satisfied_by().Body);
var lambda = Expression.Lambda<Func<T,bool>>(body, left_side.is_satisfied_by().Parameters);
var body = Expression.AndAlso(left_side.is_satisfied_by().Body, right_side.is_satisfied_by().Body);
var lambda = Expression.Lambda<Func<T,bool>>(body, left_side.is_satisfied_by().Parameters);
There are a few differences. Since these are Binary operations we need a left and right to compare. We also use the OrElse and the AndElse. There is also an Expression.And and an Expression.Or but those are bitwise operators, we need the conditional operators respectively (&&) and (||) so we use OrElse and AndElse. Also notice we are only using the parameter from the left_side. Our left_side and right_side should be of the same type T so this should not be a problem.
Putting it all together we can use our specification pattern like this:
specification = new AnonymousSpecification<Employee>(x => x.Name.Equals("Adam Aldrich"))
.Or(new AnonymousSpecification<Employee>(x => x.Age.Equals(4)));
results = session.Linq<Employee>().Where(specification.is_satisfied_by());
Here is what the query looks like
specification = new AnonymousSpecification<Employee>(x => x.Name.Equals("Adam Aldrich"))
.And(new AnonymousSpecification<Employee>(x => x.Age.Equals(27)));
results = session.Linq<Employee>().Where(specification.is_satisfied_by());
specification = new NotSpecification<Employee>(
new AnonymousSpecification<Employee>(x => x.Name.Equals("Adam Aldrich")));
results = session.Linq<Employee>().Where(specification.is_satisfied_by());
Summary
Whew! that was a lot. I hope it made sense. The biggest thing I hope you take away from this is how you can use the Specification pattern to keep query logic inside the domain. Then take that specification and feed it into Linq to NHibernate (or any Linq provider for that matter). I also hope you take away that Expression<Func<T,bool>> is a generic way to pass a query around between Linq providers. I only implemented a few operators (And,Or,Not) but you can see how you could easily build others (GreaterThan, LessThan) into the library. Also you can wrap all this up into a nice Internal DSL to make it much easier to use, I’ll save that for a later post.
I have attached the source code along with tests so you can see more details about what is going on.
Tags: .Net, Linq, Nhibernate