4
            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?

4

3 回答 3

4

首先, ToList() 从不使用延迟执行。它总是立即实现信息。它不会每次都返回数据库,因为它已经抓取了所有实体。

要知道哪些运营商在做什么,只需看看这个链接

于 2013-01-09T15:31:04.427 回答
3

不同之处在于您第一次调用 ToList() 时,它将 IQueryable(它只是一个查询的定义)转换为一个 IEnumerable 列表(查询数据库)。换句话说,您会返回一个现在在内存中的对象列表,因此对结果列表的任何进一步 LINQ 调用都使用适用于内存中对象的 IEnumerable 版本。此外,EF 具有缓存结果的功能,因此即使您在原始 IQueryable 引用上调用 ToList,它也可能使用内存中的对象,而不是从数据库中获取它们。我Count 再次命中数据库而不是计算缓存的结果,因为对 count 的查询与对 ToList 的查询不同(它是一种聚合/分组),而且可能是这样设计的,因为数据库引擎是更有效地提供计数。

在您的示例中,costCenters是一个 IQueryable,因为您所做的只是定义一个查询,但还没有调用 ToList。 lstCostCenter是表示使用 ToList 执行后查询的内存中结果的 IEnumerable。通常在 IQueryable 上执行时产生结果的立即调用、.Count、.ToList 等将导致 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().

于 2013-01-09T16:03:49.307 回答
1

http://msdn.microsoft.com/en-us/library/bb738633.aspx

区别在于“强制执行”。如果您强制立即执行查询,则会缓存结果。并且转换运算符如 ToList()、ToArray() 甚至 foreach 都归类为强制执行,因此进一步的调用只会对内存缓存进行操作。其中 Count()、First()、Max() 和 Average() 不被视为强制执行......它们是查询的一部分......我猜。

于 2013-01-09T16:31:22.583 回答