In a general sense, a Specification object is just a predicate wrapped in an object. If a predicate is used very often with a class, it may make sense to "Move the method"> the predicate to the class to which it belongs.
This template really comes to life when you create something more complex, like this:
var spec = new All(new CustomerHasFunds(500.00m), new CustomerAccountAgeAtLeast(TimeSpan.FromDays(180)), new CustomerLocatedInState("NY"));
and transfer it or serialize; this may make even more sense when you provide some kind of βspecsβ user interface.
However, C # provides more idiomatic ways of expressing things like extension methods and LINQ:
var cutoffDate = DateTime.UtcNow - TimeSpan.FromDays(180); // captured Expression<Func<Customer, bool>> filter = cust => (cust.AvailableFunds >= 500.00m && cust.AccountOpenDateTime >= cutoffDate && cust.Address.State == "NY");
I played with some experimental code that implements specifications in terms of Expression
s, with very simple static design methods.
public partial class Customer { public static partial class Specification { public static Expression<Func<Customer, bool>> HasFunds(decimal amount) { return c => c.AvailableFunds >= amount; } public static Expression<Func<Customer, bool>> AccountAgedAtLeast(TimeSpan age) { return c => c.AccountOpenDateTime <= DateTime.UtcNow - age; } public static Expression<Func<Customer, bool>> LocatedInState(string state) { return c => c.Address.State == state; } } }
However, this is a whole template download that does not add value! These Expression
only look at public properties, so you can just use the plain old lambda! Now, if one of these specifications needs access to a non-public state, we really need a construction method with access to a non-public state. I will use lastCreditScore
as an example here.
public partial class Customer { private int lastCreditScore; public static partial class Specification { public static Expression<Func<Customer, bool>> LastCreditScoreAtLeast(int score) { return c => c.lastCreditScore >= score; } } }
We also need a way to make a composition from these Specifications - in this case, a composite that requires all children to be true:
public static partial class Specification { public static Expression<Func<T, bool>> All<T>(params Expression<Func<T, bool>>[] tail) { if (tail == null || tail.Length == 0) return _0 => true; var param = Expression.Parameter(typeof(T), "_0"); var body = tail.Reverse() .Skip(1) .Aggregate((Expression)Expression.Invoke(tail.Last(), param), (current, item) => Expression.AndAlso(Expression.Invoke(item, param), current)); return Expression.Lambda<Func<T, bool>>(body, param); } }
I believe that part of the flaw in this can lead to complex Expression
trees. For example, build this:
var spec = Specification.All(Customer.Specification.HasFunds(500.00m), Customer.Specification.AccountAgedAtLeast(TimeSpan.FromDays(180)), Customer.Specification.LocatedInState("NY"), Customer.Specification.LastCreditScoreAtLeast(667));
creates an Expression
tree that looks like this. (These are slightly formatted versions of what ToString()
returns when called in Expression
- a note that you wonβt be able to see the structure of an expression at all if you have only a simple delegate! A few notes: a DisplayClass
is a class generated by the compiler, which contains the local variables captured in the closure to solve the rising wave problem , and the resettable Expression
uses a single =
sign to represent equality comparison, not C # typical ==
.)
_0 => (Invoke(c => (c.AvailableFunds >= value(ExpressionExperiment.Customer+Specification+<>c__DisplayClass0).amount),_0) && (Invoke(c => (c.AccountOpenDateTime <= (DateTime.UtcNow - value(ExpressionExperiment.Customer+Specification+<>c__DisplayClass2).age)),_0) && (Invoke(c => (c.Address.State = value(ExpressionExperiment.Customer+Specification+<>c__DisplayClass4).state),_0) && Invoke(c => (c.lastCreditScore >= value(ExpressionExperiment.Customer+Specification+<>c__DisplayClass6).score),_0))))
Dirty! Many calls to immediate lambdas and stored lock references created in build methods. Substituting links to the closure with their captured values ββand Ξ²-reduction nested lambdas (I also Ξ±-converted all parameter names to unique generated characters as an intermediate step to simplify Ξ²-reduction), much simpler Expression
tree results:
_0 => ((_0.AvailableFunds >= 500.00) && ((_0.AccountOpenDateTime <= (DateTime.UtcNow - 180.00:00:00)) && ((_0.Address.State = "NY") && (_0.lastCreditScore >= 667))))
These Expression
trees can then be further combined, compiled into delegates, fairly printed, edited, passed to LINQ interfaces that understand Expression
trees (such as those provided by EF) or whatever you have.
On the side of the note, I built a silly little micro-test and actually found that removing lock references had a significant impact on the speed of evaluating the Expression
example when compiling the delegate - this reduced the evaluation time by almost half (!), From 134.1 ns to 70, 5 ns per call on the car where I am sitting in front of me. On the other hand, Ξ²-reduction did not reveal any distinguishable differences, possibly because compilation does this anyway. In any case, I doubt that the usual set of specification classes can achieve such an evaluation speed for a set of four conditions; if such a regular set of classes had to be built for other reasons, such as the convenience of a code-builder-UI, I think it would be desirable for the set of classes to create Expression
rather than evaluate directly, but first think about whether you need a template in general C # - I've seen too much code with an overdose of specs.