C#表达式树Expression基础讲解
什么是表达式树
表达式树以树形数据结构表示代码,其中每一个节点都是一种表达式,比如方法调用和 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) }
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();
通过代码创建表达式树
- 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基础讲解的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: 这样一来二往就认识了