0

私はEFコアを使用しています(ASP.NETコア内で、要求/スコープごとにDbContextが存在します)。トランザクションを使用してEFコアの一意の制約付き列を変更する

Employee実体は、このプロパティを持っています

public bool? IsBoss { get; set; } 

と、この設定:

entityBuilder.Property(b => b.IsBoss).IsRequired(false); 
entityBuilder.HasIndex(b => b.IsBoss).IsUnique(); 

これを濾過したインデックスを作成し、その唯一の真の、1偽の、しかし、多くのNULLがあることができます。

私のアプリでは、常に1人の従業員がIsBoss==trueであることが必要です。

2人の従業員を交換したいとします。

employee1.IsBoss = null; 
employee2.IsBoss = true; 
context.SaveChanges(); 

これは、一意制約違反例外をスローします。

私はトランザクションでそれをラップすることにより、これを修正することができます

using (var transaction = context.BeginTransaction()) 
{ 
    try 
    { 
    employee1.IsBoss = null; 
    context.SaveChanges(); 

    employee2.IsBoss = true; 
    context.SaveChanges(); 

    transaction.Commit(); 
    } 
    catch 
    { 
    transaction.Rollback(); 
    } 
} 

私の質問は:なぜ最初のアプローチは失敗するのか?私はEF Core automatically wrapsをトランザクション内のすべてと考えました。なぜ私はトランザクションを使用する必要がありますか?

答えて

1

最初の方法は、トランザクションと異なる理由により失敗します。 Tracking issue on EF repoはあなたと同じシナリオをカバーしています。

SaveChangesが呼び出されると、EFは変更を処理し、データベースに送信するコマンドを計算します。この一連のコマンドは依存関係を持つことができます。あなたの場合と同じように、の値をnullに設定してから、employee2の値をtrueに設定する必要があります。 EFはコマンドをソートして実行する順序を調べます。 EFは、外部キーの制約に基づいてこのソートを行いました。しかし、この問題が報告されたとき、私たちはユニークなインデックスを考慮しなかったので、コマンドが間違った順序で送信されてユニークな制約違反が発生しました。

この問題は、現在のコードベースではすでに修正されています。これは次回の一般公開で利用可能になります。一方、回避策として、SaveChangesを2回目のコードと同じように2回呼び出す必要があります。 SaveChangesを複数回呼び出すと、データベースに送信されるコマンドの順序を制御できます。両方の変更を1つのアトミック操作にしない限り、トランザクション内でラップする必要はありません。各SaveChangesには、ユーザーがトランザクションを開始しない限り、独自のトランザクションがあります。

+0

詳細な回答ありがとうございました...私はそれがバグであるとは思わなかった!あなたは、次のリリースでは、1つの 'SaveChanges()'と1つのトランザクションだけを使ってこの1)と2)を行うことができると言っていますか? – grokky

+1

答えはどちらの質問でもyesです:あなたは 'SaveChanges'を1つだけ必要とし、デフォルトではトランザクションでラップされます。 – Smit

関連する問題