【问题标题】:Can a LINQ Expression defined as a lambda expression include other LINQ Expressions?定义为 lambda 表达式的 LINQ 表达式能否包含其他 LINQ 表达式?
【发布时间】:2018-09-29 19:17:05
【问题描述】:

使用 LINQ 表达式时,C# 编译器会方便地将 C# lambda 转换为 Expression 对象:

//using System;
//using System.Linq.Expressions;

Expression<Func<int, bool>> lambda_expression = (int x) => x == 3;

这很方便,与显式构造表达式相比,可以节省大量输入:

Expression<Func<int, bool>> explicit_expression_object;
{
    var x = Expression.Parameter(typeof(int), "x");
    explicit_expression =
        Expression.Lambda<Func<int, bool>>(Expression.Equal(x, Expression.Constant(3)), x);
}

但是,在某些情况下,需要使用“普通”表达式对象语法,例如在运行时动态创建表达式时。因此,我目前发现自己混合使用“表达式 lambda”和动态生成的“显式”表达式对象。

是否可以将 Expression 对象“包含”或“嵌入”到表达式 lambda 中?

例如:

Expression inner_expression_object = Expression.Constant(3);

Expression<Func<int, bool>> wrapper_expression =
    (int x) => x == inner_expression_object.Embed();

【问题讨论】:

  • 看看这个(堆栈溢出)答案stackoverflow.com/a/24403382/9204431>
  • Related, almost a duplicate。剧透:不,你必须手动组合它们。
  • 您可以为此写一个ExpressionVisitor,类似LINQKit 用于查询。
  • 请注意,Embed 无法知道正确的类型,所以我认为您需要Embed&lt;T&gt; 才能工作。
  • 虽然如果有提供返回类型的 Expression 对象的通用变体会很好。

标签: c# linq lambda linq-expressions


【解决方案1】:

使用ExpressionVisitor,您可以用Expression 替换对Expression 扩展方法的调用。

首先,您需要一个 ExpressionVisitor 类来扩展 Embed 方法调用到它们的值:

public class EmbedVisitor : ExpressionVisitor {
    public override Expression Visit(Expression node) {
        if (node?.NodeType == ExpressionType.Call) {
            var callnode = node as MethodCallExpression;
            if (callnode.Method.Name == "Embed" && callnode.Method.DeclaringType == typeof(ExpressionExt))
                return callnode.Arguments[0].Evaluate<Expression>();
        }

        return base.Visit(node);
    }
}

那么你需要一个静态类来提供所需的扩展方法:

public static class ExpressionExt {
    public static T Embed<T>(this Expression e) {
        return default(T);
    }

    public static Expression ExpandEmbed(this Expression orig) => new EmbedVisitor().Visit(orig);

    public static T Evaluate<T>(this Expression e) {
        //A little optimization for constant expressions
        if (e.NodeType == ExpressionType.Constant)
            return (T)((ConstantExpression)e).Value;
        else
            return (T)Expression.Lambda(e).Compile().DynamicInvoke();
    }
}

现在您可以使用这些扩展嵌入的 Expression 值子表达式:

var inner_expression_object = Expression.Constant(3);

Expression<Func<int, bool>> wrapper_expression =
    (int x) => x == inner_expression_object.Embed<int>();

var expanded = wrapper_expression.ExpandEmbed();
// Expression<Func<int,bool>> expanded == (int x) => x == 3;

也可以直接嵌入Expression表达式并展开:

Expression<Func<int,bool>> wrap2 = x => x == Expression.Multiply(Expression.Constant(4), Expression.Constant(8)).Embed<int>();
var expanded2 = wrap2.ExpandEmbed();
// Expression<Func<int,bool>> expanded2 = x => x == 4*8;

【讨论】:

  • 喜欢您的解决方案,但我不清楚各种实际用例,请您详细说明一些
  • 还有深入了解表达式树的好资源
  • 尝试使用expanded.Evaluate&lt;int&gt;(); 之类的代码,但这会导致以下行中的无效转换异常return (T)Expression.Lambda(e).Compile().DynamicInvoke();
  • @MrinalKamboj 以及 expanded 中的哪些内容会评估为 int
  • @MrinalKamboj Here 是一篇关于使用 LINQ to Entities(或 SQL)的用例的文章。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-21
  • 2016-10-01
  • 1970-01-01
相关资源
最近更新 更多