次の構造に分割されているasp.netコアプロジェクトがあります。
コントローラーはリクエストを受信し、データベースに対するアクション(ユーザーの作成、レコードの更新など)を担当する適切なストアを呼び出します。コントローラーがデータベースにとらわれていないことを確認する方法-トランザクションを制御するべきではありません。それらの存在に注意してください。
ストアはトランザクションを作成し、必要なリポジトリーとそれらに必要なCRUDメソッドを呼び出します。
これはかなり簡単です。ただし、トランザクション管理のため、コードを繰り返していることがよくあります。
ユーザーを追加してグループに追加したい場合があるので、GroupsStore
とUsersStore
を持っている場合-まったく同じGroupsUsersStore
ができましたそれらのストアとして、しかしそれを1つのトランザクションに入れることによって。
これは明らかに間違っていますが、トランザクションをSQLサーバーにネストすることはできず、それらのストアも独立して動作するようにしたいので、これ以上の解決策は考えられません。
このような問題をどのように解決しますか?
ユーザーを追加してグループに追加したい場合があります。つまり、GroupsStoreとUsersStoreがある場合、これらのストアとまったく同じように機能するGroupsUsersStoreが1つのトランザクションに配置されます。
これは明らかに間違っていますが、トランザクションをSQLサーバーにネストすることはできず、それらのストアも独立して動作するようにしたいので、これ以上の解決策は考えられません。
このような問題をどのように解決しますか?
あなたは反対側から問題に取り組んでいると思います。トランザクション制御は、1つのアプリケーション層でのみ実行する必要があります。したがって、現在よりも高いレベルでトランザクションを制御する必要がある場合は、いくつかの選択肢があります。
グループストアとユーザーストアがある場合
私は、ユーザーをビジネスエンティティとして実際に管理できるUserManager(またはそれと呼ぶもの)が必要だと思います。ユーザーをグループに割り当てるなど。
「ストア」クラスにオプションのブールコンストラクタパラメータautocommit
を指定して、トランザクションを自分で管理するか(つまり、作業が完了した後に自動的にコミットするか)、呼び出し側に任せるかを制御します。このパラメーターのデフォルト値はtrue
です。
したがって、GroupsStore
はデフォルトで自動的にコミットされ、UsersStore
ストアも単独で使用した場合は自動的にコミットされます。
これで、「グループとユーザー」のトランザクションを組み合わせるために、おそらく別のGroupsUsersStore
クラスを作成しますが、このクラスには重複したコードを含める必要はありません。代わりに、GroupsStore
とUsersStore
を再利用して、両方をautocommit=false
で呼び出すことができます。
あなたのストアはビジネスレイヤーのように聞こえますか?ストア内の特定の単一のメソッドが複数のリポジトリを呼び出してその作業を実行し、エラーが発生した場合はすべてロールバックできると想定します。その場合は、ストアで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は、何らかの成功/失敗/メッセージングデータを返して、どのような結果が発生したかを呼び出したコントローラーに示す必要があります。