00 | 什么是表达式树

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

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

[HttpGet]
public IActionResult Expression()
{
// 查询 年龄Age 大于 18 的元素
Expression<Func<User,bool>> 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<Func<User, bool>> 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<Func<User, bool>> {
NodeType = ExpressionType.Lambda,
Type = typeof(Func<User, bool>),
Parameters = new ReadOnlyCollection<ParameterExpression> {
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)
}

01 | Expression 和 Func 的区别

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

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

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

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

02 | 通过代码创建表达式树

  • ParameterExpression
  • BinaryExpression
  • MethodCallExpression
  • ConstantExpression

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

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

一般构建步骤:

  • 先创建 ParameterExpression
  • 接着由里到外逐步构建
    • 先左节点(Left)
    • 后右节点(Right)
    • 接着Body节点
  • 将其拼接成 Expression
public IActionResult GetUserByManualExpression()
{
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<Func<User, bool>> expression = Expression.Lambda<Func<User, bool>>(body, parameters: parameterExpression); var data = _userService.GetUsers(expression); return Ok(new
{
code = 200,
msg = "OK",
data
});
}

[C# Expression] 之基础概念的更多相关文章

  1. JavaScript--我发现,原来你是这样的JS(基础概念--躯壳,不妨从中文角度看js)

    介绍 这是红宝书(JavaScript高级程序设计 3版)的读书笔记第二篇(基础概念--躯壳篇),有着部分第三章的知识内容,当然其中还有我个人的理解.红宝书这本书可以说是难啃的,要看完不容易,挺厚的, ...

  2. JS--我发现,原来你是这样的JS(二)(基础概念--躯壳篇--不妨从中文角度看js)

    一.介绍 这是红宝书(JavaScript高级程序设计 3版)的读书笔记第二篇(基础概念--躯壳篇),有着部分第三章的知识内容,当然其中还有我个人的理解. 红宝书这本书可以说是难啃的,要看完不容易,挺 ...

  3. 第214天:Angular 基础概念

    一.Angular 简介 1. 什么是 AngularJS - 一款非常优秀的前端高级 JS 框架 - 最早由 Misko Hevery 等人创建 - 2009 年被 Google 公式收购,用于其多 ...

  4. 【Machine Learning】机器学习及其基础概念简介

    机器学习及其基础概念简介 作者:白宁超 2016年12月23日21:24:51 摘要:随着机器学习和深度学习的热潮,各种图书层出不穷.然而多数是基础理论知识介绍,缺乏实现的深入理解.本系列文章是作者结 ...

  5. TCP/IP基础概念及通信过程举例

    TCP/IP基础概念及通信过程举例 出现 上个世纪60年代,由于中央集中式网络的容灾性较弱,以美国国防部为中心的一家组织研究出分组交换网络.后来为了验证分组交换技术的实用性,ARPANET出现了,并且 ...

  6. Jmeter基础之---jmeter基础概念

    Jmeter基础之---jmeter基础概念 JMeter 介绍: 一个非常优秀的开源的性能测试工具. 优点:你用着用着就会发现它的重多优点,当然不足点也会呈现出来. JMeter 介绍: 一个非常优 ...

  7. 快速入门系列--WCF--01基础概念

    转眼微软的WCF已走过十个年头,它是微软通信框架的集大成者,将之前微软所有的通信框架进行了整合,提供了统一的应用方式.记得从自己最开始做MFC时,就使用过Named Pipe命名管道,之后做Winfo ...

  8. 理解 angular2 基础概念和结构 ----angular2系列(二)

    前言: angular2官方将框架按以下结构划分: Module Component Template Metadata Data Binding Directive Service Dependen ...

  9. JavaBean 基础概念、使用实例及代码分析

    JavaBean 基础概念.使用实例及代码分析 JavaBean的概念 JavaBean是一种可重复使用的.且跨平台的软件组件. JavaBean可分为两种:一种是有用户界面的(有UI的):另一种是没 ...

随机推荐

  1. Codeforces 1406E - Deleting Numbers(根分+数论)

    Codeforces 题面传送门 & 洛谷题面传送门 一道个人感觉挺有意思的交互题,本人一开始想了个奇奇怪怪的做法,还以为卡不进去,结果发现竟然过了,而且还是正解( 首先看到这类题目可以考虑每 ...

  2. 洛谷 P6499 - [COCI2016-2017#2] Burza(状压 dp)

    题面传送门 一道挺有意思的思维题(?) 首先我们假设根节点深度为 \(0\),那么 Daniel 的目标显然就是堵住一些节点使得 Stjepan 不能移动到深度为 \(k\) 的节点,Stjepan ...

  3. android 点击图片从Fragment跳转到activity

    android 点击图片从Fragment跳转到activity 在Fragment里编写 public View onCreateView(@NonNull LayoutInflater infla ...

  4. C#gridview颜色提示

    OnRowCreated="gridStatistic_RowCreated private void FillUI() { gridStatistic.DataSource = dtSta ...

  5. 18. MYSQL 字符编码配置

    MYSQL 5.7版本的my.ini 在C盘隐藏文件夹下 C:\ProgramData\MySQL\MySQL Server 5.7 [client] default-character-set=ut ...

  6. hashtable深度探索

    1.什么是哈希表(hashtable)?为什么要发明哈希表? 首先回答第二个问题,在之前的数据结构中我们学习了数组,链表,二叉树等数据结构,记录在结构中的相对位置是随机的,和记录的关键字之前不存在确定 ...

  7. Oracle—数据库名、数据库实例名、数据库域名、数据库服务名的区别

    Oracle-数据库名.数据库实例名.数据库域名.数据库服务名的区别 一.数据库名 1.什么是数据库名       数据库名就是一个数据库的标识,就像人的身份证号一样.他用参数DB_NAME表示,如果 ...

  8. 【Linux】【Shell】【Basic】流程控制

    1. 选择执行: 1.1. if 单分支的if语句: if 测试条件 then 代码分支 fi 双分支的if语句: if 测试条件; then 条件为真时执行的分支 else 条件为假时执行的分支 f ...

  9. vue2 安装打包部署

    vue2项目搭建记录 mkdir -p /opt/wks/online_pre/1006cd /opt/wks/online_pre/1006mkdir hongyun-ui /opt/code/vu ...

  10. layui 弹窗中 分页展示table

    1. 需求:点击查看更多,展示该类别 所有数据,并分页 2. 参考文档: (1)https://www.jianshu.com/p/40da11ebae66 (2) https://blog.csdn ...