表达式树的使用场景是什么?为何LINQ查询转SQL需先转为表达式树?
为什么需要表达式树做中间层,不能直接把查询表达式转SQL
你可以这么理解:你写的查询表达式本质是编程语言的语法糖,写法非常灵活,直接转SQL会面临很多问题,加一层表达式树的好处非常明确:
- 统一逻辑表示:同样的逻辑可以有N种写法,比如
score > 80、80 < score、score >= 81语义完全一致,如果直接从原始代码转SQL,你要写无数分支兼容不同写法;而表达式树会先把所有写法都转换为只表语义的标准树形结构,只要逻辑相同,树的结构就完全一致,后续转SQL只需要处理统一的结构即可,开发量大幅降低。 - 逻辑复用和扩展:表达式树是和具体数据源无关的中间格式,同一份表达式树既可以转SQL Server的查询语句,也可以转MySQL、PostgreSQL的语句,甚至可以转NoSQL的查询规则。如果没有中间层,每种数据源都要单独适配所有查询写法,维护成本极高。
- 方便做查询优化:树形结构非常方便做逻辑优化,比如合并重复条件、剪枝无用逻辑、调整条件顺序提升查询性能,这些操作直接在原始代码上处理难度非常大,在标准化的表达式树上做就很简单。
- 能保留完整的类型信息:你写的查询里经常会调用编程语言的内置方法,比如
score.ToString().Contains("8"),直接从代码文本转SQL根本识别不了这些方法对应的数据库函数;表达式树会完整保留每个节点的类型、方法信息、参数值,转SQL时可以准确映射到对应数据库的语法。
你给出的LINQ代码对应的表达式树结构及转SQL的过程
你给出的代码:
IEnumerable<int> scoreQuery = from score in scores where score > 80 select score;
对应的表达式树是如下的二叉树结构:
根节点(Select方法调用) ├─ 左子节点(Where方法调用) │ ├─ 左子节点(数据源节点:scores集合引用) │ └─ 右子节点(lambda表达式:score => score > 80) │ └─ 二元运算节点(大于运算:GreaterThan) │ ├─ 左子节点(参数节点:score) │ └─ 右子节点(常量节点:80) └─ 右子节点(lambda表达式:score => score) └─ 参数节点(score)
转SQL的过程就是按规则遍历这棵树:
- 从Where节点的左子节点拿到数据源
scores,对应SQL的FROM Scores - 遍历Where节点的条件子节点,识别是大于运算,左是
score字段,右是常量80,对应SQL的WHERE score > 80 - 遍历Select节点的投影子节点,识别是取
score字段,对应SQL的SELECT score - 把三部分拼接起来,最终得到SQL语句:
SELECT score FROM Scores WHERE score > 80
内容的提问来源于stack exchange,提问作者Nika Kurashvili
相关产品推荐
相关产品推荐

