web-dev-qa-db-ja.com

ネストされたトランザクション管理.NET

次の構造に分割されているasp.netコアプロジェクトがあります。

  • コントローラ
    • お店
      • リポジトリー

コントローラーはリクエストを受信し、データベースに対するアクション(ユーザーの作成、レコードの更新など)を担当する適切なストアを呼び出します。コントローラーがデータベースにとらわれていないことを確認する方法-トランザクションを制御するべきではありません。それらの存在に注意してください。

ストアはトランザクションを作成し、必要なリポジトリーとそれらに必要なCRUDメソッドを呼び出します。

これはかなり簡単です。ただし、トランザクション管理のため、コードを繰り返していることがよくあります。

ユーザーを追加してグループに追加したい場合があるので、GroupsStoreUsersStoreを持っている場合-まったく同じGroupsUsersStoreができましたそれらのストアとして、しかしそれを1つのトランザクションに入れることによって。

これは明らかに間違っていますが、トランザクションをSQLサーバーにネストすることはできず、それらのストアも独立して動作するようにしたいので、これ以上の解決策は考えられません。

このような問題をどのように解決しますか?

5
gilmishal

ユーザーを追加してグループに追加したい場合があります。つまり、GroupsStoreとUsersStoreがある場合、これらのストアとまったく同じように機能するGroupsUsersStoreが1つのトランザクションに配置されます。

これは明らかに間違っていますが、トランザクションをSQLサーバーにネストすることはできず、それらのストアも独立して動作するようにしたいので、これ以上の解決策は考えられません。

このような問題をどのように解決しますか?

あなたは反対側から問題に取り組んでいると思います。トランザクション制御は、1つのアプリケーション層でのみ実行する必要があります。したがって、現在よりも高いレベルでトランザクションを制御する必要がある場合は、いくつかの選択肢があります。

  • ストアをリファクタリングします(実際には、サービスであると思われます)。各ストアは、限界を超えずにすべての操作を実行できます。たとえば、ストアをマージしたい場合があります。ただし、 [〜#〜] srp [〜#〜] 従順に注意してください。
  • アプリケーションの上位レベルのレイヤー(オーケストレーションレイヤー)を構築します。これにより、1つのビジネス操作を実行する必要があるときにトランザクションを制御し、ストアを呼び出します。

グループストアとユーザーストアがある場合

私は、ユーザーをビジネスエンティティとして実際に管理できるUserManager(またはそれと呼ぶもの)が必要だと思います。ユーザーをグループに割り当てるなど。

1

「ストア」クラスにオプションのブールコンストラクタパラメータautocommitを指定して、トランザクションを自分で管理するか(つまり、作業が完了した後に自動的にコミットするか)、呼び出し側に任せるかを制御します。このパラメーターのデフォルト値はtrueです。

したがって、GroupsStoreはデフォルトで自動的にコミットされ、UsersStoreストアも単独で使用した場合は自動的にコミットされます。

これで、「グループとユーザー」のトランザクションを組み合わせるために、おそらく別のGroupsUsersStoreクラスを作成しますが、このクラスには重複したコードを含める必要はありません。代わりに、GroupsStoreUsersStoreを再利用して、両方をautocommit=falseで呼び出すことができます。

1
Doc Brown

あなたのストアはビジネスレイヤーのように聞こえますか?ストア内の特定の単一のメソッドが複数のリポジトリを呼び出してその作業を実行し、エラーが発生した場合はすべてロールバックできると想定します。その場合は、ストアでSQL接続とトランザクションを開始し、依存関係としてリポジトリに渡します。

FooStore.csの内部:

public StoreResult<ResultThing> DoStuff(int stuff) 
{
     using (var connection = GetOpenConnection())
     using (var transaction = connection.BeginTransaction())
     {
          var fooRepo = new FooRepo(connection, transaction);
          fooRepo.DoThing(stuff); // calls connection.Execute(...)
          // add more repo calls here
          transaction.Commit();
          return new StoreResult<ResultThing>();
     }
}

ここで、StoreResultは、何らかの成功/失敗/メッセージングデータを返して、どのような結果が発生したかを呼び出したコントローラーに示す必要があります。

1
Graham