Back to Home

Specification Design Pattern in C #

.NET · Specification · DDD · IsSatisfiedBy

Specification Design Pattern in C #

    A “specification” in programming is a design pattern by which the representation of business logic rules can be transformed into a chain of objects connected by Boolean logic operations.

    I became acquainted with this term in the process of reading DDD Evans . On Habré there are articles describing the practical application of the pattern and the problems that arise in the implementation process .

    In short, the main advantage of using “specifications” is to have one understandable place in which all the rules for filtering objects of an object model are concentrated, instead of thousands of lambda expressions smeared with an even layer on the application.

    The classic design pattern implementation looks like this:

    public interface ISpecification
    {
        bool IsSatisfiedBy(object candidate);
    }
    

    What is wrong with C #?


    1. There are also , whose signature matches IsSatisfiedByExpression>Func>
    2. There are Extension methods. alexanderzaytsev with the help of them does like this:

      public class UserQueryExtensions 
      {
        public static IQueryable WhereGroupNameIs(this IQueryable users,
      string name)
        {
            return users.Where(u => u.GroupName == name);
        }
      }
      

    3. You can also implement just such an add-on on LINQ :

      public abstract class Specification
      {
        public bool IsSatisfiedBy(T item)
        {
          return SatisfyingElementsFrom(new[] { item }.AsQueryable()).Any();
        }
         public abstract IQueryable SatisfyingElementsFrom(IQueryable candidates);
      }
      

    Ultimately, the question arises: is it worth using a template in C # a decade ago from the Java world and how to implement it?


    We decided it was worth it this way:

    public interface IQueryableSpecification
        where T: class 
    {
        IQueryable Apply(IQueryable query);
    }
    public interface IQueryableOrderBy
    {
        IOrderedQueryable Apply(IQueryable queryable);
    }
    public static bool Satisfy(this T obj, Func spec) => spec(obj);
    public static bool SatisfyExpresion(this T obj, Expression> spec)
    => spec.AsFunc()(obj);
    public static bool IsSatisfiedBy(this Func spec, T obj)
    => spec(obj);
    public static bool IsSatisfiedBy(this Expression> spec, T obj) 
    => spec.AsFunc()(obj);
    public static IQueryable Where(this IQueryable source, 
    IQueryableSpecification spec)
        where T : class
        => spec.Apply(source);
    

    Why not ?Func


    It’s Funcvery difficult to go to Expression. More often it is necessary to transfer filtering exactly to the level of building a query to the database, otherwise you will have to pull out millions of records and filter them in memory, which is not optimal.

    Why not ?Expression>


    The transition from Expressionto Functhe contrary, is trivial: var func = expression.Compile(). However, building an Expression is by no means a trivial task . Even more not pleasant if conditional assembly of the expression is required (for example, if the specification contains three parameters, two of which are optional). And it does very poorly in cases requiring subqueries like . Ultimately, these considerations suggested that the easiest way is to modify the target and transmit it further through the fluent interface. Additional Where methods let the code look like it's a regular LINQ chain.Expression>query.Where(x => someOtherQuery.Contains(x.Id))

    IQueryable

    Guided by this logic, we can highlight the abstraction for sorting


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

    Then by adding Dynamic Linq and a little special street magicReflection , you can write a basic object to filter anything in a declarative style. The code below analyzes the public properties of the heir AutoSpecand the type to which filtering should be applied. If a match is found and the heir property is AutoSpecfilled in, a Queryable filter rule for this field will be automatically added.

    public class AutoSpec : IPaging, ILinqSpecification, ILinqOrderBy
        where TProjection : class, IHasId
    {
        public virtual IQueryable Apply(IQueryable 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 ILinqOrderBy.Apply(IQueryable queryable)
            => !string.IsNullOrEmpty(OrderBy)
            ? queryable.OrderBy(OrderBy)
            : queryable.OrderBy(x => x.Id);
    }
    

    AutoSpecyou can implement without Dynamic Linq, with the help of only Expression, but the implementation will not fit in ten lines and the code will be much more difficult to understand.

    UPD


    om2804 and xyzuvw rightly indicated that they IQueryableSpecdid not meet the layout requirements. The fact is that I rarely have to deal with the need to make ||, and && is achieved simply query.Where(spec1).Where(spec2). I decided to do a little refactoring to make the code cleaner:

         // Переименуем IQueryableSpecification в IQueryableFilter
        public interface IQueryableFilter
            where T: class 
        {
            IQueryable Apply(IQueryable query);
        }

    There is such a library : LinqSpecs . I don’t like the fact that you need to create separate types of specifications for each sneeze. Enough for me. Let's use the Predicate Builder from Pete Montgomery .Expression>



            /// 
            /// Creates a predicate that evaluates to true.
            /// 
            public static Expression> True() { return param => true; }
            /// 
            /// Creates a predicate that evaluates to false.
            /// 
            public static Expression> False() { return param => false; }
            /// 
            /// Creates a predicate expression from the specified lambda expression.
            /// 
            public static Expression> Create(Expression> predicate) { return predicate; }
            /// 
            /// Combines the first predicate with the second using the logical "and".
            /// 
            public static Expression> And(this Expression> first, Expression> second)
            {
                return first.Compose(second, Expression.AndAlso);
            }
            /// 
            /// Combines the first predicate with the second using the logical "or".
            /// 
            public static Expression> Or(this Expression> first, Expression> second)
            {
                return first.Compose(second, Expression.OrElse);
            }
    

    Details of the method implementation are Composeexplained in the link above. Now add syntactic sugar so that you can use && and || and IHasIdthe generic restriction , because I'm not interested in creating specifications for a Value Object . This restriction is not necessary, it just seems to me better.

         public static class SpecificationExtenions
        {
            public static Specification AsSpec(this Expression> expr)
                where T : class, IHasId
                => new Specification(expr);
        }
        public sealed class Specification : IQueryableFilter
            where T: class, IHasId
        {
            public Expression> Expression { get; }
            public Specification(Expression> expression)
            {
                Expression = expression;
                if (expression == null) throw new ArgumentNullException(nameof(expression));
            }
            public static implicit operator Expression>(Specification spec)
                => spec.Expression;
            public static bool operator false(Specification spec)
            {
                return false;
            }
            public static bool operator true(Specification spec)
            {
                return false;
            }
            public static Specification operator &(Specification spec1, Specification spec2)
                => new Specification(spec1.Expression.And(spec2.Expression));
            public static Specification operator |(Specification spec1, Specification spec2)
                => new Specification(spec1.Expression.Or(spec2.Expression));
            public static Specification operator !(Specification spec)
                => new Specification(spec.Expression.Not());
            public IQueryable Apply(IQueryable query)
                => query.Where(Expression);
            public bool IsSatisfiedBy(T obj) => Expression.AsFunc()(obj);
        }

    I'm used to writing “specification expressions” with static fields in the entity class to which they relate:

         public class Category : HasIdBase
        {
            public static readonly Expression> NiceRating = x => x.Rating > 50;
            public static readonly Expression> BadRating = x => x.Rating < 10;
            public static readonly Expression> Active= x => x.IsDeleted == false;
           //...
        }
        var niceCategories = db.Query.Where(Category.NiceRating);

    Given the code above, you can rewrite it like this:

         public class Category : HasIdBase
        {
            public static readonly Specification NiceRating
                 = new Specification(x => x.Rating > 50);
           //...
        }
        var niceCategories = db.Query
             .Where((Category.NiceRating || Category.BadRating) && Category.IsActive);

    Now get rid of DynamicLinq. We'll have to work a bit with expression trees.
    public enum Compose
    {
        And,
        Or
    }
    public static Spec AsSpec(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 =>
                    {
                        // собираем вручную выражения вида 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);
                        return (Expression>)
                            Expression.Lambda(delegateType, body, parameter);
                    })
                    .ToArray();
                if (!props.Any()) return new Spec(x => true);
                // и собираем через || или &&
                var expr = compose == Compose.And
                    ? props.Aggregate((c, n) => c.And(n))
                    : props.Aggregate((c, n) => c.Or(n));
                return expr.AsSpec();
            }

    Read Next