トランザクション制御を行う際のポイント
トランザクション制御の実装方法まで分かったところで、実際にトランザクション制御を行う際のポイントをいくつか紹介します。
ポイント1:トランザクション制御はビジネスロジック層で行う
トランザクション制御は、プレゼンテーション層でもデータアクセス層でもなく、ビジネスロジック層で行うようにします。リスト2で掲載したコードもビジネスロジック層のものです。
これは、トランザクション制御の方針がビジネスロジックの要件に左右されるためです。
仮にデータアクセス層でトランザクション制御を行ってしまうと、複数のテーブルに対しての処理を1つのトランザクションにまとめることができなくなってしまいます。また、1つのトランザクションにしようと1つのデータアクセス層処理で複数のテーブルを更新してしまっては、それはすでにデータアクセスではなくビジネスロジックの役割を与えられていることになるので、これはその処理をビジネスロジック層に移動させるべきです。
ポイント2:可能ならビジネスロジック層の処理の全体をトランザクション制御対象とする
ビジネスロジック層に更新処理メソッドがあった場合、更新処理メソッドの最初でトランザクションを開始し、最後にトランザクションをコミット/ロールバックするようにすべきです。
/// <summary> /// 予約情報を更新します。 /// </summary> /// <param name="reservation"></param> public void Update(Reservation reservation) { using (var ts = new TransactionScope()) { if (OverlapsOtherReservation(reservation)) { // 重複した予約あり throw new OverlapReservationException(); } _reservationRepository.Update(reservation); _reservationRepository.Save(); ts.Complete(); } }
これは次のように、中途半端にトランザクションの範囲を絞ることで、トランザクションに参加しないデータアクセスが発生することを防ぐためです。そのデータアクセスが参照系だけならそれほど影響はありませんが、更新系処理をうっかりトランザクションに参加させなかった場合、本記事冒頭の「トランザクション制御なし」と同じように、その更新処理だけ独立して確定されてしまいます。
public void Update(Reservation reservation) { if (OverlapsOtherReservation(reservation)) { // 重複した予約あり throw new OverlapReservationException(); } using (var ts = new TransactionScope()) { _reservationRepository.Update(reservation); _reservationRepository.Save(); ts.Complete(); } }
なお、ビジネスロジックの要件として複数のトランザクションを制御しなければならないような場合はこの限りではありません。ただ、可能な限りトランザクションのスコープは広くとるべきです。そのためにも、次のようにトランザクションの単位でメソッドアウトすることをお勧めします。
public void UpdateTables(ComplexData data) // (1) { UpdateTable1(data.Table1Entity); UpdateTable2(data.Table2Entity); } private void UpdateTable1(Table1Entity entity) // (2) { using (var ts = new TransactionScope()) { // Table1更新処理 ts.Complete(); } } private void UpdateTable2(Table2Entity entity) { using (var ts = new TransactionScope()) { // Table2更新処理 ts.Complete(); } }
(1)更新処理メインロジックのメソッドでは、各トランザクション管理対象毎のメソッドを呼ぶようにする。
(2)トランザクション制御対象処理ごとにprivateなメソッドとしてメソッドアウトする。このメソッドでは内部の処理すべてをTransactionScopeのusingブロックで囲みます。
まとめ
データの整合性を保つためのトランザクション制御のポイントは、以下のとおりです。
- データを更新する際の一連の処理の塊を「トランザクション」という。
- トランザクション制御とは、一連の処理の開始、確定(コミット)、取消(ロールバック)を制御することをいう。
-
トランザクション制御には大きく分けて2つのタイプがある。
-
明示的トランザクション制御
バッチ処理などでよく使われる。分散トランザクションには対応していない。
自分でトランザクションの開始、確定、取消をすべて制御する方法。 -
暗黙的トランザクション制御
現在の主流のトランザクション制御方法。
分散トランザクションに対応している上、構文がシンプルになる。
-
明示的トランザクション制御
-
トランザクション制御の実装は次のように行う。
-
明示的トランザクション制御
IDbConnection.BeginTransactionメソッドでトランザクションを開始し、IDbTransaction.Commit/Rollbackメソッドで終了させる。 -
暗黙的トランザクション制御
System.Transaction.TransactionScope型をusingブロックともに使用し、コミットしてよい場合、usingブロックの最後でTransactionScope.Completeメソッドを呼び出す。
-
明示的トランザクション制御
-
トランザクション制御のポイントは次の2つ。
- ポイント1:トランザクション制御はビジネスロジック層で行う。
- ポイント2:トランザクション制御対象処理ごとにメソッドを作成し、極力メソッドの内部すべてを1つのトランザクション制御範囲とする。
さて、次回はデータの整合性を保つために必要なもう一つの要素、排他制御について紹介する予定です。次回もお楽しみに。