API for Specification Pattern Implementation using Linq

Development API (code)
Table of Contents
Summary

Source code for the implementation of the namespace DesignPatterns.Specification.Linq


Operations
ISpecification.cs
How to do it
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace DesignPatterns.Specification.Linq
{
    public interface ISpecification<TEntity> where TEntity:class
    {
        string Description { get; }

        Expression<Func<TEntity, bool>> predicate { get; }

        bool IsSatisfiedBy(TEntity entity);
    }
}
Specification.cs
How to do it
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace DesignPatterns.Specification.Linq
{
    public abstract class Specification<TEntity> : ISpecification<TEntity> where TEntity: class
    {
        #region "PRIVATE MEMBERS"

        private Func<TEntity, bool> _compiledExpression;

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

        #endregion       

        #region "CONSTRUCTORS"
        #endregion

        #region "PUBLIC MEMBERS"        

        public abstract string Description { get;} 

        public abstract Expression<Func<TEntity, bool>> predicate {get;}

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

        #endregion

        #region "PRIVATE METHODS"
        protected Expression<Func<TEntity, bool>> Compose(Expression<Func<TEntity, bool>> first, Expression<Func<TEntity, bool>> second, Func<Expression, Expression, Expression> merge)
        {
            // build parameter map (from parameters of second to parameters of first)
            var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f);

            // replace parameters in the second lambda expression with parameters from the first
            var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);

            // apply composition of lambda expression bodies to parameters from the first expression
            return Expression.Lambda<Func<TEntity, bool>>(merge(first.Body, secondBody), first.Parameters);
        }
        #endregion
    }

}
AndSpecification.cs
How to do it
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace DesignPatterns.Specification.Linq
{
    public  class AndSpecification<TEntity> : Specification<TEntity> where TEntity : class
    {
        private ISpecification<TEntity> _left;
        private ISpecification<TEntity> _right;

        public AndSpecification(ISpecification<TEntity> s1, ISpecification<TEntity> s2)
        {
            _left = s1;
            _right = s2;
        }

        public override string Description { get { return "(" + _left.Description + ") AND " + _right.Description ; } } 

        public override Expression<Func<TEntity, bool>> predicate
        {
            get
            {
                return this.Compose(_left.predicate, _right.predicate, Expression.And);
            }
        }
        /* Not supported on Entity Framework
        public override Expression<Func<TEntity, bool>> predicate
        {
            get
            {
                var lvParam = Expression.Parameter(typeof(TEntity), "obj");

                var lvNewExpression = Expression.Lambda<Func<TEntity, bool>>(
                    Expression.AndAlso(
                        Expression.Invoke(_left.predicate, lvParam),
                        Expression.Invoke(_right.predicate, lvParam)
                    ),
                    lvParam
                );

                return lvNewExpression;
            }
        }
         */

    }
}
OrSpecification.cs
How to do it
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace DesignPatterns.Specification.Linq
{
    public class OrSpecification<TEntity> : Specification<TEntity> where TEntity : class
    {
        private ISpecification<TEntity> _left;
        private ISpecification<TEntity> _right;

        public OrSpecification(ISpecification<TEntity> s1, ISpecification<TEntity> s2)
        {
            _left = s1;
            _right = s2;
        }

        public override string Description { get { return "(" + _left.Description +") OR " + _right.Description; } } 

        public override Expression<Func<TEntity, bool>> predicate
        {
            get
            {
                return this.Compose(_left.predicate, _right.predicate, Expression.Or);
            }
            /* Not supported on Entity Framework
            get
            {
                var lvParam = Expression.Parameter(typeof(TEntity), "obj");

                var lvNewExpression = Expression.Lambda<Func<TEntity, bool>>(
                    Expression.OrElse(
                        Expression.Invoke(_left.predicate, lvParam),
                        Expression.Invoke(_right.predicate, lvParam)
                    ),
                    lvParam
                );

                return lvNewExpression;
            }
             */
        }

    }
}
NotSpecification.cs
How to do it
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace DesignPatterns.Specification.Linq
{
    public class NotSpecification<TEntity> : Specification<TEntity> where TEntity : class
    {
        private ISpecification<TEntity> _wrapped;

        public NotSpecification(ISpecification<TEntity> s)
        {
            _wrapped = s;
        }

        public override string Description { get { return "NOT (" + _wrapped.Description + ")"; } } 

        public override Expression<Func<TEntity, bool>> predicate
        {
            get
            {
                return Expression.Lambda<Func<TEntity, bool>>(
                    Expression.Not(
                        Expression.Invoke(_wrapped.predicate, _wrapped.predicate.Parameters.Single())
                    ),
                    _wrapped.predicate.Parameters.Single()
                );
            }
            /* No supported on Entity Framework
            get
            {
                var lvParam = Expression.Parameter(typeof(TEntity), "obj");

                var lvNewExpression = Expression.Lambda<Func<TEntity, bool>>(
                    Expression.Not(
                        Expression.Invoke(_wrapped.predicate, lvParam)
                    ),
                    lvParam
                );

                return lvNewExpression;
            }
            */
        }

    }
}
ParameterRebinder.cs
How to do it
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace DesignPatterns.Specification.Linq
{
    internal class ParameterRebinder : ExpressionVisitor
    {
        private readonly Dictionary<ParameterExpression, ParameterExpression> map;

        public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
        {
            this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
        }

        public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
        {
            return new ParameterRebinder(map).Visit(exp);
        }

        protected override Expression VisitParameter(ParameterExpression p)
        {
            ParameterExpression replacement;
            if (map.TryGetValue(p, out replacement))
            {
                p = replacement;
            }
            return base.VisitParameter(p);
        }
    }
}
Extension methods
How to do it
namespace DesignPatterns.Extensions
{
    public static class SpecificationExtensions
    {
        public static ISpecification<TEntity> And<TEntity>(this ISpecification<TEntity> s1, ISpecification<TEntity> s2)
            where TEntity : class
        {
            return new AndSpecification<TEntity>(s1, s2);
        }

        public static ISpecification<TEntity> Or<TEntity>(this ISpecification<TEntity> s1, ISpecification<TEntity> s2)
            where TEntity : class
        {
            return new OrSpecification<TEntity>(s1, s2);
        }

        public static ISpecification<TEntity> Not<TEntity>(this ISpecification<TEntity> s)
            where TEntity : class
        {
            return new NotSpecification<TEntity>(s);
        }
    }
}