web-dev-qa-db-ja.com

トランザクションアクションまたはSaveChanges(false)とAcceptAllChanges()を使用しますか?

私はトランザクションを調査してきました、そして、私はfalseSaveChanges()に渡し、そしてエラーがなければAcceptAllChanges()を呼び出す限り、それらはEFで彼ら自身の世話をするように見えます:

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

何かが悪くなったら?ロールバックする必要はありませんか。または、メソッドが範囲外になるとすぐにトランザクションは終了しますか。

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

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

334
mark smith

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をどこかで補うことができます。

詳しくは myブログ投稿 をご覧ください。

440
Alex James

EF6(Entity Framework 6+)を使用している場合、これはSQLへのデータベース呼び出しでは変更されています。
参照: http://msdn.Microsoft.com/ja-jp/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
        } 
    } 
} 
108
user3885816