14

Entity Framework を操作するときに例外が発生する可能性がある 2 つの異なるシナリオを認識していますDbContext

  1. クエリの列挙 ( をスローする可能性がありますEntityCommandExecutionException)
  2. 呼び出し中SaveChanges( をスローする可能性がありますDbUpdateException)

の 1 つのインスタンス内で、DbContextこれらの例外をキャッチし、該当する場合は回復を試みてから、操作を繰り返したいと考えています。

具体的にSaveChangesは、デッドロックが原因で への呼び出しで例外がスローされた場合、 への呼び出しを再試行したいと考えていますSaveChanges。この状況を検出して再試行する方法は既に知っています。

ここでこの回答を見ました。これは、デッドロック後に SQL 接続を使用すべきではないことを示しています。DbContextこれは、このような例外から回復するために、より高いレベルの操作全体を再起動する必要があることを示しています。

DbContext確信が持てないのは、このような例外がスローされた後も引き続き を使用しても安全かどうかです。使えない状態になるのでしょうか?それでも機能しますが、正しく機能しませんか? SaveChangesトランザクション的に発生しなくなりますか?

4

1 に答える 1

6

DbContextすでに開いている SQL 接続を に指定しないDbContext場合、 を呼び出したときに が接続を開いたり閉じたりしますSaveChangesDbContextその場合、もちろん、保持しているエンティティが無効な状態にある可能性があることを除いて、アラウンドを保持しても危険はあり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呼び出すと問題が発生します。SaveChangesEF がこの「入れ子になった」トランザクションをコミットすると、実際にはそこにある唯一のトランザクションがコミットされます。によって行われたすべての変更SaveChangesはコミットされますが、この呼び出しの後に行われた可能性のある操作は実行されませんでした。明らかに、これは良い場所ではありません。

したがって、この話の教訓は、Entity Framework に接続とトランザクションを処理さSaveChangesせ、リスクなしで呼び出しをやり直すか、トランザクションを自分で処理し、データベースが例外をスローしたときにすばやく失敗する必要があるということです。二度と電話してはいけませんSaveChanges

于 2013-10-01T08:38:37.543 に答える