LINQ查询表达式详解(2)——查询表达式的转换
简介
C#在执行LINQ查询表达式的时候,并不会指定其执行语义,而是将查询表达式转换为遵循查询表达式模式的方法的调用。具体而言,查询表达式将转换为以下名称的调用:Where、Select、SelectMany、Join、GroupJoin、OrderBy、OrderByDescending、ThenBy、ThenByDescending、GroupBy、Cast等等。
如同在前文中提到的用扩展方法和Lambda表达式简化LINQ查询表达式一般,这也是对查询表达式的一种转换。简化后的方法其实就是LINQ查询的实际执行。
本文中用来示例的代码,参数设定都沿用上一篇文章的设定。
转换规则简述
从查询表达式到方法调用的转换是一种句法映射,在执行任何类型绑定或重载决策之前发生。该转换可以保证在句法上正确,但不能保证生成语法正确的 C# 代码。转换查询表达式后,以常规方法调用的方式处理生成的方法调用,而这进而可能暴露错误,例如在方法不存在、参数类型错误或方法为泛型方法且类型推断失败这些情况下。
不允许对查询表达式中的范围变量进行赋值。但允许 C# 实现在某些时候以不实施此限制,因为对于此处介绍的句法转换方案,有些时候可能根本无法实施此限制。
某些转换使用由 * 指示的透明标识符注入范围变量。
转换规则讲解
带继续符的select和groupby子句的查询表达式的转换
继续符是指 into 操作符,带有继续符的查询表达式类似如下:
from ···into x ···
转换为
from x in (from ···) ···
示例:
from c in customers
group c by c.Country into g
select new { Country=c.Country , Customer = g.Count()}
转换为
from g in
from c in customers
group c by c.Country
select new { Country = g.Key, CustCount = g.Count() }
最终转换为
customers.
GroupBy(c => c.Country).
Select(g => new { Country = g.Key, CustCount = g.Count() })
含有显式范围变量类型的查询表达式的转换
显式指定范围变量类型的 from 子句
from T x in e
转换为
from x in (e).Cast<
T>
()
显式指定范围变量类型的 join 子句
join T x in e on k1 equals k2
转换为
join x in (e).Cast<
T>
()
示例:
from Customer in customers
where c.City == "London"
select c
转换为
from c in customers.Cast<Customer>()
where c.City == "London"
select c
最终转换为
customers.Cast<Customer>().Where(c=>c.City=="London")
显式范围变量类型对于查询实现非泛型 IEnumerable 接口的集合很有用,但对于实现泛型IEnumerable 接口的集合没什么用处。如果 customers 属于 ArrayList 类型,则在面的示例中即会如此。
退化查询表达式的转换
退化查询表达式,是指选择源元素本身的查询,如:
from c in customers
select c
确保查询表达式的结果永不为源对象本身非常重要,因为这样会向查询的客户端暴露源的类型和标识符。因此,在查询表达式为退化查询的时候,可通过在源上显式调用 Select 来保护直接以源代码形式写入的简并查询。然后,由 Select 实施者及其他查询操作员确保这些方法永远不会返回源对象本身。
退化查询表达式如下:
from x in e select x
转换为
(e).Select(x=>x)
示例:
from c in customers
select c
转换为
customers.Select(c => c)
from、 let、 where、 join 和 orderby 子句的转换
转换规则
带有另一个 from 子句且后接一个 select 子句的查询表达式
from x1 in e1
from x2 in e2
select v
转换为
(e1).SelectMany( x1 => e2 , ( x1 , x2 ) => v )
带有另一个 from 子句且后接一个 select 子句的查询表达式
from x1 in e1
from x2 in e2
···
转换为
from *
in (e1).SelectMany( x1 => e2 , ( x1 , x2 ) => new { x1 , x2 })
带有 let 子句的查询表达式
from x in e
let y=f
转换为
from *
in (e).Select( x => new { x , y = f })
带有 where 子句的查询表达式
from x in e
where f
···
转换为
from x in (e).Where( x => f )
带有 join 子句(不含 into)且后接 select 子句的查询表达式
from x1 in e1
join x2 in e2 on k1 equals k2
select v
转换为
( e1 ) . Join( e2 , x1 => k1 , x2 => k2 , ( x1 , x2 ) => v )
带有 join 子句(不含 into)且后接除 select 子句之外的其他内容的查询表达式
from x1 in e1
join x2 in e2 on k1 equals k2
…
转换为
from *
in ( e1 ) . Join(
e2 , x1 => k1 , x2 => k2 , ( x1 , x2 ) => new { x1 , x2 })
…
带有 join 子句(含 into)且后接 select 子句的查询表达式
from x1 in e1
join x2 in e2 on k1 equals k2 into g
select v
转换为
( e1 ) . GroupJoin( e2 , x1 => k1 , x2 => k2 , ( x1 , g ) => v )
带有 join 子句(含 into)且后接除 select 子句之外的其他内容的查询表达式
from x1 in e1
join x2 in e2 on k1 equals k2 into g
…
转换为
from *
in ( e1 ) . GroupJoin(
e2 , x1 => k1 , x2 => k2 , ( x1 , g ) => new { x1 , g })
…
带有 orderby 子句的查询表达式
from x in e
orderby k1 , k2 , … , kn
…
转换为
from x in ( e ) .
OrderBy ( x => k1 ) .
ThenBy ( x => k2 ).
… .
ThenBy ( x => kn )
…
如果排序子句指定 descending 方向指示器,则将改为生成对 OrderByDescending 或
ThenByDescending 的调用。
转换规则实例演示
我们假定,在下面的每个查询表达式中没有 let、 where、 join 或 orderby 子句,并且最多只有一个初始 from 子句。
示例1:
from c in customers
from o in c.Orders
select new { c.Name, o.OrderID, o.Total }
转换为
customers.
SelectMany(c => c.Orders,(c,o) => new { c.Name, o.OrderID, o.Total })
示例2:
from c in customers
from o in c.Orders
orderby o.Total descending
select new { c.Name, o.OrderID, o.Total }
转换为
from * in customers.SelectMany(c => c.Orders, (c,o) => new { c, o })
orderby o.Total descending
select new { c.Name, o.OrderID, o.Total }
最终转换为
customers.SelectMany(c => c.Orders, (c,o) => new { c, o }).OrderByDescending(x => x.o.Total).Select(x => new { x.c.Name, x.o.OrderID, x.o.Total })
其中 x 是编译器生成的以其他方式不可见且不可访问的标识符。
示例3:
from o in orders
let t = o.Details.Sum(d => d.UnitPrice * d.Quantity)
where t >=
select new { o.OrderID, Total = t }
转换为
from * in orders.
Select(o => new { o, t = o.Details.Sum(d => d.UnitPrice * d.Quantity) })
where t >=
select new { o.OrderID, Total = t }
最终转换为
orders.Select(o => new { o, t = o.Details.Sum(d => d.UnitPrice * d.Quantity) }).Where(x => x.t >= ).Select(x => new { x.o.OrderID, Total = x.t })
其中 x 是编译器生成的以其他方式不可见且不可访问的标识符。
示例4:
from c in customers
join o in orders on c.CustomerID equals o.CustomerID
select new { c.Name, o.OrderDate, o.Total }
转换为
customers.Join(orders, c => c.CustomerID, o => o.CustomerID,(c, o) => new { c.Name, o.OrderDate, o.Total })
示例5:
from c in customers
join o in orders on c.CustomerID equals o.CustomerID into co
let n = co.Count()
where n >=
select new { c.Name, OrderCount = n }
转换为
from * in customers.GroupJoin(orders, c => c.CustomerID, o => o.CustomerID,
(c, co) => new { c, co })
let n = co.Count()
where n >=
select new { c.Name, OrderCount = n }
最终转换为
customers.GroupJoin(orders, c => c.CustomerID, o => o.CustomerID,(c, co) => new { c, co }).Select(x => new { x, n = x.co.Count() }).Where(y => y.n >= ).Select(y => new { y.x.c.Name, OrderCount = y.n)
其中 x 和 y 是编译器生成的以其他方式不可见且不可访问的标识符。
示例6:
from o in orders
orderby o.Customer.Name, o.Total descending
select o
转换为
orders.OrderBy(o => o.Customer.Name).ThenByDescending(o => o.Total)
select 子句的转换
from x in e select v
转换为
( e ) . Select ( x => v )
当 v 为标识符 x 时,转换仅为
( e )
Groupby 子句的转换
from x in e group v by k
转换为
( e ) . GroupBy ( x => k , x => v )
当 v 为标识符 x 时,转换为
( e ) . GroupBy ( x => k )
转载来源:http://blog.csdn.net/honantic/article/details/46490995
LINQ查询表达式详解(2)——查询表达式的转换的更多相关文章
- T-SQL查询进阶--详解公用表表达式(CTE)
简介 对于SELECT查询语句来说,通常情况下,为了使T-SQL代码更加简洁和可读,在一个查询中引用另外的结果集都是通过视图而不是子查询来进行分解的. 但是,视图是作为系统对象存在数据库中,那对于结果 ...
- mysql慢查询----pt-query-digest详解慢查询日志(linux系统)
一.简介 pt-query-digest是用于分析mysql慢查询的一个工具,它可以分析binlog.General log.slowlog,也可以通过SHOWPROCESSLIST或者通过tcpdu ...
- EL表达式详解(常用表达式以及取值)
EL表达式 学习总结 一. El表达式概念 二. El中的表达式 1. 算术表达式 2. 比较表达式 3. 逻辑表达式 4. 三元表达式 5. 判空表达式 三.EL 从四个作用域中取值 1. 概念 2 ...
- 转载及总结:cron表达式详解,cron表达式写法,cron表达式例子
cron表达式格式:{秒数} {分钟} {小时} {日期} {月份} {星期} {年份(可为空)}例 "0 0 12 ? * WED" 在每星期三下午12:00 执行(年份通常 ...
- cron表达式详解,cron表达式写法,cron表达式例子
(cron = "* * * * * *") cron表达式格式:{秒数} {分钟} {小时} {日期} {月份} {星期} {年份(可为空)}例 "0 0 12 ? ...
- cron表达式详解
@Scheduled(cron = "* * * * * *") cron表达式详解 1.cron表达式格式: {秒数} {分钟} {小时} {日期} {月份} {星期} {年份( ...
- MongoDB各种查询操作详解
这篇文章主要介绍了MongoDB各种查询操作详解,包括比较查询.关联查询.数组查询等,需要的朋友可以参考下 一.find操作 MongoDB中使用find来进行查询,通过指定find的第一个参数可 ...
- 慢查询explan详解
慢查询排查 show status; // 查询mysql数据库的一些运行状态 show status like 'uptime'; // 查看mysql数据库启动多 ...
- hibernate(七) hibernate中查询方式详解
序言 之前对hibernate中的查询总是搞混淆,不明白里面具体有哪些东西.就是因为缺少总结.在看这篇文章之前,你应该知道的是数据库的一些查询操作,多表查询等,如果不明白,可以先去看一下 MySQL数 ...
随机推荐
- 04 vue-cli 脚手架、webpack-simple模板项目生成、组件使用
alice https://www.cnblogs.com/alice-bj/p/9317504.html https://www.cnblogs.com/alice-bj/p/9318069.htm ...
- jmeter 5.0,http请求登录返回的cookie在头部处理方法
http登录之后返回的cookie在响应的头部,再次请求虽然加了cookie管理器,但是下一个请求调用响应是登陆失效,这里讲一下我的解决方法 1:在登录之后添加正则表达式,提取cookie 2:提取之 ...
- js+分布上传大文件
文件夹上传:从前端到后端 文件上传是 Web 开发肯定会碰到的问题,而文件夹上传则更加难缠.网上关于文件夹上传的资料多集中在前端,缺少对于后端的关注,然后讲某个后端框架文件上传的文章又不会涉及文件夹. ...
- Ubuntu:笔记本计算机屏幕合盖后进入/不进入休眠设置
造冰箱的大熊猫,本文在Ubuntu 18.04上验证通过@cnblogs 2019/5/12 1.问题描述 安装有Ubuntu的笔记本计算机与KVM控制器相连,同时一台台式机也与KVM相连.启动笔记本 ...
- Confluence 6.15 附件(Attachments )宏
附件宏显示添加到一个页面中的所有文件列表. 同时附件宏允许用户(用户需要具有正确的权限)来进行下面的操作: 直接从列表中上传一个文件到页面 编辑附件属性和标签 删除一个附件文件(这个将会删除文件的所有 ...
- 顺序表应用6:有序顺序表查询(SDUT 3330)
Problem Description 顺序表内按照由小到大的次序存放着n个互不相同的整数,任意输入一个整数,判断该整数在顺序表中是否存在.如果在顺序表中存在该整数,输出其在表中的序号:否则输出&qu ...
- ubuntu安装扩展在phpinfo显示不出来的解决办法
在Ubuntu中使用apt-get安装了php扩展(比如redis.memcache.memcached等),在终端输入php -m是显示已加载,但是在phpinfo中无法显示,这个时候需要重启一下p ...
- Number Of Permutations
Number Of Permutations 思路:利用容斥,首先所有可能的排列肯定是fac[n],然后可能会有三种 bad 的情况: ①第一个元素的排列是非递减 ②第二种是第二个元素的排列是非递减 ...
- [CSP-S模拟测试]:射手座之日(dsu on tree)
题目传送门(内部题103) 输入格式 第一行一个数$n$,表示结点的个数. 第二行$n–1$个数,第$i$个数是$p[i+1]$.$p[i]$表示结点$i$的父亲是$p[i]$.数据保证$p[i]&l ...
- uni-app 尺寸单位
uni-app 支持的通用 css 单位包括 px.rpx px 即屏幕像素 rpx 即响应式px,一种根据屏幕宽度自适应的动态单位.以750宽的屏幕为基准,750rpx恰好为屏幕宽度.屏幕变宽,rp ...