Filter a collection using the Specification Pattern

I had never thought to use the Specification pattern as the basis for filtering a collection of data objects in search results. Call me late to the party, I guess. At any rate, today’s post is published so that I’ll have easy access to this code as well as my notes, and you, dear reader, can share in my forgetfulness.

The Specification pattern is part of the original Gang of Four (GoF) patterns. The entry on Wikipedia says:

In computer programming, the specification pattern is a particular software design pattern, whereby business logic can be recombined by chaining the business logic together using Boolean logic.

I decided to try to use this pattern as the basis for some search result filters against a collection returned from a database or web service. I will illustrate one such filter here.

First, we’ll define an interface:

using System;
using System.Linq.Expressions;

namespace SpecificationApp.Common.Specification
{
    public interface ISpecification<T>
    {
        Expression<Func<T, bool>> Predicate { get; }
        bool IsSatisfiedBy(T entity);
    }
}
Then, we implement the interface, and add some operator overloads:
using System;
using System.Linq;
using System.Linq.Expressions;

namespace SpecificationApp.Common.Specification
{
    public class Specification<T> : ISpecification<T>
    {
        private readonly Expression<Func<T, bool>> _predicate;

        public Specification(Expression<Func<T, bool>> predicate)
        {
            _predicate = predicate;
        }

        public Expression<Func<T, bool>> Predicate
        {
            get { return _predicate; }
        }

        public bool IsSatisfiedBy(T entity)
        {
            return _predicate.Compile().Invoke(entity);
        }

        public static Specification<T> operator &(Specification<T> leftSide, Specification<T> rightSide)
        {
            var rightInvoke = Expression.Invoke(rightSide.Predicate, leftSide.Predicate.Parameters.Cast<Expression>());
            var newExpression = Expression.MakeBinary(ExpressionType.AndAlso, leftSide.Predicate.Body, rightInvoke);
            return new Specification<T>(
                Expression.Lambda<Func<T, bool>>(newExpression, leftSide.Predicate.Parameters)
              );
        }

        public static Specification<T> operator |(Specification<T> leftSide, Specification<T> rightSide)
        {
            var rightInvoke = Expression.Invoke(rightSide.Predicate, leftSide.Predicate.Parameters.Cast<Expression>());
            var newExpression = Expression.MakeBinary(ExpressionType.OrElse, leftSide.Predicate.Body, rightInvoke);
            return new Specification<T>(
                Expression.Lambda<Func<T, bool>>(newExpression, leftSide.Predicate.Parameters)
              );
        }
    }
}

Then, assuming we will be using a collection of Person, we implement the PersonSpecifications class:

using SpecificationApp.Common.BusinessEntities;
using SpecificationApp.Common.Specification;

namespace SpecificationApp.Common.Specifications
{
    public class PersonSpecifications
    {
        public static Specification<Person> HasSSN
        {
            get
            {
                return new Specification<Person>(p => p.SocialSecurityNumber != null 
&& p.SocialSecurityNumber != 0); } } } }

Finally, we add usage of these specifications  as filters to our Person class:

using SpecificationApp.Common.Specifications;

namespace SpecificationApp.Common.BusinessEntities
{
    public class Person : BaseEntity
    {
        // entity properties - Id and other common properties are in BaseEntity      
        public int ? SocialSecurityNumber { get; set; }
        public string FirstName { get; set; }
        public string MiddleName { get; set; }
        public string LastName { get; set; }

// specifications based filter public bool HasSSN { get { return (PersonSpecifications.HasSSN).IsSatisfiedBy(this); } } } }

Now we can reference any of our filter specifications in as simple an example as a web page code-behind (or in a WPF or Silverlight ViewModel with property references instead of direct UI binding) like so:

var people = GetPeople();
if (ckSSNFilter.Checked) // a checkbox
{
    GridView1.DataSource = people.Where(i => i.HasSSN);
}
else
{
     GridView1.DataSource = people;
}
GridView1.DataBind();

Off course, because we’re using the Specification pattern and Linq, we can stack specifications to your heart’s content. This really comes in handy when you have some complex filtering to do. We did a log of leg work to get here, but the top-level code we’ll be working with the most is simple and fluent.

I put together a simple sample console application illustrating the above here. Hope it helps.

Bob Baker

Technorati Tags: ,,

posted @ Sunday, January 16, 2011 2:09 AM

Print

 

Comments have been closed on this topic.