Entity Framework6トランザクションのロールバック


82

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では、論理的に思えるロールバック機能を持たない(Completeのみ)TransactionScopeを使用しました。MS DTCの理由により、TransactionScopeを使用できなくなりましたが、上記の例のようにtry catchを使用することもできません(つまり、コミットのみが必要です)。


1
SQLのトランザクションについて読んだことがありますか?EFはそれを模倣しようとします。AFAIK、SQLでトランザクションをコミットしない場合、トランザクションはロールバックされます。
gunr2171 2014年

この質問も参照してください。
gunr2171 2014年

はい、SQL自体のトランザクションについて知っています。EFが何をしているのか興味がありましたが、それを模倣していれば理にかなっています。回避できるかどうかを確認します。ありがとうございました!
クッキードッグ

SaveChanges()は常にトランザクションで発生し、例外が発生した場合はロールバックされます。あなたの場合、これを手動で処理しようとする必要はありません(この特定の場合、最初にすべてのエンティティを一SaveChanges度だけ追加する方がさらに良いでしょう)。
Pawel 2014年

両方が失敗しない場合にのみ、両方のSaveChangesからアイテムを保存したいので、はい、両方の周りに1つのトランザクションが必要です。
クッキードッグ

回答:


117

ステートメントRollbackを使用しているため、手動で呼び出す必要はありませんusing

DbContextTransaction.Disposeメソッドはusingブロックの最後で呼び出されます。また、トランザクションが正常にコミットされなかった場合(呼び出されなかったり、例外が発生したりしない場合)、トランザクションは自動的にロールバックされます。以下はSqlInternalTransaction.Disposeメソッドのソースコードです(DbContextTransaction.DisposeSqlServerプロバイダーを使用すると最終的にそれに委任されます):

private void Dispose(bool disposing)
{
    // ...
    if (disposing && this._innerConnection != null)
    {
        this._disposing = true;
        this.Rollback();
    }
}

ご覧の_innerConnectionとおり、nullでないかどうかを確認し、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);
    }
}

23

常にEFでSQLServerを使用する限り、catchを明示的に使用してRollbackメソッドを呼び出す必要はありません。usingブロックが例外で自動的にロールバックできるようにすると、常に機能します。

ただし、Entity Frameworkの観点から考えると、すべての例で明示的な呼び出しを使用してトランザクションをロールバックする理由がわかります。EFにとって、データベースプロバイダーは任意でプラグイン可能であり、プロバイダーはMySQLまたはEFプロバイダーが実装されている他のデータベースに置き換えることができます。したがって、EFの観点からは、EFはデータベースプロバイダーの実装を認識していないため、プロバイダーが破棄されたトランザクションを自動的にロールバックするという保証はありません。

したがって、ベストプラクティスとして、EFのドキュメントでは、明示的にロールバックすることを推奨しています。これは、いつかプロバイダーを破棄時に自動ロールバックしない実装に変更した場合に備えてです。

私の意見では、適切で適切に記述されたプロバイダーは、破棄時にトランザクションを自動的にロールバックするため、try-catch-rollbackを使用してusingブロック内のすべてをラップする追加の作業はやり過ぎです。


1
この洞察に感謝します。コードを詳しく調べてみると、MouhongLinが言及したSqlInternalTransactionを呼び出すSqlTransactionでオーバーライドされる抽象クラスDbTransactionのDisposeに行き着きます。
ShawnFumo 2015年

4
  1. トランザクションをインスタンス化するために「using」ブロックを記述したので、廃棄時に自動的にロールバックされるため(コミットされていない限り)、Rollback関数について明示的に言及する必要はありません。
  2. ただし、usingブロックなしでインスタンス化する場合は、例外が発生した場合(正確にはcatchブロック内)にトランザクションをロールバックすることが不可欠であり、より堅牢なコードをnullチェックすることも必要です。BeginTransactionの動作は、トランザクションスコープとは異なります(すべての操作が正常に完了した場合は、完全な関数が必要です)。代わりに、SQLトランザクションの動作に似ています。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.