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:
public interface ISpecification
{
bool IsSatisfiedBy(object candidate);
}
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:
public class UserQueryExtensions
{
public static IQueryable<User> WhereGroupNameIs(this IQueryable<User> users,
string name)
{
return users.Where(u => u.GroupName == name);
}
}
3. You may implement an add-on over LINQ:
public abstract class Specification<T>
{
public bool IsSatisfiedBy(T item)
{
return SatisfyingElementsFrom(new[] { item }.AsQueryable()).Any();
}public abstract IQueryable<T> SatisfyingElementsFrom(IQueryable<T> candidates);
}
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:
public interface IQueryableSpecification<T>
where T: class
{
IQueryable<T> Apply(IQueryable<T> query);
}public interface IQueryableOrderBy<T>
{
IOrderedQueryable<T> Apply(IQueryable<T> queryable);
}public static bool Satisfy<T>(this T obj, Func<T, bool> spec) => spec(obj);
public static bool SatisfyExpresion<T>(this T obj, Expression<Func<T, bool>> spec)
=> spec.AsFunc()(obj);public static bool IsSatisfiedBy<T>(this Func<T, bool> spec, T obj)
=> spec(obj);public static bool IsSatisfiedBy<T>(this Expression<Func<T, bool>> spec, T obj)
=> spec.AsFunc()(obj);public static IQueryable<T> Where<T>(this IQueryable<T> source,
IQueryableSpecification<T> spec)
where T : class
=> spec.Apply(source);
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.
If you found this note useful, continue reading on the blog — https://bit.ly/2wn3bZG.
Thank you and stay tuned!