Introduction

LinqSpec is a framework that will help you to create specifications for Linq queries. You can read more about the specification pattern here.
Almost all users of Linq create specifications in their daily work, but most of them write those specifications scattered all over the code.
The idea behind this project is to help the user to write, test and expose specifications as first-class objects.
You will learn how to use LinqSpec in this brief document.

Defining simple specifications

In order to define our first specification named "CustomersFromCountry" we need to inherit from LinqSpec.Specification<T>:

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

So this is our implementation:

public class CustomersFromCountry : Specification<Country>
{
    private readonly Country country;

    public CustomersFromCountry(Country country)
   {
        this.country = country;
    }

    public override Expression<Func<Country, bool>> IsSatisfiedBy()
    { 
         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> FindAll<T>(Specification<T> specification)
    {
        return [a queryable source].Where(specification.IsSatisfied());
    }
    public int Count<T>(Specification<T> specification)
    {
        return [a queryable source].Count(specification.IsSatisfied());
    }

The usage is very simple;
   var customersFromArgentina = customersDao.Retrieve(new CustomersFromCountry(Countries.Argentina));

Specifications and boolean operators

One of the most interesting features of LinqSpecs is that you can operate known specifications with "!", "&&" and "||".
For example:
   var fromArgentina = new CustomersFromCountry(Countries.Argentina);
   var preferred = new CustomersPreferred();
   var result = customersDao.Retrieve(fromArgentina && !preferred);
This code returns all customers from Argentina that are not preferred.

Side note: the & operator work as an && (AndAlso) operator. The same for | and ||.

Comparing specifications

The result of and'ing, or'ing and negating specifications implements equality members. That's said;

    (spec1 && spec2).Equals(spec1 && spec2); //this returns true
    (spec1 && spec2).Equals(spec2 && spec1); //this returns false, because AndAlso and OrElse are not commutable operations.
    (spec1 && (spec2 || !spec3)).Equals(spec1 && (spec2 || !spec3)); //returns true
    (new CustomerFromCountry("Argentina") && new CustomerPreferred)
           .Equals(new CustomerFromCountry("Argentina") && new CustomerPreferred()); //returns true

This is an useful feature when you are writing Asserts in your unit tests.
Note: your custom Specifications must implement equality members. Also make sure including spec parameters in the Equals member.

Alternative way to expose specifications

An alternative way of exposing specs is with a static class:
public static CustomerSpecs
{
    public static Specification<Customer> FromCountry(Country country) 
    { 
        return new CustomerFromCountry(country);
    }

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

Usage

    customersDao.FindAll(CustomerSpecs.FromCountry(argentina) && CustomerSpecs.EligibleForDiscount(3223));

The AdHocSpecification

The AdHocSpecification is an alternative way to write an spec without writing a class.
You should not abuse of them, and try to write those in a single place as explained above. Also AdHocSpecification doesn't implement Equals, so two AdHocSpecifications are equal only if they are the same instance.

Last edited Jun 14, 2010 at 2:39 PM by jfromaniello, version 12