TransactionsまたはSaveChanges(false)とAcceptAllChanges()を使用していますか?


346

私はトランザクションを調査してきましたが、エラーがなければ、私が渡しfalseSaveChanges()呼び出しを行う限り、EFでトランザクションを処理しているようAcceptAllChanges()です。

SaveChanges(false);
// ...
AcceptAllChanges();

問題が発生した場合はどうなりますか?ロールバックする必要はありませんか、またはメソッドがスコープから外れるとすぐに、トランザクションは終了しますか?

トランザクションの途中で割り当てられたインディティカラムはどうなりますか?私が悪くなる前に誰かが私の後にレコードを追加したとしたら、これはID値が欠落していることを意味します。

TransactionScopeコードで標準クラスを使用する理由はありますか?


1
これSaveChanges(fase); ... AcceptAllChanges();は、そもそもパターンがなぜだったのかを理解するのに役立ちました。上記の質問に対する受け入れられた回答がブログの作成者によってどのように記述されているかに注目してください 。そのブログは他の質問で参照されています。それはすべて一緒になります。
Red Pea

回答:


451

ほとんどの場合、Entity Frameworkを使用 SaveChanges()場合十分です。これにより、トランザクションが作成されるか、アンビエントトランザクションに参加し、そのトランザクションで必要なすべての作業が実行されます。

SaveChanges(false) + AcceptAllChanges()ペアリングが役立つこともあります。

これに最も役立つのは、2つの異なるコンテキスト間で分散トランザクションを実行する場合です。

すなわちこのようなもの(悪い):

using (TransactionScope scope = new TransactionScope())
{
    //Do something with context1
    //Do something with context2

    //Save and discard changes
    context1.SaveChanges();

    //Save and discard changes
    context2.SaveChanges();

    //if we get here things are looking good.
    scope.Complete();
}

もしは context1.SaveChanges()成功したがcontext2.SaveChanges()、全体の分散トランザクションが中止されて失敗します。ただし、残念ながらEntity Frameworkはの変更をすでに破棄しているcontext1ため、失敗を再生したり効果的にログに記録したりすることはできません。

ただし、コードを次のように変更すると、

using (TransactionScope scope = new TransactionScope())
{
    //Do something with context1
    //Do something with context2

    //Save Changes but don't discard yet
    context1.SaveChanges(false);

    //Save Changes but don't discard yet
    context2.SaveChanges(false);

    //if we get here things are looking good.
    scope.Complete();
    context1.AcceptAllChanges();
    context2.AcceptAllChanges();

}

への呼び出しによりSaveChanges(false)、必要なコマンドがデータベースに送信されますが、コンテキスト自体は変更されないため、必要に応じてもう一度実行するか、またはObjectStateManagerます。

これは、トランザクションが実際に例外をスローした場合、ObjectStateManagerどこかで各コンテキストの状態を再試行またはログ記録することにより、補正できることを意味します。

詳細については、私の ブログ投稿を参照しください。


3
ありがとう SaveChanges、保存されるようにマークを付けますが、acceptallchangesを実行するまで実際にはコミットしません。
マークスミス

33
@マーク:「ロールバック」という意味の場合は、オブジェクトをデータベースにある状態に戻します。そうしないと、ユーザーによるオブジェクトへの変更がすべて失われるため、そうする必要はありません。 。 SaveChanges(false)データベースへの実際の更新を行いながらAcceptAllChanges()、EFに「正常に保存されているため、保存する必要のあるものを忘れてもかまいません」と伝えます。場合はSaveChanges(false)失敗した、AcceptAllChanges()と呼ばれることはありませんし、EFはまだ変更された、必要に戻ってデータベースに保存する性質を持つものとして、あなたのオブジェクトを検討します。
BlueRaja-ダニープフルフフト

Code Firstを使用してこれを行う方法をアドバイスできますか?SaveChangesまたはAcceptAllChangesメソッドにパラメーターはありません
Kirsten Greed

2
この手法をCode First
Kirsten Greed

13
これはEF 6.1ではもう不可能です。現在、どのような調整を行う必要があるか知っていますか?
Alex Dresko 14

113

EF6(Entity Framework 6+)を使用している場合、SQLへのデータベース呼び出しでこれが変更されました。
参照:http : //msdn.microsoft.com/en-us/data/dn456843.aspx

context.Database.BeginTransactionを使用します。

MSDNから:

using (var context = new BloggingContext()) 
{ 
    using (var dbContextTransaction = context.Database.BeginTransaction()) 
    { 
        try 
        { 
            context.Database.ExecuteSqlCommand( 
                @"UPDATE Blogs SET Rating = 5" + 
                    " WHERE Name LIKE '%Entity Framework%'" 
                ); 

            var query = context.Posts.Where(p => p.Blog.Rating >= 5); 
            foreach (var post in query) 
            { 
                post.Title += "[Cool Blog]"; 
            } 

            context.SaveChanges(); 

            dbContextTransaction.Commit(); 
        } 
        catch (Exception) 
        { 
            dbContextTransaction.Rollback(); //Required according to MSDN article 
            throw; //Not in MSDN article, but recommended so the exception still bubbles up
        } 
    } 
} 

51
トランザクションで "using"を使用している場合、roolbackを使用したtry-catchは必要ありません。
ロバート

12
私はこのような例外をトラップするために例外を取っています。これにより、データベース操作がサイレントに失敗します。SOの性質上、誰かがこの例を取り上げて、実動アプリケーションで使用する可能性があります。
B2K、2015

3
@ B2K:良い点ですが、このコードはリンクされたマイクロソフトの記事からコピーされてます。私は誰も彼らのコードを本番環境で使用ないことを望みます:)
J Bryan Price

6
@Robert MSDNの記事によれば、Rollback()が必要です。TransactionScopeの例では、意図的にRollbackコマンドを省略しています。@ B2Kをthrow;MSDNスニペットに追加し、それがMSDN記事のオリジナルではないことを明確に示しました。
トッド

6
(正しければ)これで問題が解決する可能性があります。EF+ MSSQLのように聞こえる場合、ロールバックは必要ありませんが、EF +他のSQLプロバイダーは必要です。EFは、どのデータベースとRollback()通信するかを認識していないことになっているため、MySqlまたはその自動動作を持たない何かと通信する場合に呼び出されます。
Jaredのような言葉

-5

一部のデータベースは、dbContextTransaction.Commit()で例外をスローできるため、これはより適切です。

using (var context = new BloggingContext()) 
{ 
  using (var dbContextTransaction = context.Database.BeginTransaction()) 
  { 
    try 
    { 
      context.Database.ExecuteSqlCommand( 
          @"UPDATE Blogs SET Rating = 5" + 
              " WHERE Name LIKE '%Entity Framework%'" 
          ); 

      var query = context.Posts.Where(p => p.Blog.Rating >= 5); 
      foreach (var post in query) 
      { 
          post.Title += "[Cool Blog]"; 
      } 

      context.SaveChanges(false); 

      dbContextTransaction.Commit(); 

      context.AcceptAllChanges();
    } 
    catch (Exception) 
    { 
      dbContextTransaction.Rollback(); 
    } 
  } 
} 

7
私はこのような例外をトラップするために例外を取っています。これにより、データベース操作がサイレントに失敗します。SOの性質上、誰かがこの例を取り上げて、実動アプリケーションで使用する可能性があります。
B2K、2015

6
これは本質的に、引用しているMSDNページに帰属を示した他の回答と同じではありませんか?私が見る唯一の違いは、あなたがにパスし、さらにを呼び出すことです。falsecontext.SaveChanges();context.AcceptAllChanges();
Wai Ha Lee

@ B2Kロールバックは必要ありません-トランザクションが機能しない場合、何もコミットされません。また、ロールバックを明示的に呼び出しは失敗する可能性があります-私の答えはここを参照してくださいstackoverflow.com/questions/41385740/...
ケン

ロールバックは私が反対しているものではありません。この回答の作成者は、例外を再スローするようにコードを更新し、私が反対していたことを解決しました。
B2K 2018年

すみません、電話からコメントしました。トッドは例外を再スローしますが、eMeLはしません。キャッチには、ロールバックの原因となる問題を開発者またはユーザーに通知するものがあるはずです。これは、ログファイルへの書き込み、例外の再スロー、またはユーザーへのメッセージの返信などです。
B2K 2018年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.