具有标量结果的LINQ是否会触发延迟加载

gdo*_*ica 6 .net c# linq entity-framework lazy-loading

我阅读了团队发布的加载相关实体帖子Entity Framework,并对最后一段感到有些困惑:

有时,知道有多少实体与数据库中的另一个实体相关而实际上不会产生加载所有这些实体的成本是有用的.使用LINQ Count方法的Query方法可用于执行此操作.例如:

using (var context = new BloggingContext())
{
    var blog = context.Blogs.Find(1);

    // Count how many posts the blog has 
    var postCount = context.Entry(blog)
                          .Collection(b => b.Posts)
                          .Query()
                          .Count();
}
Run Code Online (Sandbox Code Playgroud)

为什么这里需要Query+ Count方法?
难道我们不能简单地使用LINQ的COUNT方法吗?

var blog = context.Blogs.Find(1);
var postCount = blog.Posts.Count();
Run Code Online (Sandbox Code Playgroud)

是否会触发延迟加载并且所有集合都将被加载到内存中,而不是我会得到我想要的标量值?

Dan*_*zey 6

您将在机器人案例中获得所需的标量值.但要考虑发生的事情的差异.

.Query().Count()表单的数据库上运行查询SELECT COUNT(*) FROM Posts并将该值分配给整数变量.

有了.Posts.Count,你SELECT * FROM Posts在数据库上运行(类似的东西)(已经非常昂贵).然后,将结果的每一行逐字段映射到C#对象类型中,因为枚举集合以查找计数.通过以这种方式询问计数,您强制加载所有数据,以便C#可以计算有多少.

希望很明显,向数据库询问行数(实际上没有返回所有这些行)会更有效率!


Ami*_*ach 3

第一个方法不会加载所有行,因为该Count方法是从 an 调用的IQueryable,但第二个方法是加载所有行,因为它是从 an 调用的ICollection

我做了一些测试来验证它。我用 Table1 和 Table2 进行了测试,其中 Table1 具有 PK“Id”,Table2 具有 FK“Id1”(1:N)。我使用了这里的 EF profiler http://efprof.com/

第一种方法:

var t1 = context.Table1.Find(1);

var count1 = context.Entry(t1)
                        .Collection(t => t.Table2)
                        .Query()
                        .Count();
Run Code Online (Sandbox Code Playgroud)

Select * From Table2

SELECT TOP (2) [Extent1].[Id] AS [Id]
FROM   [dbo].[Table1] AS [Extent1]
WHERE  [Extent1].[Id] = 1 /* @p0 */

SELECT [GroupBy1].[A1] AS [C1]
FROM   (SELECT COUNT(1) AS [A1]
        FROM   [dbo].[Table2] AS [Extent1]
        WHERE  [Extent1].[Id1] = 1 /* @EntityKeyValue1 */) AS [GroupBy1]
Run Code Online (Sandbox Code Playgroud)

第二种方法:

var t1 = context.Table1.Find(1);
var count2 = t1.Table2.Count(); 
Run Code Online (Sandbox Code Playgroud)

表2被加载到内存中:

SELECT TOP (2) [Extent1].[Id] AS [Id]
FROM   [dbo].[Table1] AS [Extent1]
WHERE  [Extent1].[Id] = 1 /* @p0 */

SELECT [Extent1].[Id]  AS [Id],
       [Extent1].[Id1] AS [Id1]
FROM   [dbo].[Table2] AS [Extent1]
WHERE  [Extent1].[Id1] = 1 /* @EntityKeyValue1 */
Run Code Online (Sandbox Code Playgroud)

为什么会发生这种情况?

的结果Collection(t => t.Table2)是一个实现ICollection但未加载所有行并具有名为 的属性的类IsLoaded。该方法的结果Query是一个IQueryable,这允许Count在不预加载行的情况下进行调用。

的结果t1.Table2是 anICollection并且它正在加载所有行以获取计数。顺便说一句,即使您只使用t1.Table2而不询问计数,行也会加载到内存中。