2015-12-08 12 views
8

私は現在、データレイヤー内のトランザクションを管理するためにTransactionScopeを使用していますが、ネストされたトランザクションと非同期の問題を実行しています。 MSDTCに送信します。私は正確な問題は見つけられませんでしたが、このシナリオのように見えるのはparticuarly wellではなく、代わりにDatabase.BeginTransaction()を使用しているはずです。EF6でネストされたトランザクションの振る舞い

私の問題は、Database.BeginTransaction()がネストされたトランザクションとどのように動作するか、特に私が新しいトランザクションを作成するのではなくアンビエントトランザクションを使用したいというシナリオでは、情報を見つけることができないことです。私の疑念は、それがこのように動作するようには意図されていないということです。ネストされたトランザクションを管理したい場合は、トランザクション管理を抽象化してより制御してください。

不必要な抽象レイヤーを追加したくないこの分野で誰かが経験したことを知りたいと思っていました。別のトランザクションでネストされたDatabase.BeginTransaction()の動作を確認できましたか?私のDALについて

追加情報:

あり
public class AddBlogPostHandler 
{ 
    private readonly MyDbContext _myDbContext; 

    public AddBlogPostHandler(MyDbContext myDbContext) 
    { 
     _myDbContext = myDbContext; 
    } 

    public async Task ExecuteAsync(AddBlogPostCommand command) 
    { 
     using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) 
     { 
      // .. code to create and add a draft blog post to the context 
      await _myDbContext.SaveChangesAsync(); 

      var publishBlogPostCommand = new PublishBlogPostCommand(); 
      // ..set some variables on the PublishBlogPostCommand 
      await PublishBlogPostAsync(command); 

      scope.Complete(); 
     } 
    } 
} 

public class PublishBlogPostHandler 
{ 
    private readonly MyDbContext _myDbContext; 

    public PublishBlogPostHandler(MyDbContext myDbContext) 
    { 
     _myDbContext = myDbContext; 
    } 

    public async Task ExecuteAsync(PublishBlogPostCommand command) 
    { 
     using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) 
     { 
      // .. some code to do one set of update 
      await _myDbContext.SaveChangesAsync(); 

      // .. some other db updates that need to be run separately 
      await _myDbContext.SaveChangesAsync(); 

      scope.Complete(); 
     } 
    } 
} 

答えて

5

:CQSパターンに基づいて、私はこのネスティングが発生したかの簡略化/不自然な例は以下のようになり、コマンドまたはクエリハンドラでDbと関連のコードをカプセル化する傾向があります内側のトランザクションが独立してコミットまたはロールバックできるという意味でネストされたトランザクションなどはありません。ネストされたトランザクションは、実際には参照カウントのみを維持します。最後のコミットでは、実際にコミットします。最初のロールバックでは、物理的なロールバックを取得します。あなたがそれを認識していることを確認するだけです。

MSDTCの使用を避けることが重要です。これはTransactionScopeBeginTransactionの両方で可能です。前者では、スコープ内の接続を明示的にOpenにする必要があります。そのため、EFは常に新しい接続をオープンしません。

これは、EF(L2Sにはない)の欠陥です。この問題にコメントして、顧客がこの問題に遭遇していることをチームが認識していることを確認してください。

特に私のシナリオでは、新しいトランザクションを作成するのではなく、アンビエントトランザクションを使用したいと考えています。

これはTransactionScopeに最適です。私はBeginTransactionへのあなたのスイッチが誤解に基づいていると思います。たぶんあなたはコメントで明確にすることができます。最初の段落で説明した別のトランザクション

内にネスト

確認Database.BeginTransactionの行動()。私のDALについて

追加情報:CQSパターンに基づいて、私は、コマンドまたはクエリハンドラでDbと関連のコードをカプセル化する傾向があるので、このネスティングが発生したかの簡略化/不自然な例は次のようになります。

上記のように、コードは紛失したコールを除いて正常に見えます。

このパターンは、同じトランザクション内で複数のクエリとコマンドを実行することをサポートします。ちょうどそれのまわりで別のスコープを囲む。接続を2回開かないようにしてください。処置を取る前に​​を確認してください。

+0

'BeginTransaction'のネストされた呼び出しが新しいトランザクション(TransactionScopeOption.RequiresNew'と同じ)を作成するのか、アンビエントなトランザクション(' TransactionScopeOption.Required'に相当)を使うのか本当に尋ねていますか?私がそれを働かせることができないことを除けば、「BeginTransaction」への切り替えは、それがEF6 +のための彼らの推薦であることに基づいていました。私は 'TransactionScope'を使って動作させようとしていますが、手動で接続を開くと、外部トランザクションが完了する前に例外が発生します(トランザクションは完了しましたが、破棄されていません)。 –

+1

BeginTransactionは必須です。同じ接続に複数のトランスを持たせる方法はありません。したがって、RequiresNewのように動作する可能性はありません。 「EF6のための彼らの推薦である」私はそれも読んだが、私は推論が与えられなかったと思う。不思議なアドバイス。 「外部トランザクションが完了する前に例外が発生します。」バグを調査して修正する必要があります。あなたがしなければならないのは、マイナーなバグを修正する必要がある場合は、なぜアプローチ全体をドロップするのですか? – usr

+0

素晴らしいですが、1つの接続につき1つのトランザクションしか持つことができないことは気づいていませんでした。私はちょうど 'TransactionScope'を私のシナリオで動かすことができなかったので、小さな抽象化を書いてしまいました。これは入れ子になったトランザクションスコープを持ち、基礎となるトランザクションを開くために' Database.BeginTransaction'を使用することを意味しました。助けていただきありがとうございます@usr –

関連する問題