不好意思,标题太长了:)
我真的找不到任何答案,这个问题已经在我脑海里盘旋了一段时间了。最后是问题的简短总结。
基本上我想知道linq到实体/sql循环通过一个数据阅读器之前,它映射的结果集到实体/sql类之一,或者它已经映射。Iqueryable是否必须在数据读取器中循环才能循环Iqueryable。为了显示我在代码中的意思,我想知道
foreach(object o in SomeIQueryableObject)
{
...do stuff with the object
}
将导致A:
while(Reader.Read())
{
SomeIqueryableObject.Add(Reader.TranslateRow());
}
//now the iqueryable has been loaded with objects and we can loop through it
//with the above code
foreach(object o in SomeIQueryableObject)
{
...do stuff with the object
}
还是会导致B:
while(Reader.Read())
{
...do stuff with the object
}
//Iqueryable doesn't have to loop through Reader
//before you can loop through Iqueryable.
简短总结:当我在一个可查询对象上使用foreach时,这是否等同于使用Reader.Read()
恐怕正确的答案是:视情况而定。
首先,这取决于我们谈论的IQueryable
实现的类型;IQueryable<T>
只是一个接口,实际的实现留给了实现者。据我所知,微软并没有提供任何关于如何做到这一点的严格指导方针(见鬼,没有人说IQueryable<T>
需要与数据库有任何共同之处,这将使问题毫无意义)。
当我们将自己限制为linq-to-sql/entity-framework时,答案仍然是:这取决于(至少对于linq-to-sql,尽管如果EF的工作方式有很大不同,我会感到惊讶)。这取决于几个因素(例如,您正在执行的查询类型,数据量等)。基本上它的工作原理类似于你的第二个伪代码(即不加载所有的数据在一次),但优化可以用来检索数据块/缓冲它,所以在某些情况下,查询执行在数据库端可能结束得比你的枚举更快(虽然试图利用这是任何方式可能是…非平凡的)。
如果您想模拟批量加载版本,则显式调用ToList()
/ToArray()
(或使用任何其他会导致读取所有数据的方法)。
AFAIK EF将使用您的第二个选项。我之所以这么说,是因为我有过这样的经历:在执行另一个查询时,EF会打开第二个连接,同时循环遍历第一个查询的结果。由于我正在使用一个事务,并且我禁用了分布式事务,并且第二个打开的连接(当第一个连接仍然打开并被使用时)禁用了当前事务的重用,因此我的第二个查询抛出了一个异常,说DTC未启用。
所以我的结论是:如果你正在执行查询并通过结果循环(所以没有.ToList()
),那么查询仍在执行,连接仍然打开,如果EF使用数据阅读器,那么是的,它是在从数据阅读器读取的循环内。