【C#】EF Coreが実行するSQLをコンソールに出力して確認する方法

目次

概要

Entity Framework Core (EF Core) が裏側でどのようなSQLを発行しているかを可視化し、デバッグやパフォーマンス分析を行うための実装です。 DbContext の設定で LogTo メソッドを使用し、標準出力(コンソール)にリアルタイムでクエリ情報を流します。

仕様(入出力)

  • 入力: 検索したいタスクの優先度(文字列)。
  • 出力: 実行されたSQL文と、検索条件にヒットしたタスク一覧。
  • 前提: .NET 6.0以上。Microsoft.EntityFrameworkCore.Sqlite を使用。

基本の使い方

OnConfiguring メソッド内で LogTo を呼び出します。情報の氾濫を防ぐため、DbLoggerCategory でSQLコマンドのみにフィルタリングします。

protected override void OnConfiguring(DbContextOptionsBuilder options)
{
    options.UseSqlite("Data Source=mydb.db")
        .LogTo(
            Console.WriteLine, // 出力先
            new[] { DbLoggerCategory.Database.Command.Name }, // SQL実行ログのみ
            LogLevel.Information // 情報レベル
        )
        .EnableSensitiveDataLogging(); // パラメータ値も表示
}

コード全文

ここでは「タスク管理システム」を題材に、特定の優先度のタスクを検索した際に、どのような SELECT 文が発行されるかを確認します。

外部ライブラリとして Microsoft.EntityFrameworkCore.Sqlite が必要です。

dotnet add package Microsoft.EntityFrameworkCore.Sqlite
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

public class Program
{
    public static async Task Main()
    {
        // 1. データの準備(ログ出力付きContextを使用)
        using (var context = new TaskDbContext())
        {
            await context.Database.EnsureDeletedAsync();
            await context.Database.EnsureCreatedAsync();

            if (!context.Tasks.Any())
            {
                context.Tasks.AddRange(
                    new TaskItem { Title = "バグ修正", Priority = "High", DueDate = DateTime.Now.AddDays(1) },
                    new TaskItem { Title = "ドキュメント作成", Priority = "Low", DueDate = DateTime.Now.AddDays(5) },
                    new TaskItem { Title = "定例会議", Priority = "High", DueDate = DateTime.Now }
                );
                await context.SaveChangesAsync();
            }
        }

        Console.WriteLine("\n=== SQLログの確認開始 ===\n");

        // 2. 検索処理の実行
        using (var context = new TaskDbContext())
        {
            var manager = new TaskManager(context);
            // "High" のタスクを検索
            // ここでコンソールに SELECT 文が表示されます
            await manager.PrintTasksByPriorityAsync("High");
        }
    }
}

// 業務ロジッククラス
public class TaskManager
{
    private readonly TaskDbContext _context;

    public TaskManager(TaskDbContext context)
    {
        _context = context;
    }

    public async Task PrintTasksByPriorityAsync(string priority)
    {
        var tasks = await _context.Tasks
            .Where(t => t.Priority == priority)
            .OrderBy(t => t.DueDate)
            .ToListAsync();

        Console.WriteLine($"\n[検索結果: 優先度 {priority}]");
        foreach (var task in tasks)
        {
            Console.WriteLine($"- {task.Title} (期限: {task.DueDate:MM/dd})");
        }
    }
}

// エンティティ定義
public class TaskItem
{
    public int Id { get; set; }
    public string Title { get; set; } = string.Empty;
    public string Priority { get; set; } = string.Empty; // High, Medium, Low
    public DateTime DueDate { get; set; }
}

// DbContext定義
public class TaskDbContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        // SQLiteデータベース接続
        optionsBuilder.UseSqlite("Data Source=tasks_debug.db");

        // 【ログ出力設定】
        // Console.WriteLine にログを流す
        optionsBuilder.LogTo(
            action: Console.WriteLine,
            // フィルタ: SQLコマンド実行イベントのみを通す(これをしないと接続確立等のログで埋まる)
            events: new[] { DbLoggerCategory.Database.Command.Name },
            minimumLevel: LogLevel.Information
        );

        // 【機密データログ設定】
        // SQLパラメータの値(WHERE Priority = ? の中身)を出力する設定
        // ※本番環境では無効にすること
        optionsBuilder.EnableSensitiveDataLogging();
    }
}

コンソール出力例

info: 1/16/2026 10:00:00.000 ... (Microsoft.EntityFrameworkCore.Database.Command)
      Executed DbCommand (2ms) [Parameters=[@__priority_0='High' (Size = 4)], CommandType='Text', CommandTimeout='30']
      SELECT "t"."Id", "t"."DueDate", "t"."Priority", "t"."Title"
      FROM "Tasks" AS "t"
      WHERE "t"."Priority" = @__priority_0
      ORDER BY "t"."DueDate"

[検索結果: 優先度 High]
- 定例会議 (期限: 01/16)
- バグ修正 (期限: 01/17)

カスタムポイント

  • 出力先の変更: コンソールアプリ以外の場合、Console.WriteLineSystem.Diagnostics.Debug.WriteLine に変更すると、IDEの出力ウィンドウで確認できます。
  • 例外時のみ出力: 常にログを出すのではなく、エラー発生時のみ詳細を知りたい場合は、DbCommandInterceptor を継承したクラスを作成し、CommandFailed メソッド内でのみログ出力を行う高度な方法もあります。
  • クエリ文字列の取得: 実行せずにSQL文字列だけ欲しい場合、EF Core 5.0以降では以下のように書けます。C#var query = context.Tasks.Where(t => t.Priority == "High"); string sql = query.ToQueryString(); // SQL文字列を取得

注意点

  1. 機密情報の取り扱い: EnableSensitiveDataLogging を有効にすると、ユーザー名やパスワードなどの個人情報がログにそのまま出力されます。開発環境(Development)以外では絶対に使用しないでください。
  2. パフォーマンス: コンソール出力は同期的なI/O処理であり、比較的高負荷です。本番環境で大量のアクセスがある場合、パフォーマンス低下の主因になります。
  3. フィルタリング: events 引数を省略すると、データベース接続のオープン/クローズ、トランザクション開始など、大量の内部イベントが出力され、肝心のSQLが見つけにくくなります。

応用

開発環境でのみ有効にする設定 (ASP.NET Core)

appsettings.json の設定値を使い、環境ごとにログレベルを切り替えるのが一般的です。コードを変更する必要がなくなります。

appsettings.Development.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      // SQLコマンドのログレベルだけを下げる
      "Microsoft.EntityFrameworkCore.Database.Command": "Information"
    }
  }
}

まとめ

「データが取れない」「クエリが遅い」と感じたら、まずはこの設定で実際のSQLを確認してください。

LogTo メソッドを使えば、1行追加するだけで発行されるSQLを確認できます。

DbLoggerCategory.Database.Command.Name でフィルタリングすることで、SQL文だけを効率よく抽出できます。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次