Specification Design Pattern in C#

In computer programming, the specification pattern is a particular software design pattern, whereby business rules can be recombined by chaining the business rules together using boolean logic. The pattern is frequently used in the context of domain-driven design.

In short, the main benefit of using “specifications” is a possibility to have all the rules for filtering domain model objects in one place, instead of a thousand of lambda expressions spread across an application.

The classic implementation of the design pattern is as follows:

What’s wrong with it in respect of C#?

  1. There are Expression<Func<T, bool>>and Func<T, bool>>. Their signatures match
  2. There are Extension methods. Developers may use them in the following way:
  3. You may implement an add-on over LINQ:

Finally, we have a question if it is worth using a 10-year old pattern of Java in C# and how we can implement it.

We decided that we should use it. So, the implementation of the pattern is now as follows:

Why not using Func<T, bool>?

It is very difficult to go on to Expression from Func. Often, it is necessary to move filtering to a database query level, otherwise, you will have to retrieve millions of records and filter them in memory, which is not optimal.

Why not using Expression<Func<T, bool>>?

Moving from Expression to Func is quite trivial:

var func = expression.Compile()

However, its linking Expression is not as simple as it may seem.  It becomes even more unpleasant if you need to perform a conditional assembly of the statement (for example, when a specification has three parameters, two of which are optional). Expression<Func<T, bool>> does not work correctly when it is necessary to use subqueries such as query.Where(x => someOtherQuery.Contains(x.Id)).

After all, having analyzed all these arguments, I decided that the simplest way is to modify the target IQueryable and further pass it using a fluent interface. Additional Where methods represent the code as a traditional chain of LINQ transformations.

Following this logic, we can outline an abstraction for sorting.

Then, after adding Dynamic Linq and Reflection, we can create a basic object for filtering in a declarative style. The code below analyzes public properties of the AutoSpec descendant and the type for which the filtration will be applied. If they match and a property of the AutoSpec descendant is filled, a new filtering rule for the given field will be automatically added.

It is possible to implement AutoSpec without Dynamic Linq, using only Expression. However, its implementation will take many rows and it will be difficult to understand the code.

UPD

Still, some people say that IQueryableSpec does not meet requirements of linking. The fact is that I face || very rarely, while to get &&, I simply use query.Where(spec1).Where(spec2). That’s why I decided to perform a small refactoring:

There is the LinqSpecs library. What I don’t like about the library is that I have to create separate types of specifications each time. I think that it is quite enough to add Expression<Func<T, bool>> in this case.

Use Predicate Builder:

Now, add syntax sugar to use && and ||, and set the IHasId restriction on generic, which is optional.

I got used to writing specification statements as static fields in the class of the entity to which they belong:

Taking into account the code above, we may re-write the code:

To move on, we will get rid of DynamicLinq. In this case, we need to work with expression trees.

 

Max Arshinov

Max Arshinov

Chief Executive Officer (CEO) at hightech.today
Max Arshinov has been developing applications for business for more than 10 years. He had been a developer, PM, and head of the testing department until he set up his own company "Hightech". Now, Maxim is a CEO of the company.
Max Arshinov

Latest posts by Max Arshinov (see all)

Max Arshinov

Max Arshinov has been developing applications for business for more than 10 years. He had been a developer, PM, and head of the testing department until he set up his own company "Hightech". Now, Maxim is a CEO of the company.