var costCenters = from c in dbContext.CostCenters //no sql call here
orderby c.DisplayRank descending
select c;
List<CostCenter> lstCostCenter = costCenters.ToList();//Immediate execution to sql the first time
lstCostCenter = costCenters.ToList();//no Sql call ??
int test = costCenters.Count();//Call Sql everytime
test = costCenters.Count();//Call Sql again???
我正在使用实体框架5
我开始学Linq了。我真的很困惑,哪些即时执行函数每次都会调用SQL。正如您在上面的示例中看到的,ToList()和Count()都是立即执行的函数,但是只有Count()会在后续调用时连接到sql。ToList()连接sql 1次,但Count()将每次连接sql。
我如何确定哪些linq函数不会多次调用sql ?
首先,ToList()从不使用延迟执行。它总是能立即实现信息。它不会每次都回到数据库,因为它已经抓取了所有的实体。
区别在于第一次调用ToList()时,它将IQueryable(这只是查询的定义)转换为IEnumerable(查询数据库)列表。换句话说,您将返回一个现在在内存中的对象列表,因此对结果列表的任何进一步LINQ调用都将使用与内存中对象一起工作的IEnumerable版本。此外,EF有一个缓存结果的特性,因此即使您在原始IQueryable引用上调用ToList,它也可能使用内存中的对象,而不是从数据库中获取它们。我猜测 Count再次命中数据库,而不是计算缓存的结果,因为对Count的查询与对tollist的查询不同(它是一种聚合/分组),也可能是这样设计的,因为DB引擎在提供计数方面更有效。
在你的例子中,costCenters
是一个IQueryable,因为你所做的只是定义一个查询,但还没有调用tollist。lstCostCenter
是IEnumerable,表示使用ToList执行查询后的内存结果。通常立即调用产生结果,. count, . tolist等,当在IQueryable上执行时将导致DB调用(当它找到可以重用的缓存结果时例外),并且调用IEnumerable对象(在您的情况下lstCostCenter
)将在内存中操作。
要获得更可预测的结果,首先在IQueryable上调用ToList,然后在IEnumerable上调用所有进一步的调用。换句话说,您在lstCostCenter
上调用的任何内容都保证不会访问数据库。这通常是处理它的最佳方法,除非您期望得到的列表非常大。例如,如果lstCostCenter最终有10,000个对象,那么您可能不想执行lstCostCenter.Where(x=>x.Blah > 5)
,因为这会循环遍历内存中的所有10,000个对象以过滤它们。在这种情况下,最好先修改查询,将额外的调用附加到IQueryable,然后调用ToList,这样我们就可以利用DB引擎,它更擅长处理大集合:costCenters.Where(x=>x.Blah > 5).ToList()
.
http://msdn.microsoft.com/en-us/library/bb738633.aspx
区别在于"强制执行"。如果强制立即执行查询,则缓存结果。而像ToList()、ToArray()甚至foreach这样的转换操作符被归类为强制执行,因此进一步的调用将只对内存缓存进行操作。其中Count(), First(), Max()和Average()不被认为是强制执行…它们是查询的一部分……我猜。