Composing Expressions in C#
This post follows on from Reusing predicates in Entity Framework.
In my previous post I introduced the "and" extension method which allowed me to create a new entity framework safe expression from two existing expressions. Here's how we make that happen:
The code is from a blogpost from Colin Meek from 2008: InvocationExpression and LINQ to Entities.
I'll just dump my version of the code here. The idea is exactly the same (even using the visitor as an immutable stack):
So, now we've got that, we can create our extensions for Expressions:
With that code in place, we can do:
And it will all just work!
In my previous post I introduced the "and" extension method which allowed me to create a new entity framework safe expression from two existing expressions. Here's how we make that happen:
The code is from a blogpost from Colin Meek from 2008: InvocationExpression and LINQ to Entities.
I'll just dump my version of the code here. The idea is exactly the same (even using the visitor as an immutable stack):
public class InvocationExpander : ExpressionVisitor { private readonly ParameterExpression _parameter; private readonly Expression _expansion; private readonly InvocationExpander _previous; public static T Expand<T>(T expr) where T : LambdaExpression { return (T)new InvocationExpander().Visit(expr); } public InvocationExpander() { } private InvocationExpander(ParameterExpression parameter, Expression expansion, InvocationExpander previous) { _parameter = parameter; _expansion = expansion; _previous = previous; } public InvocationExpander Push(ParameterExpression parameter, Expression expansion) { return new InvocationExpander(parameter, expansion, this); } protected override Expression VisitInvocation(InvocationExpression iv) { if (iv.Expression.NodeType != ExpressionType.Lambda) { return base.VisitInvocation(iv); } var lambda = (LambdaExpression)iv.Expression; return lambda .Parameters .Select((x, i) => new { Parameter = x, Expansion = iv.Arguments[i] }) .Aggregate(this, (previous, pair) => previous.Push(pair.Parameter, pair.Expansion)) .Visit(lambda.Body); } protected override Expression VisitParameter(ParameterExpression p) { var expander = this; while (null != expander) { if (expander._parameter == p) { return base.Visit(expander._expansion); } expander = expander._previous; } return base.VisitParameter(p); } }
So, now we've got that, we can create our extensions for Expressions:
// https://github.com/juanplopes/simple/blob/master/src/Simple/Expressions/PredicateBuilder.cs
public static class ExpressionExtensions { public static Expression<Func<T, bool>> True<T>() { return True<T>(Expression.Parameter(typeof(T), "f")); } public static Expression<Func<T, bool>> True<T>(ParameterExpression param) { return Expression.Lambda<Func<T, bool>>(Expression.Constant(true), param); } public static Expression<Func<T, bool>> False<T>() { return False<T>(Expression.Parameter(typeof(T), "f")); } public static Expression<Func<T, bool>> False<T>(ParameterExpression param) { return Expression.Lambda<Func<T, bool>>(Expression.Constant(false), param); } public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) { var invokedExpr = Expression.Invoke(second, first.Parameters); return Expression.Lambda<Func<T, bool>> (Expression.AndAlso(first.Body, invokedExpr), first.Parameters).ExpandInvocations(); } public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) { var invokedExpr = Expression.Invoke(second, first.Parameters); return Expression.Lambda<Func<T, bool>> (Expression.OrElse(first.Body, invokedExpr), first.Parameters).ExpandInvocations(); } public static Expression<T> ExpandInvocations<T>(this Expression<T> expr) { return InvocationExpander.Expand(expr); } }
With that code in place, we can do:
And it will all just work!
Comments
Post a Comment