一覧画面が遅い、ログに同じような照会が何百回も出る——典型が N+1。親を1回取ったあと、関連を行ごとに追加で取りに行くパターンだ。EF Core では Include 忘れや遅延ロード、ループ内の別クエリが原因になりやすい。
先に結論
- まず発行SQL(またはコマンドログ)で「同じ表への連続読み」を確認する
- 対策の第一候補は
Include / ThenInclude、または投影(必要な列だけ DTO)
- ループ内で
FirstAsync や別 DbSet を叩いていないかを疑う
- 遅延ロードを有効にしているなら、本番前にオフ+明示ロードへ寄せるのが無難
見つけ方
Microsoft.EntityFrameworkCore.Database.Command を Information にしてコマンド数を見る
- 1リクエストで「親1 + 子N」の形になっていないか数える
- デバッガや MiniProfiler でリクエスト単位のクエリ一覧を見る
- 画面上は速いがバッチ処理だけ遅い、も同じ症状になりやすい
よくある原因
- コレクションを foreach しながらナビゲーションプロパティに触る
- Include なしで関連を参照(遅延ロード or 暗黙の追加照会)
- サービス層で「ID のリストを回して1件ずつ取得」
- Include の付けすぎで巨大結合になり、別問題に化けることもある
関連: EF Core DbUpdateException の切り分け
対策の選び方
- Include: 関連をまとめて取る。深さに注意
- 投影: 画面に必要な列だけ
Select して DTO 化(過剰 Include を避けやすい)
- 一括取得: ID 群を
Contains でまとめて取り、メモリで紐付け
- 分割クエリ: 巨大結合が重いときは
AsSplitQuery も検討
ざっくりまとめ
- 症状は「コマンド数が行数に比例」
- ログで見つけ、Include/投影/一括取得で潰す
- Include 過多も別の遅さの原因になるので、必要最小限にする
関連記事