You can use the following helper functions (one could probably give them a better names, but that's not essential):
public static class ExpressionUtils
{
public static Expression<Func<TOuter, TResult>> Bind<TOuter, TInner, TResult>(this Expression<Func<TOuter, TInner>> source, Expression<Func<TInner, TResult>> resultSelector)
{
var body = new ParameterExpressionReplacer { source = resultSelector.Parameters[0], target = source.Body }.Visit(resultSelector.Body);
var lambda = Expression.Lambda<Func<TOuter, TResult>>(body, source.Parameters);
return lambda;
}
public static Expression<Func<TOuter, TResult>> ApplyTo<TInner, TResult, TOuter>(this Expression<Func<TInner, TResult>> source, Expression<Func<TOuter, TInner>> innerSelector)
{
return innerSelector.Bind(source);
}
class ParameterExpressionReplacer : ExpressionVisitor
{
public ParameterExpression source;
public Expression target;
protected override Expression VisitParameter(ParameterExpression node)
{
return node == source ? target : base.VisitParameter(node);
}
}
}
Let see how the sample expression
c.Criteria = x => x.Name.StartsWith("SomeTexts");
can be built from the two different parts.
If you have
Expression<Func<Customer, string>> e = x => x.Name;
then
c.Criteria = e.Bind(x => x.StartsWith("SomeTexts"));
or if you have this instead
Expression<Func<string, bool>> e = x => x.StartsWith("SomeTexts");
then
c.Criteria = e.ApplyTo((Customer x) => x.Name);
If you have both expressions, then you can use any of the two functions, since a.Bind(b)
is equivalent to b.ApplyTo(a)
.