1

Transaction Scope を使用して EF コンテキストの悲観的ロックを実装しようとしています。

モデル:

User

Room
    ICollection<User> Users
    int Capacity

コントローラ:

public void List()
{
    using(var context = new MyDBContext())
    {
        // Let's pretend that this method grabs list of users from room and creates list of their names and then dumbs them
        foreach(var item in context.Rooms.First().GetUserNames()) 
            Console.WriteLine(item);
    }
}

public void Join()
{
    using(var context = new MyDBContext())
    {
        var room = context.Rooms.First();
        if(room.Users.Count >= room.Capacity)
            throw new Exception("No room");

        using(var transaction = new Transaction(
                TransactionScopeOption.Required,
                new TransactionOptions
                {
                    IsolationLevel = IsolationLevel.RepeatableRead
                }
            ))
        {
            room = context.Rooms.First(); // Fetch again to get latest version

            if(room.Users.Count >= room.Capacity)
                throw new Exception("No room");

            room.Users.Add(CurrentUser);

            context.SaveChanges();
            transaction.Complete();
        }
    }
}

現在、以下に説明する動作が機能するかどうかの承認を求めています。機能しない場合は、それを可能にするためのフィードバックやアドバイスをお待ちしております.

予想される動作:

3 つのスレッドがあると仮定しましょう: A - 最初に Join() を呼び出します。B - List() を呼び出します。C - Join() を 2 番目に呼び出します。

Room.Capacity は 1 です。

これは私が期待していることです: A は Join() を呼び出します。メソッドは、参加できるかどうかをチェックします。参加できるように見えるので、TransactionScope に入ります。この時点で、B は List() を呼び出します。A はまだ Join() を完了していないため、B はルームにユーザーがいないと考えています。ここで重要なことは、A がトランザクションを終了するまで B は待機せず、最後にコミットされた行のバージョンにアクセスすることです。現在、C は Join() を呼び出しています。A はまだトランザクションを処理しているため、C の最初のルーム テストは成功し、TransactionScope にも入ります。しかし、A はまだ完了していないため、C は今それを待つ必要があります。A がトランザクションを終了し、ルーム内のユーザー数が 1 になりました。A がトランザクションのロックを解除し、C がトランザクションに参加します。Room モデルを再度取得し、キャパシティをテストして参加できないことを確認し、例外をスローします。

私が壊れたかもしれないもの:

IsolationLevel.RepeatableRead についてはよくわかりません。ドキュメントでは、トランザクション中に揮発性データを読み取ることはできますが、変更することはできないと言われています。トランザクション中に新しいデータを追加できます。. 一方では、これにより List() メソッドが行をそのまま読み取れるようになり、TransactionScope が終了するのを待たないことを期待しています。また、テーブルではなく行のみをロックする必要があるため、新しい部屋を追加できます。しかし、Aがまだ実行されているときにCのTransactionScopeのロックをブロックしないのではないかと心配しています。そして最後に、「トランザクション中に揮発性データを読み取ることはできますが、変更することはできません」が TransactionScope にも当てはまるかどうかはわかりません。

要約すると、コードが期待どおりに動作するかどうか教えてください。そうでない場合は、それを達成するためにどのような調整を行う必要がありますか? 前もって感謝します。

4

0 に答える 0