我有2个表达式:
Expression<Func<TModel, IEnumerable<TList>>> list = model => list;
Expression<Func<TList, TListValue>> valueProperty = listEntry => listEntry.property;
现在我需要将它们组合成一个看起来像这样的表达式:
model => model.list.First().property
这需要与ASP.Net MVC DisplayFor(...)
我尝试使用Invoke组合它们,但这会在调用DisplayFor时导致异常。
答案 0 :(得分:2)
您可以使用表达式访问者将lambda组合在一起,而无需任何反射。使用Compose
扩展方法(您可以在下面查看),您可以将多个表达式链接到一个表达式中:
// 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);
您可以这样测试:
// 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"
以下是我正在使用的与您的TModel
,TList
和TListValue
对应的类:
class A
{
public IEnumerable<B> List
{
get
{
yield return new B();
}
}
}
class B
{
public string SomeString
{
get { return "Foo"; }
}
}
以下是用于实现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);
}
}
信用到期日:
答案 1 :(得分:1)
Expression<Func<TModel, IEnumerable<TList>>> exp1 = model => model.list;
Expression<Func<TList, TListValue>> exp2 = listEntry => listEntry.property;
// get method info of "IEnumerable<>.First()", i.e. "Enumerable.First(this IEnumerable<>)"
MethodInfo first =
typeof (System.Linq.Enumerable).GetMethods()
.Where(m => m.Name == "First" && m.GetParameters().Count() == 1)
.Single()
.MakeGenericMethod(new Type[]{typeof(TList)});
var body = Expression.Property(
Expression.Call(null, first, exp1.Body), // model.list.First()
(exp2.Body as MemberExpression).Member as PropertyInfo // get property info of "TList.property"
);
Expression<Func<TModel, TListValue>> result = Expression.Lambda<Func<TModel, TListValue>>(
body,
exp1.Parameters
);