【问题标题】:How to avoid N+1 when using lazy loading使用延迟加载时如何避免N+1
【发布时间】:2016-06-30 10:40:24
【问题描述】:

我从来没有使用过延迟加载(使用 virtual 关键字),因为一旦我尝试过,我发现如果我使用延迟加载会出现 N+1 问题。

例如,

 public class Blog 
{  
    public int BlogId { get; set; }  
    public string Name { get; set; }  
    public string Url { get; set; }  
    public string Tags { get; set; }  
    public virtual ICollection<Post> Posts { get; set; }  
}

db.Blog.Select(x=> new {
    name = x.Name,
    postCnt = x.Posts.Count()
}).ToList(); 

这不会加入表格,查询将与帖子数量一样多。

所以我这样做了,删除 'virtual' 关键字以进行急切加载。然后做

db.Blog.include("Posts").Select(x=> new {
    name = x.Name,
    postCnt = x.Posts.Count()
}).ToList();

我所有的控制器都返回一个 Json 数据,所以我从不使用延迟加载。

但是,当我看到很多教程或博客时,似乎每个人都在使用 virtual 关键字?这让我觉得我错过了什么,我觉得我做错了什么。

你能告诉我我不理解什么以及我做错了什么吗?

还有,

在急切加载中,我们将所有对象加载到内存中 对象已创建。

我在一些教程中看到了这一点,但我认为,除非使用“包含”,否则该对象不会加载任何相关数据。

我说的对吗?还是加载所有相关的对象?

[已编辑,添加更多示例代码]

public class Post {
    public int Id { get; set; }
    public int BlogId {get; set;}
    public string title { get; set; }

    [ForeignKey("BlogId")]         
    public virtual Blog blog { get; set; }
}


db.Post.Select(x => new {
    id = x.id,
    blogName = x.Blog.name
}).ToLost();

【问题讨论】:

  • Post的定义是什么?可能是导航问题。
  • @PeterSmith 我添加了 Post 示例代码,你能看一下吗?
  • 我不认为你可以通过延迟加载避免 N+1,因为只有 .Include() 可以让你免于其中。延迟加载在某些情况下很好,但对于返回 DTO 的 Web 服务,应该避免它。

标签: asp.net entity-framework


【解决方案1】:

你的情况有一点我不明白

考虑:

from fd in Folders
select new {
    id = fd.IdFolder,
    c = fd.Files.Count()
}

与:

Folder {
    /*...*/
    public virtual ICollection<File> Files { get; set; }
}

我得到以下 SQL:

SELECT 
    [Extent1].[idDossier] AS [idDossier], 
    (SELECT 
        COUNT(1) AS [A1]
        FROM [dbo].[tableF] AS [Extent2]
        WHERE [Extent1].[idDossier] = [Extent2].[idDossier]) AS [C1]
    FROM [dbo].[tableD] AS [Extent1]

有(或者实际上没有虚拟):

Folder {
    /*...*/
    public ICollection<File> Files { get; set; }
}

我得到了完全相同的查询,即使是:

Folders.Include("Files").
Select( fd => new {
    id = fd.IdFolder,
    c = fd.Files.Count()
})

如果你想要一个join来得到类似的sql:

select
    fd.IdDossier,
    count(fl.idDossier)
from
    tableD fd
    left join tablef fl on fd.IdDossier = fl.idDossier
group by fd.idDossier 

我只是不知道该怎么做

from fd in Folders
join fl in Files on fd.IdFolder equals fl.IdFolder into g
select new {
    id = fd.IdFolder,
    c = g.Count()
}

导致相同的 N+1 SQL

【讨论】:

  • 感谢您的评论,在这种情况下如何,Files.Select( x => new { fileName = x.name, folderName = x.Folder.name }); ?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-07-27
  • 2012-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多