bucket-sort logo bucket-sort

プログラミングとインフラエンジニアリングの覚え書き

  • Posts
  • About
  • Contact
  1. Home
  2. All Posts
  3. [C#] EF Core のクエリ実行と追跡を理解する

[C#] EF Core のクエリ実行と追跡を理解する

Jul 27, 2026 C# , .NET , Entity Framework Core bucket-sort

前回は、EF Core の基本部品を見ました。

今回は、クエリ実行と追跡を扱います。
EF Core では LINQ で問い合わせを書けますが、その LINQ がいつ実行されるのか、どこまで SQL に変換されるのかを理解しておくことが重要です。

クエリはすぐには実行されない

次のコードを見てください。

IQueryable<Car> query = context.Cars
    .Where(car => car.Make == "Honda")
    .OrderBy(car => car.PetName);

この時点では、まだデータベースへ問い合わせは送られていません。
IQueryable<T> は、問い合わせの形を表しているだけです。

実際に実行されるのは、結果を必要としたタイミングです。

List<Car> cars = await query.ToListAsync();

ToListAsync()、FirstOrDefaultAsync()、CountAsync() などを呼ぶと、SQL が生成されて実行されます。

すべてのレコードを取得する

List<Car> cars = await context.Cars
    .OrderBy(car => car.Id)
    .ToListAsync();

これは、Inventory テーブルから全件を取得します。
件数が少ないテーブルなら問題ありませんが、大きなテーブルでは危険です。

実務では、条件、並べ替え、件数制限を意識しましょう。

List<Car> cars = await context.Cars
    .Where(car => car.Make == "Honda")
    .OrderBy(car => car.Id)
    .Take(50)
    .ToListAsync();

条件で絞り込む

string make = "Honda";

List<Car> cars = await context.Cars
    .Where(car => car.Make == make)
    .ToListAsync();

EF Core は、変数 make を SQL パラメーターとして扱います。
文字列連結で SQL を組み立てるより安全です。

部分一致も書けます。

List<Car> cars = await context.Cars
    .Where(car => car.PetName.Contains("a"))
    .ToListAsync();

ただし、文字列操作がどのような SQL に変換されるかは、プロバイダーや照合順序にも影響されます。

並べ替えとページング

int page = 2;
int pageSize = 10;

List<Car> cars = await context.Cars
    .OrderBy(car => car.Id)
    .Skip((page - 1) * pageSize)
    .Take(pageSize)
    .ToListAsync();

ページングでは、必ず安定した並べ替えを指定しましょう。
Skip と Take だけでは、データベースがどの順番で返すか保証されません。

1 件だけ取得する

Car? car = await context.Cars
    .FirstOrDefaultAsync(car => car.Id == 1);

条件に一致するものがなければ null になります。

必ず 1 件ある前提なら SingleAsync() や SingleOrDefaultAsync() もありますが、件数チェックの意味が変わります。

Car car = await context.Cars.SingleAsync(car => car.Id == 1);

SingleAsync() は、0 件でも 2 件以上でも例外になります。
主キー検索なら FindAsync() も使えます。

Car? car = await context.Cars.FindAsync(1);

集計する

int count = await context.Cars.CountAsync();

int hondaCount = await context.Cars
    .CountAsync(car => car.Make == "Honda");

最大値や最小値も取得できます。

int maxId = await context.Cars.MaxAsync(car => car.Id);

集計は、通常データベース側で実行されます。
全件をアプリケーションへ読み込んでから数えるより効率的です。

Any と All

存在確認には AnyAsync() が便利です。

bool exists = await context.Cars
    .AnyAsync(car => car.Make == "Honda");

すべての行が条件を満たすか確認するには AllAsync() を使います。

bool allNamed = await context.Cars
    .AllAsync(car => car.PetName != "");

存在確認だけなら、CountAsync() > 0 より AnyAsync() の方が意図が明確です。

サーバー評価とクライアント評価

EF Core の LINQ は、可能な限り SQL に変換され、データベース側で実行されます。

List<Car> cars = await context.Cars
    .Where(car => car.Make == "Honda")
    .ToListAsync();

この条件は SQL に変換できます。

一方、C# の任意のメソッドがすべて SQL に変換できるわけではありません。

static bool IsSpecialName(string value)
{
    return value.StartsWith("A") && value.Length > 3;
}

このような独自メソッドを Where の中で使うと、SQL に変換できず問題になることがあります。

// 避けたい例
List<Car> cars = await context.Cars
    .Where(car => IsSpecialName(car.PetName))
    .ToListAsync();

データベースで実行できる条件と、アプリ側でしか実行できない処理を分けて考えましょう。

List<Car> candidates = await context.Cars
    .Where(car => car.PetName.StartsWith("A"))
    .ToListAsync();

List<Car> result = candidates
    .Where(car => IsSpecialName(car.PetName))
    .ToList();

先にデータベース側で絞ってから、必要な分だけアプリ側で処理するのが安全です。

追跡ありクエリ

通常、EF Core は取得したエンティティを追跡します。

Car? car = await context.Cars.FirstOrDefaultAsync(car => car.Id == 1);

if (car != null)
{
    car.PetName = "Updated";
    await context.SaveChangesAsync();
}

このように、取得後に変更して保存する場合は追跡が役立ちます。

追跡なしクエリ

読み取り専用なら、追跡なしにできます。

List<Car> cars = await context.Cars
    .AsNoTracking()
    .Where(car => car.Make == "Honda")
    .ToListAsync();

追跡なしにすると、変更検知の対象にならないため、読み取り処理では軽くなります。

一覧表示、検索結果、レポートなど、変更しないデータには AsNoTracking() を検討しましょう。

投影で必要な列だけ取得する

エンティティ全体が不要なら、必要な形へ投影します。

var cars = await context.Cars
    .AsNoTracking()
    .Select(car => new
    {
        car.Id,
        DisplayName = car.Color + " " + car.Make,
        car.PetName
    })
    .ToListAsync();

DTO を使うこともできます。

public sealed class CarListItem
{
    public int Id { get; init; }
    public string DisplayName { get; init; } = "";
    public string PetName { get; init; } = "";
}
List<CarListItem> cars = await context.Cars
    .AsNoTracking()
    .Select(car => new CarListItem
    {
        Id = car.Id,
        DisplayName = car.Color + " " + car.Make,
        PetName = car.PetName
    })
    .ToListAsync();

画面表示や API 応答では、エンティティをそのまま返すより DTO へ投影する方が扱いやすいことが多いです。

モデル主導とデータベース主導

EF Core の開発スタイルには、大きく 2 つの考え方があります。

モデルからデータベースを作る

C# のエンティティと DbContext を先に定義し、マイグレーションでデータベースを作ります。
新規開発で使いやすい方法です。

既存データベースからモデルを作る

既存のデータベーススキーマを読み取り、DbContext とエンティティを生成します。
既存システムや既存DBへ接続する場合に便利です。

どちらが正しいというより、プロジェクトの出発点がどこにあるかで選びます。

まとめ

EF Core のクエリは、LINQ で表現され、必要になったタイミングで SQL として実行されます。
どこまでデータベース側で処理されるのか、どこからアプリ側で処理されるのかを意識することが重要です。

読み取り専用では AsNoTracking()、画面表示では投影、更新する場合は追跡あり、と使い分けると無駄が減ります。
次回は、EF Core のコマンドラインツールとマイグレーションを使い、モデル変更をデータベースへ反映する方法を見ます。

C# .NET Entity Framework Core LINQ AsNoTracking クエリ
← [C#] EF Core の基本部品を理解する [C#] EF Core のコマンドライン操作とマイグレーションを理解する →

Related Posts

  • [C#] EF Core でデータを問い合わせる Jul 30, 2026
  • [C#] EF Core でデータベースの準備と初期データを扱う Aug 8, 2026
  • [C#] EF Core とリポジトリでデータ操作を整理する Aug 7, 2026
  • [C#] DbContext の設定と保存処理を拡張する Aug 6, 2026

Table of Contents

  • クエリはすぐには実行されない
  • すべてのレコードを取得する
  • 条件で絞り込む
  • 並べ替えとページング
  • 1 件だけ取得する
  • 集計する
  • Any と All
  • サーバー評価とクライアント評価
  • 追跡ありクエリ
  • 追跡なしクエリ
  • 投影で必要な列だけ取得する
  • モデル主導とデータベース主導
    • モデルからデータベースを作る
    • 既存データベースからモデルを作る
  • まとめ

Recent Posts

  • [C#] EF Core でデータベースの準備と初期データを扱う Aug 8, 2026
  • [C#] EF Core とリポジトリでデータ操作を整理する Aug 7, 2026
  • [C#] DbContext の設定と保存処理を拡張する Aug 6, 2026
  • [C#] EF Core のエンティティと表示用モデルを設計する Aug 5, 2026
  • [C#] EF Core を中心にデータアクセス層を分ける Aug 4, 2026

Categories

  • C#150
  • .NET149
  • AWS27
  • Laravel16
  • Entity Framework Core15
  • Linux15
  • MySQL9
  • Apache8
  • PHP8
  • Data Access6
  • DynamoDB6
  • セキュリティ6
  • Nginx5
  • WordPress4
  • インフラ4
  • Hugo3
  • .NET Framework1
  • Aurora1
  • Diagnostics1
  • Filament1

Tags

  • C#
  • .NET
  • AWS
  • Laravel
  • コレクション
  • PHP
  • Entity Framework Core
  • セキュリティ
  • MySQL
  • Linux
  • パフォーマンス
  • Apache
  • LINQ
  • System.Collections.Generic
  • デリゲート
  • リフレクション
  • ADO.NET
  • Code Snippet
  • DynamoDB
  • NoSQL
  • PHP-FPM
  • RDS
  • System.Collections
  • Windows
  • メタデータ
  • メモリ管理
  • CIL
  • DoS
  • Nginx
  • SQL Server
  • WordPress
  • ラムダ式
  • 監視
  • 設計
  • Amazon Linux 2023
  • Delegate
  • Docker
  • IDisposable
  • Ipset
  • Iptables
  • LINQ to Objects
  • OPCache
  • Pointer
  • Reflection
  • System.Collections.Specialized
  • Unsafe
  • Webサーバー
  • アセンブリ
  • インターフェース
  • オブジェクト指向
Powered by Hugo & Explore Theme.