有没有一种方法可以更容易地将表达式和lambdas组合在一起

本文关键字:表达式 lambdas 在一起 组合 更容易 一种 方法 有没有 | 更新日期: 2023-09-27 18:00:00

假设我有一个Expression<Func<Foo, Bar>> calculateBar,它将Bar"还原"为Foo,我可以这样使用:

IQueryable foo = getFoos();
bars = foo.Select(calculateBar);

然而,我有时需要能够引用输入Foo,所以我想包装calculateBar,这样它就可以返回Tuple<Foo, Bar>:

public static Expression<Func<TIn, Tuple<TIn, TOut>>> WithInput<TIn, TOut>(
    this Expression<Func<TIn, TOut>> expression)
{
    var param = Expression.Parameter(typeof(TIn));
    var constructor = typeof(Tuple<TIn, TOut>).GetConstructor(new[] { typeof(TIn), typeof(TOut) });
    if (constructor == null) throw new ArgumentNullException();
    return Expression.Lambda<Func<TIn, Tuple<TIn, TOut>>>(Expression.New(constructor, param, Expression.Invoke(expression, param)), param);
}

现在,这个功能在实践中运行良好。但是,在LINQ to Entities中,构造函数必须是无参数的。所以,相反,我可能想创建一个假的Tuple(new WithInput<Foo, Bar> { Input = theFoo, Output = theBar }),但把它写成一个表达式会很痛苦。

有没有一种方法可以使用Lambda而不是继续构建更多的Expression树,在现有表达式的基础上构建(而不会破坏LINQ to Entities)?

例如(psuedocode):

Expression<Func<Foo, WithInput<Foo, Bar>>> wrapper = foo => new WithInput { Input = foo, Output = Expression.Invoke(calculateBar, foo) };

有没有一种方法可以更容易地将表达式和lambdas组合在一起

与编写Tuple相比,编写MemberInit表达式并不那么痛苦。为了记录在案,它应该是这样的:

public static Expression<Func<TIn, WithInput<TIn, TOut>>> WithInput<TIn, TOut>(
    this Expression<Func<TIn, TOut>> expression)
{
    var parameter = expression.Parameters[0];
    var resultType = typeof(WithInput<TIn, TOut>);
    var body = Expression.MemberInit(Expression.New(resultType),
        Expression.Bind(resultType.GetProperty("Input"), parameter),
        Expression.Bind(resultType.GetProperty("Output"), expression.Body));
    return Expression.Lambda<Func<TIn, WithInput<TIn, TOut>>>(body, parameter);
}

现在谈正题。如果不使用某些自定义表达式处理实用程序库(您自己的或第三方的),就不可能基于现有lambda构建表达式。

例如,LINQKit提供了InvokeExpand扩展方法,它们可以这样使用:

using LinqKit;
public static Expression<Func<TIn, WithInput<TIn, TOut>>> WithInput<TIn, TOut>(
    this Expression<Func<TIn, TOut>> expression)
{
    return Linq.Expr((TIn input) => new WithInput<TIn, TOut>
    {
        Input = input,
        Output = expression.Invoke(input)
    }).Expand();
}