おーみんブログ

C#, ASP.NET Core, Unityが大好きです。

EFCoreのDetectChangesを無効にしたらどれくらいInsertが速くなるのか?

はじめに

現在常駐しているお客様先の上司とお話していたところ、EFCore(Entity Framework Core)のDetectChangesというキーワードが出てきて何だそれは!!?という状態になったので調べてみました。

ChangeTracker.AutoDetectChangesEnabledプロパティ

EFCoreではデータベースの更新処理(Add, Update, Delete)があった際にDetectChanges()メソッドによる整合性チェック(MSのドキュメントでは変更の追跡という説明がされています)が行われます。

ここら辺は以下のサイトの説明がとても分かりやすかったです。

DetectChanges() の処理を簡単に言うと、DB から取得したデータと、現在のデータを比べて、変更があればデータのステータス EntityState を更新している。 しかし、DbSet.Add や DbSet.Remove は、対象データのステータスを EntityState.Added や EntityState.Deleted に更新するので、DetectChanges() の実行は不要。

csharpvbcomparer.blogspot.com

よってInsertやDeleteの際はChangeTracker.AutoDetectChangesEnabledをfalseにして実行してみると良さそうです。

サンプルコード

サンプルコードはPersonモデル情報をSQLiteへ数十万件Insertするパフォーマンスを調査する内容になっています。 カラムはPersonId, Name, Mail, Ageといたってシンプルな感じです。

public async Task<IActionResult> Create()
{
    // DetectChangesをfalseにするかこちらで調整します。デフォルトはtrueです。
    // _context.ChangeTracker.AutoDetectChangesEnabled = false;
    Stopwatch sw = new();
    sw.Start();
    // 今回は20万件のデータと50万件のデータで確認してみました。
    for (var i = 0; i < 500000; i++)
    {
        var personData = new Person
        {
            Name = $"name{i + 1}",
            Mail = $"mailAddres{i + 1}",
            Age = i + 1
        };
        _context.Add(personData);
    }
    await _context.SaveChangesAsync();
    sw.Stop();
    TimeSpan ts = sw.Elapsed;
    ViewData["Elapsed"] = $"{ts.Seconds}秒{ts.Milliseconds}ミリ秒";
    return View();
}

それぞれStopwatchクラスで時間をはかった結果以下のようになりました。

20万件 50万件
AutoDetectChangesEnabled = false 13秒829ミリ秒 33秒310ミリ秒
AutoDetectChangesEnabled = true 14秒562ミリ秒 35秒465ミリ秒

ふむふむ。たしかに変更の追跡がない分速度は速くなっています。 今回はそれほど大きくは変わっていないですが、データの形によっては大きく変わってくるのかな?

おわりに

Entity Frameworkについてもっと勉強して理解度を深めていけるように頑張ります!!!