千家信息网

C#表达式树Expression怎么创建

发表于:2025-01-17 作者:千家信息网编辑
千家信息网最后更新 2025年01月17日,本篇内容介绍了"C#表达式树Expression怎么创建"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成
千家信息网最后更新 2025年01月17日C#表达式树Expression怎么创建

本篇内容介绍了"C#表达式树Expression怎么创建"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

什么是表达式树

表达式树以树形数据结构表示代码,其中每一个节点都是一种表达式,比如方法调用和 x < y 这样的二元运算等。可以对表达式树中的代码进行编辑和运算。 这样能够动态修改可执行代码、在不同数据库中执行 LINQ 查询以及创建动态查询。 表达式树还能用于动态语言运行时 (DLR) 以提供动态语言和 .NET 之间的互操作性,同时保证编译器编写员能够发射表达式树而非 Microsoft 中间语言 (MSIL)。 这段话是来自官网( [表达式树 (C#) | Microsoft Docs](表达式树 (C#) | Microsoft Docs) )的定义。

在 C# 中,我们可以通过 Expression 的方式来手动创建表达式树,比如:

[HttpGet]public IActionResult _Expression(){    // 查询 年龄Age 大于 18 的元素    Expression> expression1 = x => x.Age > 18;     return Ok();}

那么,x.Age > 18 这一表达式,它的树状结构是这样的:

通过 Visual Studio 自带的查看变量或添加监视的方式,我们可以发现其中 树的根节点(NodeType)是 GreaterThan,左节点(Left)是 x.Age,右节点(Right)是 18。所以由此就可以大概画出树状结构。

最后,通过这种树状结构,C# 就可以帮我们将表达式编译成具体的 SQL 执行语句。

如果想更清晰的查看表达式树的结构,可以 nuget 一个包( ExpressionTreeToString ),将表达式结构转换成字符串

PM> Install-Package ZSpitz.Util -Version 0.1.116
Expression> expression = u => u.Age >= 18;var treeStr = expression.ToString("Object notation", "C#");// 输出为下面字符串var u = new ParameterExpression {    Type = typeof(User),    IsByRef = false,    Name = "u"};new Expression> {    NodeType = ExpressionType.Lambda,    Type = typeof(Func),    Parameters = new ReadOnlyCollection {        u    },    Body = new BinaryExpression {        NodeType = ExpressionType.GreaterThanOrEqual,        Type = typeof(bool),        Left = new MemberExpression {            Type = typeof(int),            Expression = u,            Member = typeof(User).GetProperty("Age")        },        Right = new ConstantExpression {            Type = typeof(int),            Value = 18        }    },    ReturnType = typeof(bool)}

Expression 和 Func 的区别

  • Expression 存储了运算逻辑,可以将其保存成抽象语法树(AST),可以在运行时动态获取运算逻辑。

  • Func 只是存储了结果,无法保存成语法树,也无法动态获取运算逻辑。

所以,在 EFCore 中,使用表达式对数据库数据进行查询中,我们应该选择 Expression 而不是 Func,因为使用了 Func ,实际上并无法将 Func 中的表达式转换成 SQL,而是在将所有数据加载到内存后,在内存中在过滤 Func 中的条件。

简单来说就是,此时要筛选 User 表中年龄大于18的数据,可以有这两种写法

// 这种写法,实际生成的 SQL 语句, 大概是这样的 SELECT * FROM User as T WHERE T.age > 18Expression> expression1 = x => x.Age > 18;dbContext.User.Where(expression1).toList();// 而这种, 生成的语句是这样的 SELECT * FROM User, 然后将 User 表中所有数据加载到内存中后, 在进行 age > 18 的过滤Func func1 = x => x.Age > 18;dbContext.User.Where(func1).toList();

通过代码创建表达式树

  • ParameterExpression

  • BinaryExpression

  • MethodCallExpression

  • ConstantExpression

这些类几乎都没有提供构造方法,而且所有的属性都几乎只是只读。因此我们一般不会直接创建这些类的实例,而是调用 Expression 类的 Parameter、MakeBinary、Call、Constant等静态方法来生成,这些静态方法我们一般称作创建表达式树的工厂方法,而属性则通过方法参数类设置。

动态将表达式:u => u.Age >= 18; 通过代码构建出来

一般构建步骤:

  • 先创建 ParameterExpression

  • 接着由里到外逐步构建

    • 先左节点(Left)

    • 后右节点(Right)

    • 接着Body节点

  • 将其拼接成 Expression

public IActionResult GetUserByManual_Expression(){    ParameterExpression parameterExpression = Expression.Parameter(type:typeof(User), name: "u");    ConstantExpression right = Expression.Constant(18);    MemberExpression left = Expression.MakeMemberAccess(parameterExpression, member: typeof(User).GetProperty("Age"));    BinaryExpression body = Expression.GreaterThanOrEqual(left, right);    Expression> expression = Expression.Lambda>(body, parameters: parameterExpression);    var data = _userService.GetUsers(expression);    return Ok(new    {        code = 200,        msg = "OK",        data    });}

"C#表达式树Expression怎么创建"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

0