使用 Entity Framework Core 时,通常使用 LINQ 查询与数据库交互以检索和操作数据。但是,有时在获取相关实体时,您可能会遇到性能问题。您的应用程序可能会随着数据库的增长而变慢,或者您遇到了臭名昭著的 N+1 查询问题。

N+1 查询问题简介

N+1 查询问题是在使用 Entity Framework Core 等 ORM 时出现的常见性能问题。当您获取实体集合(“N”个实体),然后逐个延迟加载相关实体(“+1”实体)时,会出现此问题。对于集合中的每个实体,将执行单独的查询来获取相关数据,从而将 N+1 个查询发送到数据库。

为什么 Entity Framework Core 会出现 N+1 查询问题?

当启用延迟加载或未在单个查询中显式加载相关实体时,通常会出现此问题。默认情况下,Entity Framework Core 不会启用延迟加载,除非使用 .启用延迟加载后,仅在首次访问时获取相关实体。UseLazyLoadingProxies()

例如:


<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-comment-color)">// Fetch all guilds</span>
<span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">guilds</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-text-color)">context</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Guilds</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">ToList</span><span style="color:var(--syntax-text-color)">();</span>

<span style="color:var(--syntax-comment-color)">// Accessing navigation property triggers additional queries</span>
<span style="color:var(--syntax-declaration-color)">foreach</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">guild</span> <span style="color:var(--syntax-declaration-color)">in</span> <span style="color:var(--syntax-text-color)">guilds</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
    <span style="color:var(--syntax-comment-color)">// Execute N additional queries (one per guild)</span>
    <span style="color:var(--syntax-declaration-color)">foreach</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">player</span> <span style="color:var(--syntax-declaration-color)">in</span> <span style="color:var(--syntax-text-color)">guild</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Players</span><span style="color:var(--syntax-text-color)">)</span>
    <span style="color:var(--syntax-text-color)">{</span>
        <span style="color:var(--syntax-text-color)">Console</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">WriteLine</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">player</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Name</span><span style="color:var(--syntax-text-color)">);</span>
    <span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>



在许多情况下,出现 N+1 问题是因为开发人员忘记使用预先加载。如果没有它,Entity Framework Core 不会预先获取相关实体,从而导致在访问导航属性时出现多个查询。


<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">FetchWithLazyLoadingAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
    <span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">guilds</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">context</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Guilds</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">ToListAsync</span><span style="color:var(--syntax-text-color)">();</span>

    <span style="color:var(--syntax-declaration-color)">foreach</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">guild</span> <span style="color:var(--syntax-declaration-color)">in</span> <span style="color:var(--syntax-text-color)">guilds</span><span style="color:var(--syntax-text-color)">)</span>
    <span style="color:var(--syntax-text-color)">{</span>
        <span style="color:var(--syntax-text-color)">logger</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">LogInformation</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"Guild:\t{GuildName} have Players:\t{PlayersCount}"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">guild</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Name</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">guild</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Players</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Count</span><span style="color:var(--syntax-text-color)">);</span>
    <span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>



上面的代码会产生查询,其中 是数据库中的公会数量:N+1N


<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>06:44:49:503 info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (1ms) [Parameters=[@__get_Item_0='?' (DbType = Guid)], CommandType='Text', CommandTimeout='30']
      SELECT "p"."Id", "p"."GuildId", "p"."Username"
      FROM "Players" AS "p"
      WHERE "p"."GuildId" = @__get_Item_0
06:44:49:523 info: DatabasesSandbox.Services.GameMonitorService[0]
      Guild:    Guild 16 have Players:  100
06:44:50:696 info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (0ms) [Parameters=[@__get_Item_0='?' (DbType = Guid)], CommandType='Text', CommandTimeout='30']
      SELECT "p"."Id", "p"."GuildId", "p"."Username"
      FROM "Players" AS "p"
      WHERE "p"."GuildId" = @__get_Item_0
06:44:50:709 info: DatabasesSandbox.Services.GameMonitorService[0]
      Guild:    Guild 18 have Players:  100

</code></span></span>



使用 Entity Framework Core 优化数据库性能的策略

要缓解 N+1 查询问题并优化数据库性能,请考虑使用以下策略:

1. 预先加载

预先加载在单个查询中检索相关实体以及主实体,从而减少数据库往返次数。使用 Include 方法可以指定要加载的导航属性:


<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">FetchWithEagerLoadingAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
    <span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">guilds</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">context</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Guilds</span>
                              <span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">Include</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">g</span> <span style="color:var(--syntax-text-color)">=></span> <span style="color:var(--syntax-text-color)">g</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Players</span><span style="color:var(--syntax-text-color)">)</span>
                              <span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">ToListAsync</span><span style="color:var(--syntax-text-color)">();</span>

    <span style="color:var(--syntax-declaration-color)">foreach</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">guild</span> <span style="color:var(--syntax-declaration-color)">in</span> <span style="color:var(--syntax-text-color)">guilds</span><span style="color:var(--syntax-text-color)">)</span>
    <span style="color:var(--syntax-text-color)">{</span>
        <span style="color:var(--syntax-text-color)">logger</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">LogInformation</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"Guild:\t{GuildName} have Players:\t{PlayersCount}"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">guild</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Name</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">guild</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Players</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Count</span><span style="color:var(--syntax-text-color)">);</span>
    <span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>



此方法只产生一个查询:


<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>06:47:45:534 info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT "g"."Id", "g"."Name", "p"."Id", "p"."GuildId", "p"."Username"
      FROM "Guilds" AS "g"
      LEFT JOIN "Players" AS "p" ON "g"."Id" = "p"."GuildId"
      ORDER BY "g"."Id"
06:47:48:820 info: DatabasesSandbox.Services.GameMonitorService[0]
      Guild:    Guild 8 have Players:   100
06:47:49:649 info: DatabasesSandbox.Services.GameMonitorService[0]
      Guild:    Guild 9 have Players:   100
06:47:50:509 info: DatabasesSandbox.Services.GameMonitorService[0]
      Guild:    Guild 25 have Players:  100
06:47:51:371 info: DatabasesSandbox.Services.GameMonitorService[0]
      Guild:    Guild 6 have Players:   100
</code></span></span>



2. 显式加载

显式加载允许您按需加载相关实体。这可以使用以下方法完成:Entry


<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">FetchWithExplicitLoadingAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
    <span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">explicitGuild</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-text-color)">context</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Guilds</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">OrderBy</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">g</span> <span style="color:var(--syntax-text-color)">=></span> <span style="color:var(--syntax-text-color)">g</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Name</span><span style="color:var(--syntax-text-color)">).</span><span style="color:var(--syntax-name-color)">First</span><span style="color:var(--syntax-text-color)">();</span>
    <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">context</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">Entry</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">explicitGuild</span> <span style="color:var(--syntax-text-color)">)</span>
                 <span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">Collection</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">b</span> <span style="color:var(--syntax-text-color)">=></span> <span style="color:var(--syntax-text-color)">b</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Players</span><span style="color:var(--syntax-text-color)">).</span><span style="color:var(--syntax-name-color)">LoadAsync</span><span style="color:var(--syntax-text-color)">();</span>

    <span style="color:var(--syntax-text-color)">logger</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">LogInformation</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"Guild:\t{GuildName} have Players:\t{PlayersCount}"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">explicitGuild</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Name</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">explicitGuild</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Players</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Count</span><span style="color:var(--syntax-text-color)">);</span>

    <span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">guilds</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">context</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Guilds</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">OrderBy</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">g</span> <span style="color:var(--syntax-text-color)">=></span> <span style="color:var(--syntax-text-color)">g</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Name</span><span style="color:var(--syntax-text-color)">).</span><span style="color:var(--syntax-name-color)">ToListAsync</span><span style="color:var(--syntax-text-color)">();</span>

    <span style="color:var(--syntax-declaration-color)">foreach</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">guild</span> <span style="color:var(--syntax-declaration-color)">in</span> <span style="color:var(--syntax-text-color)">guilds</span><span style="color:var(--syntax-text-color)">)</span>
    <span style="color:var(--syntax-text-color)">{</span>
        <span style="color:var(--syntax-text-color)">logger</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">LogInformation</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"Guild:\t{GuildName} have Players:\t{PlayersCount}"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">guild</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Name</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">guild</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Players</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Count</span><span style="color:var(--syntax-text-color)">);</span>
    <span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>



显式加载允许您控制何时加载相关实体,从而避免不必要的查询。在下面的日志中,你可以看到只执行了一个查询来获取指定公会的相关实体,但所有其他公会都没有加载,而是执行了额外的查询来获取它们的相关实体:


<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>07:18:22:634 info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (0ms) [Parameters=[@__get_Item_0='?' (DbType = Guid)], CommandType='Text', CommandTimeout='30']
      SELECT "p"."Id", "p"."GuildId", "p"."Username"
      FROM "Players" AS "p"
      WHERE "p"."GuildId" = @__get_Item_0
07:18:30:019 info: DatabasesSandbox.Services.GameMonitorService[0]
      Guild:    Guild 0 have Players:   100
07:18:31:043 info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT "g"."Id", "g"."Name"
      FROM "Guilds" AS "g"
      ORDER BY "g"."Name"
07:18:34:959 info: DatabasesSandbox.Services.GameMonitorService[0]
      Guild:    Guild 0 have Players:   100
07:18:36:855 info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (0ms) [Parameters=[@__get_Item_0='?' (DbType = Guid)], CommandType='Text', CommandTimeout='30']
      SELECT "p"."Id", "p"."GuildId", "p"."Username"
      FROM "Players" AS "p"
      WHERE "p"."GuildId" = @__get_Item_0
07:18:36:869 info: DatabasesSandbox.Services.GameMonitorService[0]
      Guild:    Guild 1 have Players:   100
07:18:38:634 info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (0ms) [Parameters=[@__get_Item_0='?' (DbType = Guid)], CommandType='Text', CommandTimeout='30']
      SELECT "p"."Id", "p"."GuildId", "p"."Username"
      FROM "Players" AS "p"
      WHERE "p"."GuildId" = @__get_Item_0
07:18:38:648 info: DatabasesSandbox.Services.GameMonitorService[0]
      Guild:    Guild 10 have Players:  100
</code></span></span>



3. 查询投影

查询投影涉及从数据库中仅选择所需的数据,从而减少通过网络传输的数据量。您可以使用该方法将查询结果投影到自定义类型或匿名类型中。例如:Select


<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">FetchWithQueryProjectionAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
    <span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">guilds</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">context</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Guilds</span>
        <span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">Select</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">g</span> <span style="color:var(--syntax-text-color)">=></span> <span style="color:var(--syntax-declaration-color)">new</span>
        <span style="color:var(--syntax-text-color)">{</span>
            <span style="color:var(--syntax-text-color)">Name</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-text-color)">g</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Name</span><span style="color:var(--syntax-text-color)">,</span>
            <span style="color:var(--syntax-text-color)">Players</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-text-color)">g</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Players</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">Select</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">p</span> <span style="color:var(--syntax-text-color)">=></span> <span style="color:var(--syntax-text-color)">p</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Username</span><span style="color:var(--syntax-text-color)">).</span><span style="color:var(--syntax-name-color)">ToList</span><span style="color:var(--syntax-text-color)">()</span>
        <span style="color:var(--syntax-text-color)">})</span>
        <span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">ToListAsync</span><span style="color:var(--syntax-text-color)">();</span>

    <span style="color:var(--syntax-declaration-color)">foreach</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">guild</span> <span style="color:var(--syntax-declaration-color)">in</span> <span style="color:var(--syntax-text-color)">guilds</span><span style="color:var(--syntax-text-color)">)</span>
    <span style="color:var(--syntax-text-color)">{</span>
        <span style="color:var(--syntax-text-color)">logger</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">LogInformation</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"Guild:\t{GuildName} have Players:\t{PlayersCount}"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">guild</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Name</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">guild</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Players</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Count</span><span style="color:var(--syntax-text-color)">);</span>
    <span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>



通过投影查询结果,您可以通过仅获取必要的数据来优化数据库性能。这有助于减少查询数量并提高应用程序的效率,如您在日志中观察到的那样:


<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>07:06:56:051 info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT "g"."Name", "g"."Id", "p"."Username", "p"."Id"
      FROM "Guilds" AS "g"
      LEFT JOIN "Players" AS "p" ON "g"."Id" = "p"."GuildId"
      ORDER BY "g"."Id"
07:07:06:805 info: DatabasesSandbox.Services.GameMonitorService[0]
      Guild:    Guild 8 have Players:   100
07:07:07:965 info: DatabasesSandbox.Services.GameMonitorService[0]
      Guild:    Guild 9 have Players:   100
07:07:08:980 info: DatabasesSandbox.Services.GameMonitorService[0]
      Guild:    Guild 25 have Players:  100
</code></span></span>



4. (奖金)禁用延迟加载

如果您的应用程序中不需要延迟加载,您可以禁用它以防止意外行为并最大限度地降低 N+1 查询问题的风险。
您可以通过在 Entity Framework Core 上下文配置中将选项设置为 来全局执行此操作。例如:UseLazyLoadingProxiesfalse


<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">protected</span> <span style="color:var(--syntax-declaration-color)">override</span> <span style="color:var(--syntax-declaration-color)">void</span> <span style="color:var(--syntax-name-color)">OnConfiguring</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">DbContextOptionsBuilder</span> <span style="color:var(--syntax-text-color)">optionsBuilder</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
    <span style="color:var(--syntax-text-color)">optionsBuilder</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">UseLazyLoadingProxies</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">false</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>



结论

N+1 查询问题是 Entity Framework Core 中一个众所周知的性能问题,可能会影响数据库查询的效率。通过实施预先加载、显式加载、查询投影和禁用延迟加载等策略,您可以优化应用程序的性能。了解这些技术可以显著提高应用程序的响应能力和数据库效率。