EF6では、次のように使用できる新しいトランザクションがあります。
using (var context = new PostEntityContainer())
{
using (var dbcxtransaction = context.Database.BeginTransaction())
{
try
{
PostInformation NewPost = new PostInformation()
{
PostId = 101,
Content = "This is my first Post related to Entity Model",
Title = "Transaction in EF 6 beta"
};
context.Post_Details.Add(NewPost);
context.SaveChanges();
PostAdditionalInformation PostInformation = new PostAdditionalInformation()
{
PostId = (101),
PostName = "Working With Transaction in Entity Model 6 Beta Version"
};
context.PostAddtional_Details.Add(PostInformation);
context.SaveChanges();
dbcxtransaction.Commit();
}
catch
{
dbcxtransaction.Rollback();
}
}
}
物事が横向きになったときに実際にロールバックが必要ですか? Commitの説明に「基になるストアトランザクションをコミットする」と書かれているので、興味があります。
一方、ロールバックの説明では、「基になるストアトランザクションをロールバックします。」
コミットが呼び出されない場合、以前に実行されたコマンドが保存されないように思えるので、これは私を不思議にさせます(私にとっては理にかなっています)。しかし、その場合、ロールバック関数を呼び出す理由は何でしょうか? EF5では、ロールバック機能(完全のみ)を持たないTransactionScopeを使用しました。 MS DTCの理由により、TransactionScopeは使用できなくなりましたが、上記の例のようなtry catchも使用できません(つまり、Commitのみが必要です)。
Rollback
ステートメントを使用しているため、using
を手動で呼び出す必要はありません。
DbContextTransaction.Dispose
メソッドは、using
ブロックの最後に呼び出されます。また、トランザクションが正常にコミットされなかった場合(呼び出されなかったり、例外が発生しなかった場合)、トランザクションを自動的にロールバックします。以下はSqlInternalTransaction.Dispose
メソッドのソースコードです(DbContextTransaction.Dispose
はSqlServerプロバイダーの使用時に最終的に委任されます):
private void Dispose(bool disposing)
{
// ...
if (disposing && this._innerConnection != null)
{
this._disposing = true;
this.Rollback();
}
}
_innerConnection
がnullでないかどうかを確認し、そうでない場合は、トランザクションをロールバックします(コミットされた場合、_innerConnection
はnullになります)。 Commit
の機能を見てみましょう。
internal void Commit()
{
// Ignore many details here...
this._innerConnection.ExecuteTransaction(...);
if (!this.IsZombied && !this._innerConnection.IsYukonOrNewer)
{
// Zombie() method will set _innerConnection to null
this.Zombie();
}
else
{
this.ZombieParent();
}
// Ignore many details here...
}
internal void Zombie()
{
this.ZombieParent();
SqlInternalConnection innerConnection = this._innerConnection;
// Set the _innerConnection to null
this._innerConnection = null;
if (innerConnection != null)
{
innerConnection.DisconnectTransaction(this);
}
}
常にEFと共にSQL Serverを使用する限り、catchを明示的に使用してRollbackメソッドを呼び出す必要はありません。 usingブロックが例外を自動的にロールバックできるようにすると、常に機能します。
ただし、Entity Frameworkの観点から考えると、すべての例で明示的な呼び出しを使用してトランザクションをロールバックする理由を確認できます。 EFにとって、データベースプロバイダーは任意でプラグイン可能であり、プロバイダーはMySQLまたはEFプロバイダー実装を持つ他のデータベースに置き換えることができます。したがって、EFの観点からは、EFはデータベースプロバイダーの実装を知らないため、プロバイダーが破棄されたトランザクションを自動的にロールバックするという保証はありません。
したがって、EFのドキュメントでは、ベストプラクティスとして、明示的にロールバックすることを推奨しています-いつかプロバイダを破棄時に自動ロールバックしない実装に変更する場合に備えて。
私の意見では、よく書かれた適切なプロバイダーは破棄でトランザクションを自動的にロールバックするため、使用ブロック内のすべてをtry-catch-rollbackでラップする追加の労力は過剰です。