0

In my project; I have included specific pattern classes that are given below. I dont know how to implement this. These codes are included by previous developers.

public interface ISpecification<T>
{
    Expression<Func<T, bool>> SpecExpression { get; }
    bool IsSatisfiedBy(T obj);
}

public static class IExtensions
{
    public static ISpecification<T> And<T>(
        this ISpecification<T> left,
        ISpecification<T> right)
    {
        return new And<T>(left, right);
    }

    public static ISpecification<T> Or<T>(
        this ISpecification<T> left,
        ISpecification<T> right)
    {
        return new Or<T>(left, right);
    }

    public static ISpecification<T> Negate<T>(this ISpecification<T> inner)
    {
        return new Negated<T>(inner);
    }
}

public abstract class SpecificationBase<T> : ISpecification<T>
{
    private Func<T, bool> _compiledExpression;

    private Func<T, bool> CompiledExpression
    {
        get { return _compiledExpression ?? (_compiledExpression = SpecExpression.Compile()); }
    }

    public abstract Expression<Func<T, bool>> SpecExpression { get; }

    public bool IsSatisfiedBy(T obj)
    {
        return CompiledExpression(obj);
    }
}

public class And<T> : SpecificationBase<T>
{
    ISpecification<T> left;
    ISpecification<T> right;

    public And(
        ISpecification<T> left,
        ISpecification<T> right)
    {
        this.left = left;
        this.right = right;
    }

    // AndSpecification
    public override Expression<Func<T, bool>> SpecExpression
    {
        get
        {
            var objParam = Expression.Parameter(typeof(T), "obj");

            var newExpr = Expression.Lambda<Func<T, bool>>(
                Expression.AndAlso(
                    Expression.Invoke(left.SpecExpression, objParam),
                    Expression.Invoke(right.SpecExpression, objParam)
                ),
                objParam
            );

            return newExpr;
        }
    }
}

public class Or<T> : SpecificationBase<T>
{
    ISpecification<T> left;
    ISpecification<T> right;

    public Or(
        ISpecification<T> left,
        ISpecification<T> right)
    {
        this.left = left;
        this.right = right;
    }

    // OrSpecification
    public override Expression<Func<T, bool>> SpecExpression
    {
        get
        {
            var objParam = Expression.Parameter(typeof(T), "obj");

            var newExpr = Expression.Lambda<Func<T, bool>>(
                Expression.OrElse(
                    Expression.Invoke(left.SpecExpression, objParam),
                    Expression.Invoke(right.SpecExpression, objParam)
                ),
                objParam
            );

            return newExpr;
        }
    }
}

 public class Negated<T> : SpecificationBase<T>
{
    private readonly ISpecification<T> _inner;

    public Negated(ISpecification<T> inner)
    {
        _inner = inner;
    }

    // NegatedSpecification
    public override Expression<Func<T, bool>> SpecExpression
    {
        get
        {
            var objParam = Expression.Parameter(typeof(T), "obj");

            var newExpr = Expression.Lambda<Func<T, bool>>(
                Expression.Not(
                    Expression.Invoke(this._inner.SpecExpression, objParam)
                ),
                objParam
            );

            return newExpr;
        }
    }
}

How to implement above specification with a simple example? What is the use of this specification?

Pradees
  • 191
  • 1
  • 4
  • 17
  • This is the Specification Pattern implemented with the aid of `Expression`. The use case for it depends on your domain model. – Ofir Winegarten Mar 01 '17 at 11:06
  • @Ofir Winegarten Can u give a small example with above classes? How to use it? – Pradees Mar 01 '17 at 11:37
  • While the specification pattern is very useful to keep domain knowledge in the domain layer, you should always consider whether the rule shouldn't be part of the entity itself. E.g. why have a `CustomerIsEligibleForPromotionSpecification` if you can have a method in the `Customer` entity itself: `bool IsEligibleForPromotion`. It makes sense to have a specialized Specification class when the check needs data that is not present in the entity/aggregate. Otherwise, it should probably be just a method. – Phillippe Santana Aug 02 '19 at 20:42

1 Answers1

0

As i wrote in the comments, This is the Specification Pattern implemented with the aid of Expression.

Let's say that we have the following Domain-Model:

public class Person
{
    public string Name { get; set; }
    public DateTime BirthDate { get; set; }
    public string Country { get; set; }
}

And, also, we have a list of these:

List<Person> persons; // <-- initialized elsewhere

Now we can have two specification for them. Lets have one for those who live in Spain and one for those born before 01/01/2000

public class SpainSpec : SpecificationBase<Person>
{
    public override Expression<Func<Person, bool>> SpecExpression => person => person.Country == "Spain";
}

public class BornBefore2000 : SpecificationBase<Person>
{
    public override Expression<Func<Person, bool>> SpecExpression => person => person.BirthDate < DateTime.Parse("2000-01-01");
}

Now we can use it to find all the persons born before 2000:

ISpecification spec = new SpainSpec();
persons.Where (spec.IsSatisfiedBy);

You can ofcourse chain them to get those from spain that were born before 2000:

ISpecification spec = new SpainSpec().And(new BornBefore2000());
persons.Where (spec.IsSatisfiedBy);

This is really a very simple scenario you can have many more, depending on you model and needs.

Be careful when using Specifications so you don't loose control over it and have too many classes or find yourself re-inventing the wheel.

Ofir Winegarten
  • 9,215
  • 2
  • 21
  • 27