2010-08-20 8 views
2

Si j'ai une spécification définie comme une expression comme ci-dessous:modèle Spécifications - création de spécifications composés à l'aide lambdas (C#)

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

Et je veux définir une autre spécification « IsSuperheroine » avec la logique « est surhumaine et est féminin », comment puis-je réutiliser la spécification existante dans le nouveau?

+1

Pourquoi utiliser des expressions-vous pour cela? Il semblerait qu'un délégué suffirait. – leppie

+3

Parce que vous ne pouvez pas utiliser des délégués simples dans la clause Where de IQueryables, comme je l'ai découvert hier. – David

Répondre

1

Avez-vous vérifié predicate builder dans LinqKit? Il construit des expressions en vous laissant et/ou des expressions ensemble.

+0

Je crois que NCommon a quelque chose de similaire mais je suis en train de rouler le mien pour le moment. – David

+0

Je suis une clé absolue. Si j'avais bien lu votre lien, cela aurait pu me sauver beaucoup de travail. Tu es un dieu! – David

1

Voici une façon de le faire:

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; 
    } 
} 

Il est sans doute pas la façon la plus simple de le faire, mais ça marche ...

+0

Merci! Je vais vous croire sur parole jusqu'à ce que j'utilise l'API Expressions et que je comprenne ce que tout cela signifie. – David