Written by 10:53 Basics of C# • One Comment

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.

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.

public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, 
IQueryableOrderBy<T> spec)
    where T : class
    => spec.Apply(source);

public interface IQueryableOrderBy<T>
{
    IOrderedQueryable<T> Apply(IQueryable<T> queryable);
}

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.

public class AutoSpec<TProjection> : IPaging, ILinqSpecification<TProjection>, ILinqOrderBy<TProjection>
    where TProjection : class, IHasId
{
    public virtual IQueryable<TProjection> Apply(IQueryable<TProjection> query) => GetType()
        .GetPublicProperties()
        .Where(x => typeof(TProjection).GetPublicProperties().Any(y => x.Name == y.Name))
        .Aggregate(query, (current, next) =>
        {
            var val = next.GetValue(this);
            if (val == null) return current;
            return current.Where(next.PropertyType == typeof(string)
                   ? $"{next.Name}.StartsWith(@0)"
                   : $"{next.Name}=@0", val);
        });

    IOrderedQueryable<TProjection> ILinqOrderBy<TProjection>.Apply(IQueryable<TProjection> queryable)
        => !string.IsNullOrEmpty(OrderBy)
        ? queryable.OrderBy(OrderBy)
        : queryable.OrderBy(x => x.Id);
}

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:

 // Rename IQueryableSpecification into IQueryableFilter
    public interface IQueryableFilter<T>
        where T: class 
    {
        IQueryable<T> Apply(IQueryable<T> query);
    }

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:

/// <summary>
        /// Creates a predicate that evaluates to true.
        /// </summary>
        public static Expression<Func<T, bool>> True<T>() { return param => true; }

        /// <summary>
        /// Creates a predicate that evaluates to false.
        /// </summary>
        public static Expression<Func<T, bool>> False<T>() { return param => false; }

        /// <summary>
        /// Creates a predicate expression from the specified lambda expression.
        /// </summary>
        public static Expression<Func<T, bool>> Create<T>(Expression<Func<T, bool>> predicate) { return predicate; }

        /// <summary>
        /// Combines the first predicate with the second using the logical "and".
        /// </summary>
        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
        {
            return first.Compose(second, Expression.AndAlso);
        }

        /// <summary>
        /// Combines the first predicate with the second using the logical "or".
        /// </summary>
        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
        {
            return first.Compose(second, Expression.OrElse);
        }

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

  public static class SpecificationExtenions
    {
        public static Specification<T> AsSpec<T>(this Expression<Func<T, bool>> expr)
            where T : class, IHasId
            => new Specification<T>(expr);
    }

    public sealed class Specification<T> : IQueryableFilter<T>
        where T: class, IHasId
    {
        public Expression<Func<T, bool>> Expression { get; }

        public Specification(Expression<Func<T, bool>> expression)
        {
            Expression = expression;
            if (expression == null) throw new ArgumentNullException(nameof(expression));
        }

        public static implicit operator Expression<Func<T, bool>>(Specification<T> spec)
            => spec.Expression;

        public static bool operator false(Specification<T> spec)
        {
            return false;
        }

        public static bool operator true(Specification<T> spec)
        {
            return false;
        }

        public static Specification<T> operator &(Specification<T> spec1, Specification<T> spec2)
            => new Specification<T>(spec1.Expression.And(spec2.Expression));

        public static Specification<T> operator |(Specification<T> spec1, Specification<T> spec2)
            => new Specification<T>(spec1.Expression.Or(spec2.Expression));

        public static Specification<T> operator !(Specification<T> spec)
            => new Specification<T>(spec.Expression.Not());

        public IQueryable<T> Apply(IQueryable<T> query)
            => query.Where(Expression);

        public bool IsSatisfiedBy(T obj) => Expression.AsFunc()(obj);
    }

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

public class Category : HasIdBase<int>
    {
        public static readonly Expression<Func<Category, bool>> NiceRating = x => x.Rating > 50;
        
        public static readonly Expression<Func<Category, bool>> BadRating = x => x.Rating < 10;

        public static readonly Expression<Func<Category, bool>> Active= x => x.IsDeleted == false;

       //...
    }

    var niceCategories = db.Query<Category>.Where(Category.NiceRating);

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

 public class Category : HasIdBase<int>
    {
        public static readonly Specification<Category> NiceRating
             = new Specification(x => x.Rating > 50);
       //...
    }

    var niceCategories = db.Query<Category>
         .Where((Category.NiceRating || Category.BadRating) && Category.IsActive);

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

public enum Compose
{
    And,
    Or
}

public static Spec<T> AsSpec<T>(this object obj, Compose compose = Compose.And)
            where T : class, IHasId
        {
            var filterProps = obj.GetType()
                .GetPublicProperties()
                .ToArray();

            var filterPropNames = filterProps
                .Select(x => x.Name)
                .ToArray();

            var props = typeof(T)
                .GetPublicProperties()
                .Where(x => filterPropNames.Contains(x.Name))
                .Select(x => new
                {
                    Property = x,
                    Value = filterProps.Single(y => y.Name == x.Name).GetValue(obj)
                })
                .Where(x => x.Value != null)
                .Select(x =>
                {
                    // manually create statements type e => e.Prop == Val
                    var parameter = Expression.Parameter(typeof (T));
                    var property = Expression.Property(parameter, x.Property);
                    var body = Expression.Equal(property, Expression.Constant(x.Value));
                    var delegateType = typeof(Func<T, bool>);
                    return (Expression<Func<T, bool>>)
                        Expression.Lambda(delegateType, body, parameter);
                })
                .ToArray();

            if (!props.Any()) return new Spec<T>(x => true);
            
            // create using || or &&
            var expr = compose == Compose.And
                ? props.Aggregate((c, n) => c.And(n))
                : props.Aggregate((c, n) => c.Or(n));

            return expr.AsSpec();
        }

 

Tags: , , Last modified: September 23, 2021
Close