You can compose your lambdas together using an expression visitor, without any reflection necessary. Using a Compose
extension method (that you can check out below) you can chain together multiple expressions into a single expression:
// Your original lambdas
Expression<Func<A, IEnumerable<B>>> listGetter = a => a.List;
Expression<Func<B, string>> propGetter = b => b.SomeString;
// Stitch them together
var firstSomeStringGetter = listGetter.Compose(seq => seq.First()).Compose(propGetter);
You can test this out like so:
// Test out what the body of the expression is
Console.WriteLine(firstSomeStringGetter.Body.ToString()); // Outputs "a.List.First().SomeString"
// Test out invoking the expression
var result = firstSomeStringGetter.Compile()(new A());
Console.WriteLine(result); // Outputs "Foo"
Here's the classes I'm using that correspond to your TModel
, TList
and TListValue
:
class A
{
public IEnumerable<B> List
{
get
{
yield return new B();
}
}
}
class B
{
public string SomeString
{
get { return "Foo"; }
}
}
Here's the helper classes used to implement Compose
:
// https://stackoverflow.com/users/1117815/felipe
static class FunctionalExtensions
{
public static Expression<Func<A, C>> Compose<A, B, C>(this Expression<Func<A, B>> f, Expression<Func<B, C>> g)
{
var ex = SubstituteIn(g.Body, g.Parameters[0], f.Body);
return Expression.Lambda<Func<A, C>>(ex, f.Parameters[0]);
}
static TExpr SubstituteIn<TExpr>(TExpr expression, Expression orig, Expression replacement) where TExpr : Expression
{
var replacer = new SwapVisitor(orig, replacement);
return replacer.VisitAndConvert(expression, "SubstituteIn");
}
}
// https://stackoverflow.com/users/23354/marc-gravell
class SwapVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public SwapVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
Credit where it's due: