この特集のトップページへ
>
Chapter 5:ビジネスロジック層の構築
5.5.3 プログラムの開発
では,次にCOMコンポーネントの開発を進めてゆこう。ここでは,Table 5-12のような仕様で,4つのCOMコンポーネントを実装する。
Table 5-12 実装するCOMコンポーネント
在庫を調べるコンポーネント | |
プログラムID | dbsamplecom.chkstock |
メソッド | 解説 |
Public Function checkStock(ByVal ProductID As Long) As Long | 在庫テーブルを開き,引数ProductIDで指定された製品の在庫の数を返す |
在庫を設定するコンポーネント | |
プログラムID | dbsamplecom.setstock |
メソッド | 解説 |
Public Sub updateStock(ByVal ProductID As Long, ByVal newStock As Long) | 在庫テーブルを開き,引数ProductIDで指定された製品の在庫の数を引数newStockで指定した値に変更する |
受注テーブルに新規レコードを追加するコンポーネント | |
プログラムID | dbsamplecom.insorder |
メソッド | 解説 |
Public Sub insertOrder(ByVal ProductID As Long, ByVal CustomerName As String, ByVal Number As Long) | 受注テーブルを開き,CustomerNameの受注者がProductIDで指定された製品をNumber個発注したという情報を受注テーブルに書き出す |
受注を受け付けるコンポーネント | |
プログラムID | dbsamplecom.order |
メソッド | 解説 |
Public Sub acceptOrder(ByVal ProductID As Long, ByVal CustomerName As String, ByVal Number As Long) | 上記の3つのコンポーネントを使って,受注を受け付ける |
1)プロジェクトの設定
まずは,Visual Basicを起動し,ActiveX DLLを新規作成する。そして,次にプロジェクトのプロパティを設定する。
Table 5-12にも示したように,今回作成するCOMコンポーネントのプログラムIDは,“dbsamplecom.〜”である。よって,プロジェクト名としてdbsamplecomと設定する(Fig.5-48)。
Fig.5-48 プロジェクトのプロパティ
2)参照設定
次に参照設定をする。今回は,ADOコンポーネントを使ってデータベースにアクセスするので,“Microsoft ActiveX Data Objects 2.5 Library”を参照設定するが,それ以外にCOM+のライブラリも利用するため,“COM+ Services Type Library”も参照設定する(Fig.5-49)。
Fig.5-49 参照設定
3)dbsamplecom.chkstockの実装
では,dbsamplecom.chkstockから実装してゆく。このコンポーネントを実装するためにchkstockというクラスモジュールを用意する。プロジェクトを新規作成したときにはClass1というクラスモジュールが自動的に作成されるから,そのクラスモジュールの“(オブジェクト名)”をchkstockに変更して利用すればよいだろう。
ところで,クラスモジュールのプロパティには,COM+を利用するCOMコンポーネントに設定しておくと便利なプロパティがある。それは,MTSTransactionModeというプロパティである。このプロパティは,COM+に登録されたCOMコンポーネントのトランザクション設定のデフォルト値を決める働きを持つ(Table 5-13,Fig.5-50)。なお,COMコンポーネントのトランザクション設定については,Table 5-5を参照してほしい。
Table 5-13 MTSTransactionModeプロパティとCOM+におけるトランザクションのプロパティのデフォルト値との関係
MTSTransactionMode | トランザクションのプロパティのデフォルト値 |
NotAnMTSObject | 使用不可 |
NoTransaction | 未サポート |
RequiresTransaction | 必要 |
UsesTransaction | サポート |
RequiresNewTransaction | 新しく必要 |
Fig.5-50 MTSTransactionModeプロパティ
ここで作成するCOMコンポーネントは,トランザクションを必要とするので,MTSTransactionModeプロパティにはRequiresTransactionを設定しておくとよい。ただし,MTSTransactionModeプロパティの設定はCOM+に登録するときに有効となるトランザクション設定のデフォルト値であるにすぎない。管理者があとからトランザクション設定を変更することはできる。つまり,MTSTransactionModeプロパティは,そのCOMコンポーネントがトランザクションをサポートしているかどうかを決定するものではなく,たとえMTSTransactionModeプロパティにNotAnMTSObjectを設定しておいたとしても,管理者がコンポーネントサービス管理ツールでトランザクション設定を変更すれば,そのCOMコンポーネントでトランザクション機能を使えることになる。
では,chkStockメソッドの実装に移ろう。chkStockメソッドは,在庫テーブルを開き,指定された製品の在庫数を返すものである。その実装は,List 5-9のようになる。
chkStockメソッドの実装について順に説明する。
chkStockメソッドの基本構造は,ADOを使ってSELECT文を発行し,その結果を得ているだけである。実際にSELECT文を発行している箇所は23行目である。
COM+で特別な部分は,43行目にあるSetCompleteメソッドと50行目にあるSetAbortメソッドの呼び出し部分である。これらのメソッドは,すでに説明したように,それぞれトランザクションのコミットとアボートをCOM+に告げるものである。SetCompleteメソッドもSetAbortメソッドもObjectContextオブジェクトに備わるメソッドである。そのため,10行目で事前にGetObjectContext関数を呼び出して,ObjectContextオブジェクトを取得している。
List 5-8と見比べてもらうとわかるが,COM+に登録するCOMコンポーネントでは,トランザクションの開始を明示的に指定する必要はない。なぜなら,トランザクションはCOMコンポーネントのトランザクションのプロパティに従って,COM+によって自動的に開始されるからである。トランザクションをコミットするときには,ADODB.ConnectionオブジェクトのCommitTransメソッドの代わりにObjectContextオブジェクトのSetCompleteメソッドを,アボートするときにはADODB.ConnectionオブジェクトのRollBackTransメソッドの代わりにObjectContextオブジェクトのSetAbortメソッドを,それぞれ呼び出す。これによって,COM+に対して適切にトランザクションが通知される。これが,基本的な構造である。
ところで,List 5-9の47行目以降にあるエラーハンドラの部分には,若干の補足説明が必要だろう。まず,13行目において,On Error GoTo構文を使い,実行時エラーが発生したときには47行目のErrorHandlerラベル以降にジャンプするように設定している。実行時エラーを実際に発生させているのは,26行目である。26行目では,SELECT文を発行したときに戻り値として得られたADODB.Recordsetオブジェクトが空(すなわち1つもレコードがなかった)ならば,ErrオブジェクトのRaiseメソッドを使って実行時エラーを発生させている。このとき,47行目以降に処理が移るということになる。
ただし,実行時エラーが発生するのは,何も26行目だけに限らない。たとえば,20行目のADODB.ConnectionオブジェクトのOpenメソッドで該当するデータベースが開けないような場合にも,実行時エラーが発生する。それ以外の箇所でも,Visual Basicや利用しているCOMオブジェクト(List 5-9の場合にはADOのオブジェクト)などがさまざまな場所で実行時エラーを発生するおそれがある。
COM+においては,トランザクション処理のコミットおよびアボートの状態と,実行時エラーが発生したときとの因果関係はまったくない(ただし,「COLUMN 自動非アクティベート機能」で説明した,自動非アクティベート機能を使ったときには別である)。一般的に,実行時エラーが発生したときというのは,何らかの失敗があったときであるから,トランザクション処理をアボートさせるのが普通である。そのため,13行目でOn Error GoTo構文を使い,実行時エラーが発生したら47行目以降にジャンプさせ,50行目でObjectContextオブジェクトのSetAbortメソッドを呼び出してトランザクションをアボートに設定しているのである。26行目のエラーをトラップするだけならば,わざわざエラーハンドラを作らずに26行目の直後でSetAbortメソッドを呼び出してトランザクションを直接アボートに設定してしまってもよいわけだが,そうすると,そのほかの実行時エラーが発生したときにトランザクションがアボートされない。そのため,あらゆる実行時エラーが発生したときにトランザクションをアボートさせたいならば,エラーハンドラを作り,エラーハンドラ内でObjectContextオブジェクトのSetAbortメソッドを呼び出すという処理が不可欠になる。
ところで,エラーハンドラ内の52行目にあるErrオブジェクトのRaiseメソッドの呼び出しであるが,これはエラーハンドラ内で捕獲した実行時エラーを再発行するためのものである。52行目はトランザクション処理とはまったく関係ないが,52行目がないと発生したエラーがエラーハンドラで吸収されてしまうために,COMクライアント側に実行時エラーが戻らない。たとえば,26行目でErrオブジェクトのRaiseメソッドを呼び出して“指定された製品は見つかりません”という実行時エラーを発生させても,このエラーはエラーハンドラによって捕獲されてしまうので,COMクライアントにエラーメッセージが伝わらないのである。そこで,エラーハンドラで捕獲したエラーを52行目で再発行することにより,COMクライアントにエラーメッセージを届けようとしているのである。
4)dbsamplecom.setstockの実装
では次に,在庫数を設定するコンポーネントであるdbsamplecom.setstockにupdateStockメソッドを実装する。それにはまず,[プロジェクト]メニューから[クラスモジュールの追加]を選んでクラスモジュールを追加する。そして,先に示した3)の手順と同じように,そのクラスモジュールのプロパティの“(オブジェクト名)”をsetstockに変更する。さらに,デフォルトのトランザクション設定を[必要]にするため,MTSTransactionModeプロパティの値をRequiresTransactionに設定する。
updateStockメソッドのプログラムはList 5-9のchkStockメソッドとほぼ同様である。具体的には,List 5-10のようになる。特に説明はいらないだろう。
5)dbsamplecom.insorderの実装
次にdbsamplecom.insorderを実装する。これも先の3)や4)と同様の手続きで設定しておく必要がある。つまり,まずクラスモジュールを追加し,その“(オブジェクト名)”をinsorderに変更,さらにMTSTransactionModeプロパティの値をRequireTransactionに設定するという準備をしておく。
dbsamplecom.insorderに実装するinsertOrderメソッドは,List 5-11のようになる。
6)dbsamplecom.orderの実装
最後にdbsamplecom.orderを実装する。このCOMコンポーネントは,ルートオブジェクトに相当するもので,先の3)〜5)で実装したCOMオブジェクトを実体化し,メソッドを呼び出して処理するためのものである。このCOMコンポーネントを作るには,あらかじめクラスモジュールを新規作成し,その“(オブジェクト名)”をorderに設定,デフォルトのトランザクション設定を[新しく必要]にするためにMTSTransactionModeプロパティの値をRequiresNewTransactionに設定しておく。
次にdbsamplecom.orderコンポーネントにacceptOrderメソッドを実装する(List 5-12)。
List 5-12は,16〜18行目で3)〜5)の手順で実装しておいたCOMオブジェクトを実体化し,順にそのメソッドを呼び出して,全体の受注処理をするものである。List 5-12で示したCOMオブジェクトのトランザクション設定が[必要]または[新しく必要]に設定されていて,かつ16〜18行目で実体化されているCOMオブジェクトのトランザクション設定が[必要]に設定されていれば,これらは同じトランザクションの範囲内で実体化されることになる。すなわち,使っているCOMオブジェクトのうち,どれか1つでもアボートしたら――いい換えれば,ObjectContextオブジェクトのSetAbortメソッドを呼び出したら――すべてのCOMオブジェクトがデータベースに対して実行した操作はアボートされ,トランザクションに入るまえの状態,すなわちこのCOMオブジェクトを実体化するまえの状態までロールバックされる。
なお,List 5-12では,(1)受注レコードを書き込む,(2)在庫を確認する,(3)在庫を減らす,という順序で処理をしているが,この処理順序とトランザクションとの依存関係はない。たとえば,(1)在庫を確認する,(2)在庫を減らす,(3)受注レコードを書き込む,という順序に変更してもかまわない。
Chapter 5-2 5/16 |