Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/75.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么添加不必要的ToList()会大大加快LINQ查询的速度?_C#_Sql_Performance_Linq_Sql Server 2012 - Fatal编程技术网

C# 为什么添加不必要的ToList()会大大加快LINQ查询的速度?

C# 为什么添加不必要的ToList()会大大加快LINQ查询的速度?,c#,sql,performance,linq,sql-server-2012,C#,Sql,Performance,Linq,Sql Server 2012,为什么使用ToList()强制实现物化会使我的查询速度加快,而实际上恰恰相反呢? 1) 立即调用First() // "Context" is an Entity Framework DB-first model var query = from x in Context.Users where x.Username.ToLower().Equals(User.Identity.Name.ToLower()) se

为什么使用
ToList()
强制实现物化会使我的查询速度加快,而实际上恰恰相反呢?

1) 立即调用
First()

    // "Context" is an Entity Framework DB-first model

    var query = from x in Context.Users
                where x.Username.ToLower().Equals(User.Identity.Name.ToLower())
                select x;

    var User = query.First();

    //  ** The above takes 30+ seconds to run **
2) 调用
ToList()
后调用
First()

var query=来自Context.Users中的x
其中x.Username.ToLower()等于(User.Identity.Name.ToLower())
选择x;
var User=query.ToList().First();//在First()之前添加了ToList()
//**现在运行时间<1秒**
更新和解决方案
在获得生成的SQL之后,唯一的区别是,正如预期的那样,在第一个查询中添加了
TOP(1)
。正如他在下面的回答中所说,根本原因是SQL Server优化器(在这种情况下)在添加
TOP(1)
时选择了更糟糕的执行计划。因此,问题与LINQ无关(它通过添加
TOP(1)
,做了正确的事情),而与SQL Server的特性有关。

SQL将与LINQ不同。因此,对
.ToList()
的调用将强制.Net计算表达式,然后在内存中选择
first()

其中,作为另一个选项,应将
top 1
添加到SQL中

例如

如下所示,第一个查询应该更快!我建议做一次调查,看看发生了什么。查询的速度将取决于您的数据结构、记录数、索引等

测试的时间也会改变结果。正如一些人在评论中提到的,第一次点击EF时,它需要初始化并加载元数据。所以如果你把它们放在一起运行,第一个应该总是慢的

这里有更多关于EF的信息

注意这一行:

实体框架使用的模型和映射元数据加载到 元数据工作区。此元数据是全局缓存的,并且可用 指向同一应用程序域中ObjectContext的其他实例

&

因为与数据库的开放连接会消耗宝贵的资源 资源,实体框架打开和关闭数据库 仅在需要时连接。您还可以显式打开 连接。有关更多信息,请参阅管理连接和连接 实体框架中的事务


我只能想到一个原因。。。 要测试它,请删除
Where
子句并重新运行测试?如果结果是第一个语句更快,请在此处进行注释,我将解释原因

编辑
在LINQ语句Where子句中,您使用的是字符串的.ToLower()方法。我的猜测是,对于这种方法,LINQ没有内置到SQL的转换,因此生成的SQL是一行

SELECT *
FROM Users
现在,我们知道LINQ延迟加载,但它也知道,因为它没有计算
WHERE
子句,所以它需要加载元素来进行比较

假设
第一个查询是延迟加载结果集中的每个元素。然后执行.ToLower()比较并返回第一个结果。这将导致对服务器的
n
请求和巨大的性能开销。如果不查看SQL跟踪日志,则无法确定

第二条语句调用ToList,它在执行ToLower比较之前请求一个批处理SQL,结果只向服务器发出一个请求

替代假设
如果探查器仅显示一个服务器执行,请尝试使用Top 1子句执行相同的查询,并查看它是否需要同样长的时间。根据本文()的说法,TOP子句有时会弄乱SQL server Optimizer,并使用正确的索引阻止它

好奇编辑
尝试将LINQ更改为

var query = from x in Context.Users
            where x.Username.Equals(User.Identity.Name, StringComparison.OrdinalIgnoreCase)
            select x;

感谢@Scott找到了在LINQ中进行不区分大小写比较的方法。试一试,看看它是否更快。

因此,优化器选择了一种不好的方式来运行查询

由于您不能向SQL中添加优化器提示以迫使优化器选择更好的计划,因此我看到了两个选项

  • 在select中检索/包含的所有列上添加覆盖索引/索引视图非常可笑,但我认为它会起作用,因为该索引将使优化器很容易选择更好的计划

  • 总是过早地具体化包含First、Last或Take的查询。危险的是,随着数据变得越来越大,在本地提取所有数据和执行第一个()以及在服务器上使用Top执行查询之间的盈亏平衡点将会改变


  • 这只会使第二个查询花费更多的时间,而不是更少的时间。
    .ToList()将强制.Net计算表达式
    ,但是
    第一个()
    也会执行相同的操作(强制)。@JoeCool,如果您使用SQL Server,请按照answer@JoeCool,我知道!给我机会!:查看为调用First()生成的SQL的简单方法是使用LinqPad(免费)。然后将Linq表达式放在查询窗口中,单击“SQL”选项卡查看生成的SQL。您会注意到First()只是在sql语句前面加了一个“TOP(1)”,您是对的!我总共取出where子句,下面是我的结果:有ToList()=0.74s,没有ToList()=0.33秒。这些数字更有意义@JoeCool,你能试试好奇编辑吗?我真的很想知道是tolower还是引起问题的
    .Equals()
    。请注意,使用EF(根据)进行不区分大小写比较的“正确”方法是
    x.Username.Equals(User.Identity.Name,StringComparison.OrdinalIgnoreCase)
    。诅咒你
    Ctrl-C
    !!!,不仅是
    String.ToLowe
    
    var query = from x in Context.Users
                    where x.Username.ToLower().Equals(User.Identity.Name.ToLower())
                    select x;
    
     //SQL executed here!
     var list = query.ToList();
     var User = query.First();
    
    SELECT *
    FROM Users
    
    var query = from x in Context.Users
                where x.Username.Equals(User.Identity.Name, StringComparison.OrdinalIgnoreCase)
                select x;