2

If I have a specification defined as an Expression as below:

public Expression<Func<Foo, bool>> IsSuperhuman = 
  x => x.CanFly && x.HasXRayVision;

And I want to define another specification 'IsSuperheroine' with the logic 'is superhuman and is female', how can I reuse the existing specification within the new one?

leppie
  • 115,091
  • 17
  • 196
  • 297
David
  • 15,750
  • 22
  • 90
  • 150
  • 1
    Why are you using Expressions for this? It would seem a delegate would suffice. – leppie Aug 20 '10 at 10:36
  • 3
    Because you can't use simple delegates in the Where clause of IQueryables, as I discovered yesterday. – David Aug 20 '10 at 11:29

2 Answers2

1

Have you checked out predicate builder in LinqKit? It builds up expressions by letting you and and or expressions together.

Mant101
  • 2,705
  • 1
  • 23
  • 27
  • I believe that NCommon has something similar but I'm down with rolling my own at the moment. – David Aug 20 '10 at 11:21
  • I'm an absolute spanner. If I'd read your link properly, it could have saved me loads of work. You are a god! – David Mar 17 '11 at 10:37
1

Here's a way to do it :

Expression<Func<Foo, bool>> IsSuperhuman = x => x.CanFly && x.HasXRayVision;

Expression<Func<Foo, bool>> IsSuperheroine = AndAlso(IsSuperhuman, x => x.IsFemale);

...

public static Expression<Func<T, TResult>> AndAlso<T, TResult>(Expression<Func<T, TResult>> expr1, Expression<Func<T, TResult>> expr2)
{
    var arg = Expression.Parameter(typeof(T), expr1.Parameters[0].Name);
    var andExpr = Expression.AndAlso(
        ReplaceParameter(expr1.Body, expr1.Parameters[0], arg),
        ReplaceParameter(expr2.Body, expr2.Parameters[0], arg));
    return Expression.Lambda<Func<T, TResult>>(andExpr, arg);
}

public static Expression ReplaceParameter(Expression expr, ParameterExpression oldParam, ParameterExpression newParam)
{
    return new ReplaceParameterVisitor(oldParam, newParam).Visit(expr);
}

internal class ReplaceParameterVisitor : ExpressionVisitor
{
    private ParameterExpression _oldParam;
    private ParameterExpression _newParam;

    public ReplaceParameterVisitor(ParameterExpression oldParam, ParameterExpression newParam)
    {
        _oldParam = oldParam;
        _newParam = newParam;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        if (node == _oldParam)
            return _newParam;
        return node;
    }
}

It is probably not the simplest way to do it, but it works...

Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
  • Thanks! I'm going to take your word for it until such a time as I've been through the Expressions API and worked out what it all means. – David Aug 25 '10 at 14:07