And and Or operations over ISpecification<> types

Mar 23, 2009 at 2:07 PM
Hi,

After playing a bit with ISpecification<> object I found that (logically) you cannot perform & or | operations on ISpecification<> types.
But, the default ISpecification implementation provided with NCommon (Specification<>) overloads this operators, so if I cast my ISpecification<> objects to Specification<> ones I can perform the operation.. But, as you might guess, this is unsafe, as an ISpecification I can receive any proposed implementation, not just ones deriving from Specification<>.

After a bit of though I decided to implement two extension methods allowing And and Or operations over ISpecification<> objects.
Here are my extension methods, just in case they are usefull to anyone:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace NCommon.Specifications
{
    public static class SpecificationExtensions
    {
        /// <summary>
        /// Retuns a new specification adding this one with the passed one.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="rightHand">The right hand.</param>
        /// <param name="leftHand">The left hand.</param>
        /// <returns></returns>
        public static ISpecification<T> And<T>(this ISpecification<T> rightHand, ISpecification<T> leftHand)
        {
            InvocationExpression rightInvoke = Expression.Invoke(rightHand.Predicate,
                                                     leftHand.Predicate.Parameters.Cast<Expression>());
            BinaryExpression newExpression = Expression.MakeBinary(ExpressionType.AndAlso, leftHand.Predicate.Body,
                                                                   rightInvoke);
            return new Specification<T>(
                Expression.Lambda<Func<T, bool>>(newExpression, leftHand.Predicate.Parameters)
                );
        }

        /// <summary>
        /// Retuns a new specification or'ing this one with the passed one.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="rightHand">The right hand.</param>
        /// <param name="leftHand">The left hand.</param>
        /// <returns></returns>
        public static ISpecification<T> Or<T>(this ISpecification<T> rightHand, ISpecification<T> leftHand)
        {
            InvocationExpression rightInvoke = Expression.Invoke(rightHand.Predicate,
                                                     leftHand.Predicate.Parameters.Cast<Expression>());
            BinaryExpression newExpression = Expression.MakeBinary(ExpressionType.OrElse, leftHand.Predicate.Body,
                                                                   rightInvoke);
            return new Specification<T>(
                Expression.Lambda<Func<T, bool>>(newExpression, leftHand.Predicate.Parameters)
                );
        }
    }
}

Greets.
Pablo Ruiz
Coordinator
Mar 25, 2009 at 3:53 AM
I've added this to NCommon under the Extensions namespace. Also applied the appropriate attribution to the code pointing to this post. Thanks Pablo! 

The changes are committed to source.

- Ritesh