ひでぼ~blog

C#ときどきゲーム

EFCoreのTimestampで楽観的排他制御を行う

EFCoreのTimestampを使って楽観的排他制御をやってみます。

実行環境

Entityを用意する

次のようなEntityを用意しました。

public record Todo
{
    public int Id { get; set; }

    public string? Content { get; set; }

    public DateTime Created { get; set; }

    public DateTime? Updated { get; set; }

    public DateTime? Deleted { get; set; }

    [Timestamp]
    public byte[] Version { get; set; } = null!;
}

楽観的排他制御のためにVersionというプロパティを用意し、Timestamp属性をつけておきます。 テスト用のデータを入れておきました。

適当なカラムを更新する

コマンド引数で指定した時間だけ実行を遅らせるようにして、プログラムを同時に実行するようにします。

var dbContext = new AppDbContext();

if (!int.TryParse(args.FirstOrDefault(), out var delay))
{
    delay = default;
}

var todo = await dbContext.Todos.FirstAsync();

todo.Content = "ガチアサリ";

await Task.Delay(delay);

await dbContext.SaveChangesAsync();

実行してみる

複数のターミナルから'dotnet run'をババっと実行してみます。ドキュメントによるとDbUpdateConcurrencyExceptionが発生するそうですがどうでしょう。

Unhandled exception. Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: The database operation was expected to affect 1 row(s), but actually affected 0 row(s); data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.

無事?DbUpdateConcurrencyExceptionが発生しました。

参考

learn.microsoft.com