简介

  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)——查询表达式的转换的更多相关文章

  1. T-SQL查询进阶--详解公用表表达式(CTE)

    简介 对于SELECT查询语句来说,通常情况下,为了使T-SQL代码更加简洁和可读,在一个查询中引用另外的结果集都是通过视图而不是子查询来进行分解的. 但是,视图是作为系统对象存在数据库中,那对于结果 ...

  2. mysql慢查询----pt-query-digest详解慢查询日志(linux系统)

    一.简介 pt-query-digest是用于分析mysql慢查询的一个工具,它可以分析binlog.General log.slowlog,也可以通过SHOWPROCESSLIST或者通过tcpdu ...

  3. EL表达式详解(常用表达式以及取值)

    EL表达式 学习总结 一. El表达式概念 二. El中的表达式 1. 算术表达式 2. 比较表达式 3. 逻辑表达式 4. 三元表达式 5. 判空表达式 三.EL 从四个作用域中取值 1. 概念 2 ...

  4. 转载及总结:cron表达式详解,cron表达式写法,cron表达式例子

    cron表达式格式:{秒数} {分钟} {小时} {日期} {月份} {星期} {年份(可为空)}例  "0 0 12 ? * WED" 在每星期三下午12:00 执行(年份通常 ...

  5. cron表达式详解,cron表达式写法,cron表达式例子

    (cron = "* * * * * *") cron表达式格式:{秒数} {分钟} {小时} {日期} {月份} {星期} {年份(可为空)}例  "0 0 12 ? ...

  6. cron表达式详解

    @Scheduled(cron = "* * * * * *") cron表达式详解 1.cron表达式格式: {秒数} {分钟} {小时} {日期} {月份} {星期} {年份( ...

  7. MongoDB各种查询操作详解

    这篇文章主要介绍了MongoDB各种查询操作详解,包括比较查询.关联查询.数组查询等,需要的朋友可以参考下   一.find操作 MongoDB中使用find来进行查询,通过指定find的第一个参数可 ...

  8. 慢查询explan详解

    慢查询排查         show status;  // 查询mysql数据库的一些运行状态         show status like 'uptime'; // 查看mysql数据库启动多 ...

  9. hibernate(七) hibernate中查询方式详解

    序言 之前对hibernate中的查询总是搞混淆,不明白里面具体有哪些东西.就是因为缺少总结.在看这篇文章之前,你应该知道的是数据库的一些查询操作,多表查询等,如果不明白,可以先去看一下 MySQL数 ...

随机推荐

  1. [学习笔记] 可持久化线段树&主席树

    众所周知,线段树是一个非常好用也好写的数据结构, 因此,我们今天的前置技能:线段树. 然而,可持久化到底是什么东西? 别急,我们一步一步来... step 1 首先,一道简化的模型: 给定一个长度为\ ...

  2. VIDENT iSmart900自动多系统扫描工具OBDII支持ABS / SRS / EPB /传输诊断DPF再生/上油复位编码电池配置

    Vident系列中有许多多功能产品,其中最好的是Vident iSmart 900.购买视频系列后,以下是用户的一些评论 乔:因为我想它很好用.该工具很容易更新.我将公制重量单位的代码放到工具中.很容 ...

  3. Comet OJ - Contest #3 D 可爱的菜菜子 线段树+线性基

    题意 给你一个长度为 \(n\) 的整数序列 \(a_1, a_2, \ldots, a_n\),你需要实现以下两种操作,每个操作都可以用四个整数 \(opt\ l\ r\ v\) 来表示: \(op ...

  4. TCP连接创建与终止

    创建连接:三次握手一句话,”就是客户端发个syn,服务端发个syn+ack,客户端再回个ack“ 终止连接:四次挥手

  5. [JOI2012春季合宿]Rotate (链表)

    题意 题解 又是一道神仙题-- 显然的做法是大力splay,时间复杂度\(O((N+Q)N\log N)\), 可以卡掉. 正解: 使用十字链表维护矩阵,在周围增加第\(0\)行/列和第\((n+1) ...

  6. $\LaTeX$数学公式大全8

    $8\ Miscellaneous\ symbols$ $\infty$ \infty $\nabla$ \nabla $\partial$ \partial $\eth$ \eth $\clubsu ...

  7. c源码编译

    #include<stdio.h> #include<math.h> //程序中要调用求平方根函数sqrt int main() { double a,b,c,disc,x1, ...

  8. python|爬虫东宫小说

    2k小说网爬取最近大火的<东宫>小说,借鉴之前看过的一段代码,修改之后,进行简单爬取. from urllib import requestfrom bs4 import Beautifu ...

  9. ubuntu14.04+安卓7.1(全志源码)+openjdk-8编译

    题记:编译花了将近4小时,所以编译源码是很费时的哦,可以在编译的时候可以学习其他的知识 编译环境准备 软件:WorkStation10 系统:ubuntu14.04 内存:8G 处理器:4个 磁盘大小 ...

  10. 八十一:memcached之telnet操作memcached

    先打开telnet服务否则会报错 确认memcached服务已启动 telnet ip 端口 连上以后敲一下回车 常用的命令: 1.set:在memcached中添加一个key->value,如 ...