如何在没有调用的情况下合并两个C#Lambda表达式?

我想合并以下表达式:

// example class class Order { List Lines } class OrderLine { } Expression<Func<Order, List>> selectOrderLines = o => o.Lines; Expression<Func<List, Boolean>> validateOrderLines = lines => lines.Count > 0; // now combine those to Expression<Func> validateOrder; 

我使用selectOrderLines上的调用并将结果提供给validateOrderLines,但是因为我在Entity Framework中使用这些表达式,我必须实际创建一个干净的表达式,它应该代表:

 Expression<Func> validateOrder = o => o.Lines.Count > 0; 

我怎样才能做到这一点?

最优雅的方式是使用表达式访问者 。 特别是,这个MSDN博客条目描述了如何使用它来组合谓词(使用布尔和或或)而不需要调用。

编辑实现布尔组合不是你想要的,我写了一个ExpressionVisitor的示例用法,它解决了你的特定问题:

 public class ParameterToMemberExpressionRebinder : ExpressionVisitor { ParameterExpression _paramExpr; MemberExpression _memberExpr; ParameterToMemberExpressionRebinder(ParameterExpression paramExpr, MemberExpression memberExpr) { _paramExpr = paramExpr; _memberExpr = memberExpr; } protected override Expression Visit(Expression p) { return base.Visit(p == _paramExpr ? _memberExpr : p); } public static Expression> CombinePropertySelectorWithPredicate( Expression> propertySelector, Expression> propertyPredicate) { var memberExpression = propertySelector.Body as MemberExpression; if (memberExpression == null) { throw new ArgumentException("propertySelector"); } var expr = Expression.Lambda>(propertyPredicate.Body, propertySelector.Parameters); var rebinder = new ParameterToMemberExpressionRebinder(propertyPredicate.Parameters[0], memberExpression); expr = (Expression>)rebinder.Visit(expr); return expr; } class OrderLine { } class Order { public List Lines; } static void test() { Expression>> selectOrderLines = o => o.Lines; Expression, Boolean>> validateOrderLines = lines => lines.Count > 0; var validateOrder = ParameterToMemberExpressionRebinder.CombinePropertySelectorWithPredicate(selectOrderLines, validateOrderLines); // validateOrder: {o => (o.Lines.Count > 0)} } } 

此扩展程序有效:

 public static class Utility { public static Expression Compose(this Expression first, Expression second, Func 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 => ps, p => pf); // 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(merge(first.Body, secondBody), first.Parameters); } public static Expression> And(this Expression> first, Expression> second) { return first.Compose(second, Expression.And); } public static Expression> Or(this Expression> first, Expression> second) { return first.Compose(second, Expression.Or); } } 

样品使用:

 Expression> filter1 = p => a.ProductId == 1; Expression> filter2 = p => a.Text.StartWith("test"); Expression> filterCombined = filter1.And(filter2);