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中添加优化器提示以迫使优化器选择更好的计划,因此我看到了两个选项
这只会使第二个查询花费更多的时间,而不是更少的时间。
.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;