2017-10-12 14 views
0

私はAsp.Net MVCアプリケーションを構築しています。私はEntity Framework Coreをデータベースアクセスに使用しました。私はデータベースにSQLServerを使用しています。すべてのEFスタッフはコントローラのメソッドの内部からうまく動作します。エンドポイントとDbContextに同じトランザクションを使用

また、Webサーバーで実行する必要のないものを処理する、関連するNServiceBusサービスアプリケーションもあります。一般的なことの1つは、タイムアウト後にタスクを実行することです。 (例えば、ユーザーがウェブサイト上で何かをしてから30分後に何かします)。

サービスバスのセットアップとEntity Frameworkデータと同じデータベースを使用するSQLトランスポートを使用しています。

WebサーバーはNServiceBusメッセージを処理する必要がないため、IOCコンテナに登録されたIEndpointInstanceのみが送信されます。 NServiceBusメッセージを送信する必要があるコントローラは、IEndpointInstanceを取得し、これを使用して送信します。

コントローラのクライアント要求を処理し、EFを使用してデータベースを変更し、エンドポイントを使用してNServiceBusメッセージを送信しますが、問題があります。 EFとNServiceBusの両方が同じSQLデータベースを使用してデータとメッセージを格納していますが、これらのタスクは同じデータベーストランザクションの一部ではありません。これは、EFがデータベースへの変更を保存するために問題が起こった場合に、NServiceBusメッセージの送信が完了せず、データとメッセージが一貫性のない状態になるという問題があることを意味します。

問題は、どのようにIEndpointInstanceをEF DbContext Saveと同じトランザクション内で送信するのですか?

私は、EndPoint Transportを構成するときに、私が使用できるUseCustomSqlConnectionFactory拡張メソッドがあることを発見しました。私はIOCコンテナからWebリクエストスコープで使用されるDbContextを取得し、そのSqlConnectionを抽出してエンドポイントに提供するファクトリを提供することができました。ここでの問題は、IEndpointInstance.Sendから返されたTaskは、送信が完了するための.Wait()が返されないことです。

IMessageHandlerContextに含まれるトランザクションをEFで共有する方法についての豊富なドキュメントや例がありますが、IEndpointInstanceを取得してEFとトランザクションを共有する方法については何も見つかりません。

答えて

2

あなたは確かにそうすることができます。 DbContextの上にUnitOfWorkを実装し、それをメッセージハンドラのパイプラインと統合することができます。これにより、複数のハンドラがある場合、それらはすべて同じトランザクションに参加することになります。このアプローチは、送信のみのエンドポイントで動作し、送信と受信(基本的なトランスポートでサポートされている場合)では動作しますが、送信専用のエンドポイントでは異なる動作を実行する必要があります送信専用エンドポイントの「着信」メッセージはありません)。

あなたがそれを行う方法は、メッセージがに来るとき、あなたがメッセージ処理パイプラインに行動を注入することをある:

public class UnitOfWorkSetupBehaviorBehavior : Behavior<IIncomingLogicalMessageContext> 
{ 
    public override async Task Invoke(IIncomingLogicalMessageContext context, Func<Task> next) 
    { 
     var uow = new EntityFrameworkUnitOfWork(); 
     context.Extensions.Set(uow); 
     await next().ConfigureAwait(false); //executes the next op in the chain 
     context.Extensions.Remove<EntityFrameworkUnitOfWork>(); 
    } 
} 

とワークの実装の実際の単位は次のようになります(それが使用することに注意してください周囲トランザクション):

class EntityFrameworkUnitOfWork 
{ 
    MyDataContext context; 

    public MyDataContext GetDataContext(SynchronizedStorageSession storageSession) 
    { 
     if (context == null) 
     { 
      var dbConnection = storageSession.Session().Connection; 
      context = new MyDataContext (dbConnection); 

      //Don't use transaction because connection is enlisted in the TransactionScope 
      context.Database.UseTransaction(null); 

      //Call SaveChanges before completing storage session 
      storageSession.OnSaveChanges(x => context.SaveChangesAsync()); 
     } 
     return context; 
    } 
} 

このすべてがあまりにも困難なようであれば、あなたはドキュメンテーションのウェブサイト上でdownloadすることができます偉大なサンプルがあります。

+0

私は何かが欠けています。 IEndpointInstance.Send()を呼び出すまで動作は呼び出されません。これは、作業ユニットがそれまでに拡張機能に追加されていないことを意味します。同様に、どのようにしてExtensionsにアクセスするのですか?Send()が呼び出される前に取得しますか? –

+0

私のMVCコントローラで達成しようとしているプロセスは次のとおりです。 1.ビジネスデータDBコンテキストクエリ 2.終了するか続行するか決定します 3.ビジネスデータDBコンテキストを更新します 4.メッセージを送信します。 だから、その接続が送信まで動作で公開されていない場合、エンドポイントインスタンスから接続オブジェクトを使用してビジネスDBコンテキストを作成するにはどうすればよいですか? –

+0

@WilliamLeaderその場合、TransactionScopeのヘルプはありませんか?メッセージ+ビジネスデータが含まれているデータベースを同じ接続文字列で共有する限り、(DTCにエスカレートせずに)両方が同じトランザクションに参加します。 –

関連する問題