LoginSignup
2
5

More than 3 years have passed since last update.

Entity Framework Coreにおける行ロックと同時実行制御の挫折

Posted at

やりたかったこと

Entity Framework CoreでAzureのSQL Databaseに接続し、

  1. 行の更新は競合させない
  2. 接続エラーは数回リトライする

を実現したかった

挫折した点

行ロックは相変わらずできない

EF Coreになり、行ロックに対応していないかと期待しましたが、まだ未対応。

System.ComponentModel.DataAnnotations.ConcurrencyCheckを使って楽観ロックをすることで、更新を競合させないことは可能。
https://docs.microsoft.com/ef/core/modeling/concurrency

ConcurrencyCheckをいれたときのリトライがいまいち

公式のドキュメントでは、DbUpdateConcurrencyExceptionをキャッチして、問題のデータをアップデートするような例が提示されています。

ところが、現在のDBの値に応じて、保存したい値が異なる場合、次の(1)と(2)で同じような処理を入れる必要があります。
扱うエンティティも異なるので共通化しづらいし、リトライ機構も自分で実装する必要があり、どうもいまいち。

using (var context = new PersonContext())
{
    // データを取得

    // (1) 保存したいデータと現在のDBのデータを比較して保存する値を決める

    var saved = false;
    while (!saved)
    {
        try
        {
            // Attempt to save changes to the database
            context.SaveChanges();
            saved = true;
        }
        catch (DbUpdateConcurrencyException ex)
        {

            foreach (var entry in ex.Entries)
            {
                if (entry.Entity is Person)
                {
                    var proposedValues = entry.CurrentValues;
                    var databaseValues = entry.GetDatabaseValues();

                    foreach (var property in proposedValues.Properties)
                    {
                        var proposedValue = proposedValues[property];
                        var databaseValue = databaseValues[property];

                        // (2) ここで(1)と同じ操作をしなくてはならない
                    }

                    // Refresh original values to bypass next concurrency check
                    entry.OriginalValues.SetValues(databaseValues);
                }
                else
                {
                    throw new NotSupportedException(
                        "Don't know how to handle concurrency conflicts for "
                        + entry.Metadata.Name);
                }
            }
        }
    }
}

参考
https://docs.microsoft.com/ef/core/saving/concurrency

SqlServerRetryingExecutionStrategyはSqlExceptionしか扱わないらしい

Microsoft.EntityFrameworkCore.SqlServerRetryingExecutionStrategyのラッパークラスを作って、DbUpdateConcurrencyExceptionもSELECTからやり直すようにできればシンプルになると考え、作ってみました。

ただ、DbUpdateConcurrencyExceptionが発生しても、そもそもShouldRetryOnに入ってこないようで、挫折

参考
https://docs.microsoft.com/dotnet/api/microsoft.entityframeworkcore.sqlserverretryingexecutionstrategy

まとめ

Entity Framework Coreになっても、まだいまいちなので他の方法を探したほうがよいな、という印象でした。

2
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
5