LINQ 的查询执行何时是延迟执行,何时是立即执行,以及查询的复用

延迟执行的经典例子

我们用 select ++i 就可以看到在foreach 时候,查询才被执行。

public static void Linq99()
{
    int[] numbers = new int[] { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
    int i = 0;
    var q = from n in numbers select ++i;
    foreach (var v in q)
        Console.WriteLine("v = {0}, i = {1}", v, i);
}

输出结果:

v = 1, i = 1
v = 2, i = 2
v = 3, i = 3
v = 4, i = 4
v = 5, i = 5
v = 6, i = 6
v = 7, i = 7
v = 8, i = 8
v = 9, i = 9
v = 10, i = 10

foreach每一个遍历的时候,select出来的值和当前i的值都是一样的。

立即执行的经典例子:

public static void Linq99()
{
    int[] numbers = new int[] { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
    int i = 0;
    var q = (from n in numbers select ++i).ToList();
    foreach (var v in q)
        Console.WriteLine("v = {0}, i = {1}", v, i);
}

执行结果:

v = 1, i = 10
v = 2, i = 10
v = 3, i = 10
v = 4, i = 10
v = 5, i = 10
v = 6, i = 10
v = 7, i = 10
v = 8, i = 10
v = 9, i = 10
v = 10, i = 10

这个例子的代码跟上面延迟执行的例子代码唯一的差别在于多了一个.ToList();
这也可以证明我们之前提到的原则:

只有到用的时候才会去执行查询

由于 .ToList(); 的存在,在这里就要用到了,所以在这里就执行了查询,而不是在foreach中执行查询。注意,这时候出来的结果是一个数组了.参看后面的几个例子.

执行的一个特殊情况:重复执行

请看下面例子:

查询出一个int数组中小于3的数字。

下面例子中在第一次查询后,对数据源作了修改,然后再作第二次查询,我们可以看到第二次我们不需要再作

lowNumbers = from n in numbers where n <= 3 select n; 这样的定义,而是直接使用    foreach (int n in lowNumbers)。另外这两次的返回结果是不同的,因为我们
在第一次查询后,对数据源作了修改。

public static void Linq101()
{
    int[] numbers = new int[] { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
    var lowNumbers = from n in numbers where n <= 3 select n;
    Console.WriteLine("First run numbers <= 3:");
    foreach (int n in lowNumbers)
        Console.WriteLine(n);

for (int i = 0; i < 10; i++)
        numbers[i] = -numbers[i];

Console.WriteLine("Second run numbers <= 3:");
    foreach (int n in lowNumbers)
        Console.WriteLine(n);
}

输出结果:

First run numbers <= 3:
1
3
2
0
Second run numbers <= 3:
-5
-4
-1
-3
-9
-8
-6
-7
-2
0

下面我们再来看几个例子,加深对查询执行的理解:

重复查询的再一个例子:

public static void Linq102()
{
    int[] numbers = new int[] { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
    int i = 0;
    var q = from n in numbers select ++i;
    foreach (var v in q)
        Console.WriteLine("v = {0}, i = {1}", v, i);
    foreach (var v in q)
        Console.WriteLine("v = {0}, i = {1}", v, i);
}

执行结果:

v = 1, i = 1
v = 2, i = 2
v = 3, i = 3
v = 4, i = 4
v = 5, i = 5
v = 6, i = 6
v = 7, i = 7
v = 8, i = 8
v = 9, i = 9
v = 10, i = 10
v = 11, i = 11
v = 12, i = 12
v = 13, i = 13
v = 14, i = 14
v = 15, i = 15
v = 16, i = 16
v = 17, i = 17
v = 18, i = 18
v = 19, i = 19
v = 20, i = 20

只执行一次的立即查询:

public static void Linq102()
{
    int[] numbers = new int[] { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
    int i = 0;
    var q = (from n in numbers select ++i).ToList();
    foreach (var v in q)
        Console.WriteLine("v = {0}, i = {1}", v, i);
    foreach (var v in q)
        Console.WriteLine("v = {0}, i = {1}", v, i);
}

执行结果:

v = 1, i = 10
v = 2, i = 10
v = 3, i = 10
v = 4, i = 10
v = 5, i = 10
v = 6, i = 10
v = 7, i = 10
v = 8, i = 10
v = 9, i = 10
v = 10, i = 10
v = 1, i = 10
v = 2, i = 10
v = 3, i = 10
v = 4, i = 10
v = 5, i = 10
v = 6, i = 10
v = 7, i = 10
v = 8, i = 10
v = 9, i = 10
v = 10, i = 10

那些函数会导致立即执行查询:

以下几个扩展函数会导致LINQ会立即执行。并且只执行一次。

.ToArray();

.ToList();

.ToDictionary(k => k);

比如:

public static void Linq102()
{
    int[] numbers = new int[] { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
    int i = 0;
    var q = (from n in numbers select ++i).ToDictionary(k => k);
    foreach (var v in q)
        Console.WriteLine("v = {0}, i = {1}", v, i);
    foreach (var v in q)
        Console.WriteLine("v = {0}, i = {1}", v, i);
}

输出结果就是:

v = [1, 1], i = 10
v = [2, 2], i = 10
v = [3, 3], i = 10
v = [4, 4], i = 10
v = [5, 5], i = 10
v = [6, 6], i = 10
v = [7, 7], i = 10
v = [8, 8], i = 10
v = [9, 9], i = 10
v = [10, 10], i = 10
v = [1, 1], i = 10
v = [2, 2], i = 10
v = [3, 3], i = 10
v = [4, 4], i = 10
v = [5, 5], i = 10
v = [6, 6], i = 10
v = [7, 7], i = 10
v = [8, 8], i = 10
v = [9, 9], i = 10
v = [10, 10], i = 10

小结:

Q:通过上面几个例子,我们该如何理解LINQ的查询何时执行呢?

ALINQ的查询执行遵循以下原则:

1、一般情况下(除了下面第三条说的情况),LINQ都是延迟执行,原因:以DLINQ为例,越晚被执行,对业务逻辑的理解就越清晰,DLINQ查询对数据库的请求压力越小。编译器对LINQ查询优化可作的事情越多。

2、由于是延迟执行,也就是调用的时候才去执行。这样调用一次就被执行一次,这样就具备了重复执行的功能,参看之前的几个重复执行的例子。而这个重复执行是不需要再此书写一边查询语句的。

3、如果查询中我们对查询结果使用了 ToArrayToListToDictionary 这些转换成集合的扩展方法。使用这时候出来的对象是一个独立的集合数组,而不是LINQ查询,所以这时候不会出现多次查询,而只是一次查询。

即:var q = from n in numbers select ++i ;  这样一条语句我们可以认为它记录的不是等号右边的结果,而是记录的等号右边的表达式。

而    var q = (from n in numbers select ++i).ToDictionary(k => k); 这样一条语句我们记录的是等号右边的计算结果,而不是表达式。

为理解上面说明,我们可以再看两个例子:

public static void Linq102()
{
    int[] numbers = new int[] { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
    int i = 0;
    var q = from n in numbers select ++i;
    var qq = q.ToDictionary(k => k);
    foreach (var v in q)
        Console.WriteLine("v = {0}, i = {1}", v, i);
    foreach (var v in q)
        Console.WriteLine("v = {0}, i = {1}", v, i);
}

输出结果:

v = 11, i = 11
v = 12, i = 12
v = 13, i = 13
v = 14, i = 14
v = 15, i = 15
v = 16, i = 16
v = 17, i = 17
v = 18, i = 18
v = 19, i = 19
v = 20, i = 20
v = 21, i = 21
v = 22, i = 22
v = 23, i = 23
v = 24, i = 24
v = 25, i = 25
v = 26, i = 26
v = 27, i = 27
v = 28, i = 28
v = 29, i = 29
v = 30, i = 30

public static void Linq102()
{
    int[] numbers = new int[] { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
    int i = 0;
    var q = from n in numbers select ++i;
    var qq = q.ToDictionary(k => k);
    foreach (var v in qq)
        Console.WriteLine("v = {0}, i = {1}", v, i);
    foreach (var v in qq)
        Console.WriteLine("v = {0}, i = {1}", v, i);
}

输出结果为:

v = [1, 1], i = 10
v = [2, 2], i = 10
v = [3, 3], i = 10
v = [4, 4], i = 10
v = [5, 5], i = 10
v = [6, 6], i = 10
v = [7, 7], i = 10
v = [8, 8], i = 10
v = [9, 9], i = 10
v = [10, 10], i = 10
v = [1, 1], i = 10
v = [2, 2], i = 10
v = [3, 3], i = 10
v = [4, 4], i = 10
v = [5, 5], i = 10
v = [6, 6], i = 10
v = [7, 7], i = 10
v = [8, 8], i = 10
v = [9, 9], i = 10
v = [10, 10], i = 10

时间: 2024-08-24 20:16:18

LINQ 的查询执行何时是延迟执行,何时是立即执行,以及查询的复用的相关文章

动态SQL的执行,注:exec sp_executesql 其实可以实现参数查询和输出参数的

当需要根据外部输入的参数来决定要执行的SQL语句时,常常需要动态来构造SQL查询语句,个人觉得用得比较多的地方就是分页存储过程和执行搜索查询的SQL语句.一个比较通用的分页存储过程,可能需要传入表名,字段,过滤条件,排序等参数,而对于搜索的话,可能要根据搜索条件判断来动态执行SQL语句. 在SQL Server中有两种方式来执行动态SQL语句,分别是exec和sp_executesql.sp_executesql相对而言具有更多的优点,它提供了输入输出接口,可以将输入输出变量直接传递到SQL语句

main 函数执行以前以及以后,分别还会执行什么代码?(转载)

main函数执行之前,主要就是初始化系统相关资源: 1.设置栈指针 2.初始化static静态和global全局变量,即data段的内容 3.将未初始化部分的全局变量赋初值:数值型short,int,long等为0,bool为FALSE,指针为NULL,等等,即.bss段的内容 4.运行全局构造器,估计是C++中构造函数之类的吧 5.将main函数的参数,argc,argv等传递给main函数,然后才真正运行main函数 (1)全局对象的析构函数会在main函数之后执行: (2)可以用_onex

mirantis fuel puppet执行顺序 和 对整个项目代码的执行流程理解

stage执行顺序 stage {'zero': } -> stage {'first': } -> stage {'openstack-custom-repo': } -> stage {'netconfig': } -> stage {'corosync_setup': } -> stage {'openstack-firewall': } -> Stage['main'] 1.class {'begin_deployment': stage => 'zero

mysql慢查询Slow Query Log和未使用索引(Not Using Indexes)查询配置和使用

mysql的“慢查询”指的是超过了允许的最大查询时间(long_query_time)的sql语句,而“未使用索引”查询顾名思义就是查询语句没有使用到索引的sql语句. 慢查询配置和使用 在msyqld的启动配置文件或命令行参数中增加以下参数 long_query_time=1 log-slow-queries=/var/mysql/logs/slow.log long_query_time参数表示的是慢查询的度量时间,单位是秒,最小是1,缺省值是10,凡是执行时间超过long_query_ti

查询城市(可根据汉字、拼音和首字母查询)

查询城市(可根据汉字.拼音和首字母查询) 可查询中国所有城市信息,并且支持全称.拼音及首字母模糊查询 注:第一次运行会进行数据库初始化操作,需要等待2-4分钟.再次运行则不需要等待.如果不想等待可以直接将压缩包中的city_info数据库文件拷贝到手机中的data/data/com.example.searchcity/databases/文件夹中 再注:程序会获取城市名称对应的拼音信息,需要导入jar包实现:pinyin4j.压缩包中已经放入了一个Jar包,包括文档. 下载地址:http://

ThinkPHP5查询当前表引擎,以及InnoDB表引擎下count(*)查询效率低的问题

今天新开发的功能上线之后出现了查询效率极其低下的问题,查询日志后发现问题出在代码内的大量的count()查询上,最严重时一条简单的count()查询执行时间长达120多秒! 针对这个问题请教前辈后被告知原因:InnoDB引擎下的count()语句会在实时查询表中的所有数据后返回总数所以效率较低,而MyISAM引擎则是直接返回表内存储的行记录信息所以效率较高.因为我本地的数据库引擎为MyISAM而线上的阿里云数据库服务器引擎为InnoDB所以出现了这种本地环境与线上环境查询效率差距极大的问题. 并

原!!mybatis如何直接 执行传入的任意sql语句 并按照顺序取出查询的结果集

需求: 1.直接执行前端传来的任何sql语句,parameterType="String", 2.对于任何sql语句,其返回值类型无法用resultMap在xml文件里配置或者返回具体的bean类型,因此设置resultType="java.util.Map",但是Map并不保证存入取出顺序一致, 因此设置resultType="java.util.LinkedHashMap",为保证查询的字段值有序(存入与取出顺序一致)所以采用LinkedHa

iOS 设置 延迟执行 与 取消延迟执行 方法 以及对 run loop 初步认识

之前开发过程中经常会有需求会使用 NSObject中的"performSelector:withObject:afterDelay:"做方法延迟执行的处理, 但是 还没有什么地方需要实现 取消 这个延迟执行方法"cancelPreviousPerformRequestsWithTarget:".(具体可参见系统库文件 NSOject里面两个方法的声明). 但是 我们应该知道在什么条件下,合理使用 延迟 与 取消延迟. 延迟 和 取消延迟 应该 在同一个 事件处理循环

如何在服务器上面执行定时程序,在指定的时候执行指定的代码

首先是ServletContextListener这个类 如果实现这个接口,然后加上xml的配置 <listener>         <listener-class>com.xxx.xxx.MyTimerTask</listener-class>       </listener> 当web项目发布至tomcat启动之后会自动加载这个类,然后重写里面的contextInitialized和contextDestroyed方法就能在web容器初始化和销毁的时