结合两个Linq lambda表达式

Expression<Func> fn1 = x => x.PossibleSubPath.MyStringProperty; Expression<Func> fn2 = x => x.Contains("some literal"); 

有没有办法创建一个新的lambda表达式,它基本上使用fn1的输出并将其用作fn2的输入?

 Expression<Func> fnCombined = ... 

我知道我可以立即创建这个函数,但问题是我正在制作一些通用代码,因此真的需要能够分别创建这两个函数,然后将它们组合在一起,以便Linq可以使用它们我的数据库对象(entity framework)。

所以逻辑上我们想要做的是创建一个新的lambda,其中有一个第一个函数输入的参数,一个用该参数调用第一个函数然后将结果作为参数传递给第二个函数,然后返回。

我们可以使用Expression对象轻松复制它:

 public static Expression> Combine( Expression> first, Expression> second) { var param = Expression.Parameter(typeof(T1), "param"); var body = Expression.Invoke(second, Expression.Invoke(first, param)); return Expression.Lambda>(body, param); } 

遗憾的是,EF和大多数其他查询提供商都不会真正知道如何处理它并且无法正常运行。 每当他们点击Invoke表达式时,他们通常只会抛出某种exception。 有些人可以处理它。 从理论上讲,他们需要的所有信息都在那里,如果他们的写作具有强大的function来实现它。

然而,我们可以做的是,从概念的角度来看,用我们正在创建的新lambda的参数替换该lambda体中第一个lambda参数的每个实例,然后替换第二个lambda中第二个lambda参数的所有实例。与第一个lambda的新身体。 从技术上讲,如果这些表达式有副作用,并且这些参数不止一次使用,它们就不一样了,但是因为这些将由EF查询提供程序解析,所以它们实际上不应该有副作用。

感谢David B提供了相关问题的链接, 该问题提供了ReplaceVisitor实现。 我们可以使用ReplaceVisitor遍历表达式的整个树,并将一个表达式替换为另一个表达式。 该类型的实现是:

 class ReplaceVisitor : ExpressionVisitor { private readonly Expression from, to; public ReplaceVisitor(Expression from, Expression to) { this.from = from; this.to = to; } public override Expression Visit(Expression node) { return node == from ? to : base.Visit(node); } } 

现在我们可以编写正确的 Combine方法:

 public static Expression> Combine( this Expression> first, Expression> second) { var param = Expression.Parameter(typeof(T1), "param"); var newFirst = new ReplaceVisitor(first.Parameters.First(), param) .Visit(first.Body); var newSecond = new ReplaceVisitor(second.Parameters.First(), newFirst) .Visit(second.Body); return Expression.Lambda>(newSecond, param); } 

和一个简单的测试用例,只是为了演示正在发生的事情:

 Expression> fn1 = x => x.PossibleSubPath.MyStringProperty; Expression> fn2 = x => x.Contains("some literal"); var composite = fn1.Combine(fn2); Console.WriteLine(composite); 

哪个会打印出来:

param => param.PossibleSubPath.MyStringProperty.Contains(“some literal”)

这正是我们想要的; 查询提供程序将知道如何解析类似的东西。