DbContext
すでに開いている SQL 接続を に指定しないDbContext
場合、 を呼び出したときに が接続を開いたり閉じたりしますSaveChanges
。DbContext
その場合、もちろん、保持しているエンティティが無効な状態にある可能性があることを除いて、アラウンドを保持しても危険はありDbContext
ません (これが SQL 例外がスローされた理由である可能性があるため)。
DbContext
開いている SQL 接続とトランザクションによって提供されるの例を次に示します。
using (var connection = new SqlConnection("my connection"))
{
connection.Open();
using (var transaction = connection.BeginTransaction())
{
using (var context = new DbContext(connection))
{
// Do useful stuff.
context.SaveChanges();
}
transaction.Commit();
}
}
トランザクションのコンテキストで実行される を に指定すると、DbContext
この答えが保持されます。SqlConnection
Entity Framework はネストされたトランザクションを作成しないことに注意してください。接続が「ユーザートランザクションに登録されている」かどうかを確認するだけです。トランザクションで既に実行されている場合SaveChanges
、トランザクションは開始されません。ただし、Entity Framework は、重大な障害 (データベースのデッドロックなど) が原因でデータベースがトランザクションを中止したかどうかを検出できません。したがって、 の最初の呼び出しがSaveChanges
デッドロックなどで失敗し、 をキャッチしてリコールした場合SaveChanges
でも、Entity Framework はそれがトランザクション内で実行されていると見なします。
これは、この 2 番目の呼び出しがトランザクションなしで実行されることを意味します。これは、操作が途中で失敗した場合、ロールバックするトランザクションがないため、既に実行されたステートメントはロールバックされないことを意味します。
Entity Framework がネストされたトランザクションを使用していれば、操作の破損の問題はSaveChanges
回避できたはずですが、それでも一貫性に関する一般的な問題は解決されません。
Entity Framework は、明示的に指定しない場合、接続とトランザクションを作成します。SaveChanges
への呼び出しがより大きな全体的なトランザクションの一部である場合にのみ、接続とトランザクションを明示的に提供する必要があります。したがって、EF がネストされたトランザクションを作成し、 から戻る前にこれをコミットしたとしても、この「ネストされた」トランザクションは実際にはまったくネストされていないため、もう一度SaveChanges
呼び出すと問題が発生します。SaveChanges
EF がこの「入れ子になった」トランザクションをコミットすると、実際にはそこにある唯一のトランザクションがコミットされます。によって行われたすべての変更SaveChanges
はコミットされますが、この呼び出しの後に行われた可能性のある操作は実行されませんでした。明らかに、これは良い場所ではありません。
したがって、この話の教訓は、Entity Framework に接続とトランザクションを処理さSaveChanges
せ、リスクなしで呼び出しをやり直すか、トランザクションを自分で処理し、データベースが例外をスローしたときにすばやく失敗する必要があるということです。二度と電話してはいけませんSaveChanges
。