私はトランザクションを調査してきました、そして、私はfalse
をSaveChanges()
に渡し、そしてエラーがなければAcceptAllChanges()
を呼び出す限り、それらはEFで彼ら自身の世話をするように見えます:
SaveChanges(false);
// ...
AcceptAllChanges();
何かが悪くなったら?ロールバックする必要はありませんか。または、メソッドが範囲外になるとすぐにトランザクションは終了しますか。
トランザクションの途中で割り当てられた任意のインデント列はどうなりますか?私が悪くなる前に他の誰かが私の後にレコードを追加したのであれば、これはIdentityの値が欠けていることを意味します。
私のコードで標準のTransactionScope
クラスを使用する理由はありますか?
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
をどこかで補うことができます。
EF6(Entity Framework 6+)を使用している場合、これはSQLへのデータベース呼び出しでは変更されています。
参照: http://msdn.Microsoft.com/ja-jp/data/dn456843.aspx
context.Database.BeginTransactionを使用してください。
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 } } }