实体框架 6 - 查询性能

Entity Framework 6 - Query Performance

提问人:CrazyEight 提问时间:1/21/2020 更新时间:1/21/2020 访问量:2402

问:

我使用 Entity Framework 6,我目前有一个包含许多包含的查询,它将大约 1200 个实体加载到 dbContext 中。加载实体似乎很慢,因为查询需要将近一分钟。我能做些什么来表现吗?我有 4 个这样的查询需要 2.5 分钟才能加载? LazyLoading 已启用,但出于性能原因,我预加载了实体。

var report = DbContext.REPORT.Single(r => r.ID == reportId);

//this query takes a bit less than 1 minute
DbContext.REPORT_ELEMENT
    .Include(re => re.LAYOUT)
    .Include(re => re.PAGEMASTER)
    .Include(re => re.REPORT_ELEMENTS)
    .Include(re => re.SUBTITLE_CONTENT)
    .Include(re => re.REPORT_ELEMENT_NOTE)
    .Include("SUBTITLE_CONTENT.CONTENT_ELEMENT.LANGUAGE")
    .Include("TITLE_CONTENT.CONTENT_ELEMENT.LANGUAGE")
    .Where(re => re.REPORT_ID == report.ID)
    .Load();
C# 实体框架-6 dbcontext

评论

0赞 Anduin Xue 1/21/2020
考虑添加AsNotracking()
0赞 Jan 1/21/2020
另一个选项可能是 learn.microsoft.com/en-us/ef/ef6/querying/raw-sql 在 EF Core 中,您可以使用它,然后放置输入语句。如果这适用于 EF 中的 input语句,我不确定。
0赞 panoskarajohn 1/21/2020
您的问题非常广泛,可能适用许多解决方案。通常,为了提高查询性能,您需要在 SQL Server 上打开探查器,总结您正在使用的是 MSSQL。查看执行计划,然后尝试对其进行优化。此外,延迟加载会降低性能,而不是增强性能。索引也可能有所帮助,但在这种情况下我对此表示怀疑。也可能有所帮助,因为您正在选择要提供的数据。使用探查器检查额外的查询 2,5 分钟是相当多的。Select
0赞 CrazyEight 1/21/2020
@panoskarajohn 如何指定问题?您需要什么信息?我需要将代码片段的包含中列出的所有引用加载到 dbcontext 中,因为我需要它们来编写 xml。如果我一块一块地重新加载它们,整个过程会更慢
0赞 panoskarajohn 1/21/2020
@CrazyEight正如我在上面的评论中提到的,查询的分析时间将是一个开始。执行计划将是一个很好的补充。也。提供的答案也是有效的。尝试调试,看看发生了什么。可能会发生额外的查询。尝试禁用,看看发生了什么。尝试调查。老实说,这些东西是反复试验的。您提到将所有数据都放入 XML 中。您可能需要重新考虑这一点。所有数据都有价值吗?ProxiesLazyLoading

答:

1赞 Anduin Xue 1/21/2020 #1

性能建议:

  • 防止跟踪。以只读模式查询。
  • 防止在一次查询中获取过多数据。尝试对它进行分页。
  • 防止包含。查询的 s 太多,导致性能不佳。Include

防止跟踪

请考虑为此添加可提高查询性能。AsNoTracking

参考: https://learn.microsoft.com/en-us/ef/core/querying/tracking#no-tracking-queries

只获取您需要的数据

查询速度慢的主要原因是它输出的数据过多。请考虑添加:,以仅获取您需要的数据或当前页面所需的数据。使用寻呼机生成报告。这可能有很大帮助。Take(200)Skip()

防止Include

Include生成 SQL 以选择多个表。这大大增加了复杂性。您只能选择所需的数据,并阻止编写函数。Include

例如,如果你只想把最后一个球放进盒子里,可以考虑这样写:

public class Box
{
    public int Id { get; set; }
    public IEnumerable<Ball> Balls { get; set; }
}

public class Ball
{
    public int Id { get; set; }

    public int BoxId { get; set; }
    public Box Box { get; set; }
}
var boxes = await Boxes
            // DO NOT Call Include(t => t.Balls) here!
            .Where(somecondition)
            .Select(t => new Box(){
              Id = t.Id,
              Balls = t.Balls.OrderByDescending(x => x.CreationTime)
                         .Take(1) // Only get what you need
            })               
            .ToListAsync()

此外,当我们使用 Select 时,我们可以删除,因为它在这里不会有任何影响。.Include

评论

0赞 CrazyEight 1/21/2020
但是我想一次加载所有数据,因为我之后会创建一个 xml 文件。如果我一点一点地加载数据,就会触发很多查询,这使得性能完全变慢。AsNoTracking() 似乎使整个事情变得更慢
0赞 Michael Steinecke 1/21/2020 #2

除了 Anduin 的建议之外,我还想添加一些建议,将其拆分为几个不同的查询。EF 将能够跟踪同一 .作为一般经验法则 - 不要在同一查询中使用三个以上的。此外,请确保数据库中每个生成的 JOIN 都有一个索引。Includes()DBContextIncludes()

为此,除了导航属性之外,还必须公开实体中的 FK 字段。

您的初始查询将变成如下所示:

    DbContext.LAYOUT
        .Where(re => re.LAYOUT_ID == report.LAYOUT_FK)
        .Load();
    DbContext.PAGEMASTER
        .Where(re => re.PAGEMASTERT_ID == report.PAGEMASTER_FK)
        .Load();
0赞 Jonathan Magnan 1/21/2020 #3

免责声明:我是 Entity Framework Plus 项目的所有者

查询 IncludeOptimized 功能允许使用 include 进行筛选,并同时优化查询性能。

它通常会提高性能(将查询拆分为较小的查询)

DbContext.REPORT_ELEMENT
    .IncludeOptimized(re => re.LAYOUT)
    .IncludeOptimized(re => re.PAGEMASTER)
    .IncludeOptimized(re => re.REPORT_ELEMENTS)
    .IncludeOptimized(re => re.SUBTITLE_CONTENT)
    .IncludeOptimized(re => re.REPORT_ELEMENT_NOTE)
    .IncludeOptimized(re => re.SUBTITLE_CONTENT.Select(sc => sc.CONTENT_ELEMENT))  // SelectMany?
    .IncludeOptimized(re => re.SUBTITLE_CONTENT.Select(sc => sc.CONTENT_ELEMENT).Select(ce => ce.LANGUAGE)) // SelectMany?
    .IncludeOptimized(re => re.TITLE_CONTENT)
    .IncludeOptimized(re => re.SUBTITLE_CONTENT.Select(sc => sc.CONTENT_ELEMENT)) // SelectMany?
    .IncludeOptimized(re => re.SUBTITLE_CONTENT.Select(sc => sc.CONTENT_ELEMENT).Select(ce => ce.LANGUAGE)) // SelectMany?
    .Where(re => re.REPORT_ID == report.ID)
    .Load();

文档:EF+ 查询 IncludeOptimized