Skip to content

Introduction

Sergey Navozenko edited this page Nov 3, 2018 · 8 revisions

Defining simple specifications

In order to define our first specification named CustomerFromCountrySpec we need to inherit from Specification<T>:

public abstract class Specification<T>
{
    public abstract Expression<Func<T, bool>> ToExpression();
}

So this is our implementation:

using LinqSpecs;

public enum Country { Argentina, France, Italia, ... }

public class CustomerFromCountrySpec : Specification<Customer>
{
    public Country Country { get; set; }
   
    public CustomerFromCountrySpec(Country country)
    {
        Country = country;
    }

    public override Expression<Func<Customer, bool>> ToExpression()
    { 
        return c => c.Country == Country;
    }
}

Simple as is, to use this class, your repository or DAO should implement these kind of methods:

public IEnumerable<T> Find(Specification<T> specification)
{
    return [a queryable source].Where(specification).ToList();
}

public int Count(Specification<T> specification)
{
    return [a queryable source].Count(specification);
}

The usage is very simple:

var spec = new CustomerFromCountrySpec(Country.Argentina);
var customersFromArgentina = customerRepository.Find(spec);

Alternative way to expose specifications

An alternative way of exposing specifications is with a static class:

public static class CustomerSpecs
{
    public static Specification<Customer> FromCountry(Country country) 
    { 
        return new CustomerFromCountrySpec(country);
    }

    public static Specification<Customer> EligibleForDiscount(decimal discount)
    {
        return new AdHocSpecification<Customer>(
            c => c.IsPreferred && !c.HasDebt &&
                c.LastPurchaseDate > DateTime.Today.AddDays(-30));
    }
}

Usage:

customerRepository.Find(
    CustomerSpecs.FromCountry(argentina) &&
    CustomerSpecs.EligibleForDiscount(3223));

Why does LinqSpecs use expression trees?

In classic implementation of the specification pattern by Eric Evans and Martin Fowler a Specification class has one method called IsSatisfiedBy that returns a boolean value:

bool IsSatisfiedBy(T candidate)

This method checks whether the candidate object matches some criteria. Such approach is simple and convenient when a query is executed in memory, but it can't be used in queries to a database.

In LinqSpecs all specifications produce an expression tree:

Expression<Func<T, bool>> ToExpression()

An expression can be translated to a piece of SQL query, or MongoDb query, or anything. For example, you can pass expression as a filter to IQueryable<T> data source from Entity Framework or NHibernate, your filter will be processed on the server side.