データの整合性を確保するためのトランザクション制御
実例で学ぶASP.NET Webフォーム業務アプリケーション開発のポイント 第5回
ダウンロード サンプルファイル (5.6 MB)
ASP.NETでのトランザクション制御
トランザクション制御の概念が分かったところで、今度は実際どのようにしてトランザクション制御を行うか説明していきましょう。
なお、トランザクション制御がからむとSQL Server Compact 4.0では実際のSQL Serverと挙動が異なる部分がありますので、次のようにWeb.configを書き換え、SQL Server Expressを使用するように変更します。
<connectionStrings> <add name="ApplicationServices" connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\aspnetdb.mdf;User Instance=true" providerName="System.Data.SqlClient" /> <!-- 変更前 SQL Server Compact <add name="MRRSConnectionString" connectionString="Data Source=|DataDirectory|\MRRS.sdf" providerName="System.Data.SqlServerCe.4.0" /> --> <!-- 変更後 SQL Server Express --> <add name="MRRSConnectionString" connectionString="data source=.\SQLEXPRESS;Integrated Security=true;AttachDBFilename=|DataDirectory|\MRRS.mdf;User Instance=true;Initial Catalog=MRRS;" providerName="System.Data.SqlClient"/> </connectionStrings>
トランザクション制御方法の種類
ASP.NETをはじめとした.NETアプリケーションでトランザクション制御を行うには、主に次の2つの方法があります。
- 明示的トランザクション制御
- 暗黙的トランザクション制御
1. 明示的トランザクション制御
.NET Framework 1.0の頃からある方法で、ADO.NETの機能を使い開発者自らがトランザクションの開始、終了をはじめとした挙動を制御する方法です。
明示的トランザクション制御の実装コード例を以下に示します。
/// <summary>
/// 予約情報を追加します。
/// </summary>
/// <param name="reservation"></param>
public void Insert(Reservation reservation)
{
using (var context = new MRRSContext())
{
var reservationRepository = new ReservationRepository(context);
var reservationHistoryRepository = new ReservationHistoryRepository(context);
var conn = ((IObjectContextAdapter)context).ObjectContext.Connection; // (1)
conn.Open(); // (2)
using (var tran = conn.BeginTransaction()) // (3)
{
try
{
if (OverlapsOtherReservation(reservationRepository, reservation))
{
// 重複した予約あり
throw new OverlapReservationException(); // (6)
}
// 予約登録
reservationRepository.Insert(reservation);
reservationRepository.Save();
// 予約履歴登録
RegisterHistory(reservationHistoryRepository, reservation, Operation.Insert);
tran.Commit(); // (4)
}
catch
{
tran.Rollback(); // (5)
throw;
}
}
conn.Close(); // (7)
}
}
(1)IDbConnectionオブジェクトを取得する。EFを用いる場合、context.Database.ConnectionプロパティでもIDbConnectionオブジェクトを取得することもできますが、今回のサンプルのように自分でIDbConnection.Openメソッドを呼び出すと、予約登録の箇所で例外が発生してしまいます。これを回避するため、上記のようなコードでIDbConnectionオブジェクトを取得する必要があります(参考:Exception from DbContext API: EntityConnection can only be constructed with a closed DbConnection)。
(2)IDbConnection.Openメソッドを呼び出し、DB接続を開く。この後の(3)でトランザクションを開始するには、DB接続が開いていないといけないため、ここでOpenメソッドを呼び出す必要があります。
(3)IDbConnection.BeginTransactionメソッドにより、トランザクションを開始する。
(4)処理が正常に終わったら、IDbTransaction.Commitメソッドによりトランザクションをコミットする。
(5)システムエラーが発生したら、IDbTransaction.Rollbackメソッドによりトランザクションをロールバックする。
(6)システムエラーに限らず、業務エラー発生時も同様にロールバックし、処理を中断する。
(7)IDbConnection.Closeメソッドを呼び出し、DB接続を閉じる。
後述する暗黙的トランザクション制御に比べれば多少処理が煩雑にはなりますが、開発者が完全に制御することが可能なので、バッチ処理など複雑なトランザクション制御が必要な際にはまだまだ現役で使われています。
ただ、複数のデータベース接続に対する処理を1つのトランザクションとする「分散トランザクション」には対応していません。分散トランザクションが必要な場合は暗黙的トランザクション制御を使うようにしましょう。
暗黙的トランザクション制御
.NET Framework 2.0(ADO.NET 2.0)から登場した方法で、現在は大半のケースでこちらを使用することを推奨します。
暗黙的トランザクション制御の実装コード例を以下に示します。
/// <summary>
/// 予約情報を更新します。
/// </summary>
/// <param name="reservation"></param>
public void Update(Reservation reservation)
{
using (var ts = new TransactionScope()) // (1)
{
if (OverlapsOtherReservation(_reservationRepository, reservation))
{
// 重複した予約あり
throw new OverlapReservationException(); // (3)
}
_reservationRepository.Update(reservation);
_reservationRepository.Save();
// 履歴登録
RegisterHistory(_reservationHistoryRepository, reservation, Operation.Update);
ts.Complete(); // (2)
}
}
(1)usingブロックを使用し、System.Transaction.TransactionScopeクラスのインスタンスを生成する。usingブロックの内部が1つのトランザクションとして扱われます。
(2)処理が正常に終わったらTransaction.Completeメソッドを呼び出す。Completeメソッドを呼び出すことで、このトランザクションが完了したというマークが付けられます。その後、usingブロックを抜ける際にTransactionScope.Disposeメソッドが自動的に呼び出され、その中でコミット処理が行われます。
(3)システムエラーや業務エラー発生時はTransactionScope.Completeメソッドが呼び出されずにusingブロックを抜ける。Completeメソッドが呼び出されないため、トランザクションが完了したというマークが付けられません。そのときはDisposeメソッドの中でロールバック処理が行われます。
暗黙的トランザクション制御のほうが明示的トランザクション制御に比べ大幅にコードが簡略化されて見通しがよくなっていることが分かると思います。また、IDbConnectionオブジェクトとの関連付けなどの細々とした処理がなくなっていることも分かると思います。このようにデータアクセステクノロジに特化した処理が不要なことが、暗黙的トランザクション制御の大きな利点の一つです。
さらに、暗黙的トランザクション制御は前述のように、複数のデータベース接続にまたがる「分散トランザクション」に対応しています。最近は作成するアプリケーションの規模によっては、データベースが冗長化されていることも珍しくありません。そんな時でも、暗黙的トランザクション制御を用いることで、単一トランザクション、分散トランザクションにかかわらず同じコードがそのまま利用できます。このことは、習得容易性や可読性の面からも好ましいです。
この記事は参考になりましたか?
- 実例で学ぶASP.NET Webフォーム業務アプリケーション開発のポイント連載記事一覧
- この記事の著者
-
山田 祥寛(ヤマダ ヨシヒロ)
静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるLaravel実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数。
※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です
-
WINGSプロジェクト 高野 将(タカノ ショウ)
<個人紹介>新潟県長岡市在住の在宅リモートワークプログラマー。家事や育児、仕事の合間に長岡IT開発者勉強会(NDS)、Niigata.NET、TDDBCなどのコミュニティに関わったり、Web記事や書籍などの執筆を行ったりしている。著書に『アプリを作ろう! Visual C#入門 Visual C# 2017対応』(日経BP社、2017)など。< WINGSプロジェクト について>有限会社 WINGSプロジェクト が運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。 2026年時点での 登録メンバ は約50名で、現在も執筆メンバを 募集中 。興味のある方は、どしどし応募頂きたい。 著書 、 記事 多数。 RSS X: @WingsPro_info (公式)、 @WingsPro_info/wings (メンバーリスト) Facebook
※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です
この記事は参考になりましたか?
この記事をシェア
