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