Using LINQ specifications in the data access layer

The specification pattern is a way to encapsulate query criteria inside aptly named business rule objects, called specifications. A specification has a single purpose. It should answer whether an entity of some type satisfies the conditions or criteria for a specific business rule or not.

In this recipe, we'll show you how to set up and use the specification pattern with the NHibernate repository and LINQ expressions.

Getting ready

To complete this recipe we will need a LinqSpecs library. The documentation and source code can be found at http://linqspecs.codeplex.com.

Complete the Setting up an NHibernate repository recipe.

How to do it…

  1. Install LinqSpecs using the NuGet Package Manager console by executing the following command:
    Install-Package LinqSpecs
    
  2. Add the following two methods to the IRepository interface:
    IEnumerable<T> FindAll(Specification<T> specification);
    T FindOne(Specification<T> specification);
  3. Add the following three methods to NHibernateRepository:
    public IEnumerable<T> FindAll(Specification<T> specification)
    {
      var query = GetQuery(specification);
      return WithinTransaction(() => query.ToList());
    }
    
    public T FindOne(Specification<T> specification)
    {
      var query = GetQuery(specification);
      return WithinTransaction(() => query.SingleOrDefault());
    }
    
    private IQueryable<T> GetQuery(
      Specification<T> specification)
    {
      return session.Query<T>()
        .Where(specification.IsSatisfiedBy());
    }
  4. Add the following specification to Eg.Core.Data.Queries:
    public class MoviesDirectedBy : Specification<Movie>
    {
      private readonly string _director;
    
      public MoviesDirectedBy(string director)
      {
        _director = director;
      }
    
      public override 
         Expression<Func<Movie, bool>> IsSatisfiedBy()
      {
        return m => m.Director == _director;
      }
    }
  5. Add another specification to Eg.Core.Data.Queries, using the following code:
    public class MoviesStarring : Specification<Movie>
    {
      private readonly string _actor;
    
      public MoviesStarring(string actor)
      {
        _actor = actor;
      }
    
      public override Expression<Func<Movie, bool>> IsSatisfiedBy()
      {
        return m => m.Actors.Any(a => a.Actor == _actor);
      }
    }

How it works…

The specification pattern allows us to separate the process of selecting objects from the concern about which objects to select. The repository handles selecting objects, while the specification objects are concerned only with the objects that satisfy their requirements.

In our specification objects, the IsSatisfiedBy method of the specification object returns a LINQ expression to determine which objects to select.

In the repository, we get an IQueryable from the session, pass this LINQ expression to the where method, and execute the LINQ query. Only objects that satisfy the specification will be returned.

For a detailed explanation of the specification pattern, check out http://martinfowler.com/apsupp/spec.pdf.

There's more…

To use our new specifications with the repository, use the following code:

var movies = repository.FindAll(
    new MoviesDirectedBy("Stephen Spielberg"));

Specification composition

We can also combine specifications to build queries that are more complex. For example, the following code will find all movies directed by Steven Speilberg and starring Harrison Ford:

var movies = repository.FindAll(
    new MoviesDirectedBy("Steven Spielberg")
    && new MoviesStarring("Harrison Ford"));

LinqSpecs handles this rather intuitive syntax using operator overloading. When two specifications are combined, for example with an && operator, the LINQ expressions will be combined accordingly.

See also

  • Transaction auto-wrapping for the data access layer
  • Setting up an NHibernate repository
  • Using named queries in the data access layer
  • Using ICriteria in the data access layer
  • Using paged queries in the data access layer
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.116.14.245