Specifying Specifications

time to read 3 min | 484 words

imageSpecification:  An explicit statement of the required characteristics for a matching entity / object.

Specifications are very useful concepts when you need to think about searches. They are useful because they seperate what you are searching from how you are searching.

In the example to the right, we have a specification for a job opening. We can use it to search for matching job openings. But couldn't we do it directly? Yes we could, but then we would dealing with both what and how at the same time.

Let us say that we want to search for an opening matching a male candidate, age 28, shall we? How do we handle that?

Pretty simple, actually:

OpeningSpecification spec = new OpeningSpecification();
spec.Age = 28;
spec.Gender = Gender.Male;
spec.FindAll(openingRepository);

The specification is responsible for translating that to a query that would return the correct result, which this translation can be wildly different than the specification itself. For example, let us take the age. We want opening that match a person whose age is 28, but the opening doesn't have a DesiredAge property, they have min / max ages that they allow.

That fact means that we search for a male doesn't mean that we need to exclude every opening where the max/min age reject someone with age of 28 years. But we don't care about that when we use the speficiation, we just want to find the matching opening.

From an implementation perspective, we use the property setters as a way to incremently build the query, here is how I built the Age property:

[Property]
public virtual int? Age
{
	get { return age; }
	set
	{
		age = value;
		if (value == null)
			return;
		criteria
			.Add(Expression.Le("MaxAge", age) || Expression.IsNull("MaxAge"))
			.Add(Expression.Ge("MinAge", age) || Expression.IsNull("MinAge"));
	}
}

As an aside, consider the usage of the operator overloading there, I completely forgot to use that before, and that is a shame, since I was the one that added that to NHibernate. Andrew Davey reminded me how useful this is.

You may notice that I have a [Property] on the specification Age property, and that it carries some unusual properties such as Id, Name and Username. Those are used to persist the specification, so I can save it and then load it again, extremely useful if I want to save a search for "Males over 30", and re-run it later.

Another advantage here is that I have the possibility to construct a specification easily from another object. You can see the FromCandidate() method on the specification above, it build a specification from the candidate that matches job opening that the candidate is eligable for.

All in all, a very useful concept, and it gets even nicer if you throw MonoRail into the mix, but this is a matter for another post.