概要
Entity Framework Core (EF Core) がデータベースに対してSQLを実行する際の待機時間(タイムアウト)を設定する方法です。 デフォルト(通常30秒)よりも長い時間がかかる集計処理や一括更新処理を行う場合に、意図的な切断(TimeoutException)を防ぐために設定値を延長します。
仕様(入出力)
- 入力: タイムアウト秒数(int)。
- 出力: 設定が適用された
DbContextインスタンス。 - 前提: .NET 6.0以上。
- リレーショナルデータベースプロバイダ(SQLite, SQL Server, PostgreSQL等)で有効です。
- 本コードは
Microsoft.EntityFrameworkCore.Sqliteを使用します。
基本の使い方
DbContextOptionsBuilder のプロバイダ設定用メソッド(UseSqlite, UseSqlServer 等)の第2引数で、アクションデリゲートを使用して設定します。
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Data Source=app.db", options =>
{
// タイムアウトを 180秒(3分)に設定
options.CommandTimeout(180);
});
}
コード全文
ここではコンソールアプリケーションで DbContext を初期化する際に、タイムアウト値を構成する完全な例を提示します。 DIコンテナ(AddDbContext)を使用する場合も、設定記述の場所が Program.cs に変わるだけで記法は同じです。
外部ライブラリとして Microsoft.EntityFrameworkCore.Sqlite が必要です。
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
public class Program
{
public static async Task Main()
{
// タイムアウト設定が適用されたContextを使用
using (var context = new LongRunningContext())
{
// DB作成
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();
Console.WriteLine($"現在のタイムアウト値: {context.Database.GetCommandTimeout()} 秒");
// 通常通りデータ操作を実行
context.Products.Add(new Product { Name = "Heavy Calculation Item" });
await context.SaveChangesAsync();
Console.WriteLine("データベース操作が完了しました。");
}
}
}
// DbContext定義
public class LongRunningContext : DbContext
{
// コンストラクタでオプションを受け取るパターン(DI使用時など)
public LongRunningContext() { }
public LongRunningContext(DbContextOptions<LongRunningContext> options) : base(options) { }
public DbSet<Product> Products => Set<Product>();
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// 既に設定されていない場合のみ設定(DIコンテナでの設定を優先するため)
if (!optionsBuilder.IsConfigured)
{
// 接続文字列
var connectionString = "Data Source=app.db";
optionsBuilder.UseSqlite(connectionString, sqliteOptions =>
{
// 【ここがポイント】
// コマンド実行のタイムアウト時間を 180秒 (3分) に設定
// デフォルトは通常 30秒
sqliteOptions.CommandTimeout(180);
});
// ログ出力(動作確認用)
optionsBuilder.LogTo(Console.WriteLine, Microsoft.Extensions.Logging.LogLevel.Information);
}
}
}
// エンティティ定義
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
}
カスタムポイント
- DIコンテナでの設定(ASP.NET Core等):
Program.csでAddDbContextを使用する場合は以下のように記述します。C#builder.Services.AddDbContext<LongRunningContext>(options => options.UseSqlite( connectionString, providerOptions => providerOptions.CommandTimeout(180) ) ); - 一時的な変更: アプリケーション全体ではなく、特定の重い処理の間だけタイムアウトを延ばしたい場合は、コード内で動的に変更可能です(応用を参照)。
注意点
- デフォルト値: 多くのプロバイダではデフォルトが30秒です。設定しない場合、30秒を超えるクエリは
OperationCanceledExceptionやSqlExceptionで失敗します。 - 長すぎる設定: 無闇に長い時間(または無限
0)を設定すると、データベースのロックが長時間解放されず、他のユーザーのアクセスを阻害する原因になります。必要な処理にのみ適用してください。 - 接続タイムアウトとの違い:
CommandTimeoutは「クエリの実行待ち時間」です。「DBへの接続待ち時間」であるConnect Timeout(接続文字列で指定)とは別物ですので混同しないようにしてください。
応用
特定の処理だけタイムアウトを変更する
Context全体の設定を変えるのではなく、特定の重いレポート出力処理の時だけ時間を延ばしたい場合の方法です。
using (var context = new LongRunningContext())
{
// このコンテキストインスタンスのタイムアウトを一時的に5分に変更
context.Database.SetCommandTimeout(300);
// 時間のかかる集計処理を実行
// await context.Database.ExecuteSqlRawAsync("EXEC VeryHeavyStoredProcedure");
// 処理が終わったら戻すことも可能(またはContextを破棄)
context.Database.SetCommandTimeout(null); // nullでデフォルトに戻る
}
まとめ
バッチ処理や大量データのマイグレーション時など、明確に時間がかかると分かっている場合に有効です。
UseSqlite や UseSqlServer のオプション用ラムダ式内で CommandTimeout を指定します。
全体設定は OnConfiguring または AddDbContext で行い、局所的な設定は Database.SetCommandTimeout を使用します。
