Visual Basic 6.0がやってくる!

Visual Basic 6.0データアクセス新機能(後編)



福岡 寿和 FUKUOKA,Toshikazu
富士通SSL



 OLE DBは,“汎用”データアクセスパラダイムを導入する低水準の新しいインターフェイスです.すなわち,OLE DBはISAM,Jet,リレーショナルデータソースに限定されず,形式や記憶方法にも関係なく任意のデータ型を処理することができます.実際,この汎用性は,Excelの表計算ソフト,テキストファイル,Microsoft Exchange などのグループウェア,または,ActiveXサーバーなどのビジネスオブジェクトに存在するデータにもアクセスできると言うことを意味しています.
Visual Basic Version 6.0では,OLE DBとのプログラミング インターフェイスの ADO を介してOLE DBのフレキシビリティを利用することができます.また,独自のOLE DBプロバイダを作成することもできます.

ADO2.0概要

 ODBCにも言えることですが,OLE DBも複雑なインターフェイスのためにVisual Basicから直接アクセスするようには設計されていません.そのため,ODBCにDAOやRDOがあるようにOLE DBには,ActiveXデータオブジェクト(ADO)があります(図1).そして,ODBCがAPIによりDAOやRDOと接続しているのに対して,OLE DBとADOは,COMにより接続しています.OLE DBは,データを公開するデータプロバイダとデータを使用するデータコンシューマ,データを処理するサービスコンポーネントから構成(図2)されていて,その間もCOMベースで接続されています.

図1:Visual Basic 6.0での各データアクセス手法の構造
図1

図2:OLE DBのデータコンシューマとサービスコンポーネントの関係
図2

 ADOのオブジェクト構造は,「これで,ユニバーサルデータアクセスが実現できるのか?」と心配になるくらい単純なものです(図3).

図3:ADOのオブジェクト構造図
図3

しかし,ADOの難しさは,このオブジェクト構造図には表われてきません.なぜなら,Connectionオブジェクト,Commandオブジェクト,Recordsetオブジェクトには,個々のOLE DBプロバイダ固有の動的プロパティが含まれています.この動的プロパティが,ADOの柔軟性と共に難しさの原因となります.プログラムを作成するときには,基本的なプロパティのみ使って汎用性を高めるのか,動的プロパティを使って最適化していくのかを考慮する必要があります.

ADO2.0のオブジェクト

Connectionオブジェクト

 ADOを使うときには,まずConnectionオブジェクトを作成して,OLE DBプロバイダとの接続を確立します.Connectionオブジェクトを作成するには,次のようなコードを実行します.

Set adcnnTest = New ADODB.Connection
 次にConnectionStringプロパティに接続文字列を設定します.たとえば,ODBCを使うときは(データソース名をVBMとする),

adcnnTest.ConnectionString ="Provider=MSDASQL;DSN=VBM"
を実行します.なお,Visual Basic 6.0に付属しているOLE DBプロバイダごとの設定値を表1にまとめてみました.

表1:OLE DBプロバイダ別ConnectStringプロパティ設定値
OLE DBプロバイダ ConnectStringプロパティの例
Microsoft OLE DB Provider for ODBC Drivers Provider=MSDASQL;User ID=<ユーザーID>Data Source=<データソース名>
Microsoft Jet 3.51 OLE DB Provider Provider=Microsoft.Jet.OLEDB.3.51;Data Source=<MDBファイル名>
Microsoft OLE DB Provider for SQL Server Provider=SQLOLEDB;User ID=<ユーザーID>;Data Source=<サーバー名>
Microsoft OLE DB Provider for SQL Server Provider=SQLOLEDB;User ID=<ユーザーID>;Data Source=<サーバー名>
Microsoft OLE DB Provider for Oracle Provider=MSDAORA; User ID=<ユーザーID>;Data Source=<データベース別名>

 最後に,Openメソッドを使って,OLE DBプロバイダと実際に接続します.

AdcnnTest.Open

Errorオブジェクト

 ADOでは,メソッドの実行などで複数のエラーが発生するときがあります.それは,指定したOLE DBプロバイダが検出したエラーです.このとき,ADOはそのすべてのエラーをErrorsコレクションの中に保存します(リスト1・図4).もちろん,別の ADO 操作でエラーが発生すると,以前のErrors コレクションは消去され,最新のErrorsコレクションが作成されます.

図4:ADOのエラーメッセージ
図4

 また,ADOは,エラーが通知されるとOn Error Gotoでトラップできるエラーイベントも発生させるので,Errorオブジェクトに対する処理をモジュール内で一個所に集めることができます.
 エラーが発生しない ADO 操作は Error オブジェクトに影響を与えないので,Errors コレクションを消去する場合は,Clear メソッドを使用します.

リスト1:Errorsコレクションの使用例
    strMsg = ""
    With conADOConn
        For iintLoop = 1 To .Errors.Count
            strMsg = strMsg & .Errors(iintLoop - 1).Description & _
                     "(" & .Errors(iintLoop - 1).NativeError & ")" & vbCrLf & _
                     .Errors(iintLoop - 1).Source & _
                     "(" & .Errors(iintLoop - 1).SQLState & ")" & _
                     vbCrLf & vbCrLf
        Next
    End With
    MsgBox strMsg, vbOKOnly + vbExclamation, App.Title

Recordsetオブジェクト

 Recordsetオブジェクトを作成して,OpenメソッドによりSQL文,テーブル名,Commandオブジェクトを指定し,これをConnectionオブジェクトと結合することで,検索結果をレコードセットとして生成します.Recordsetオブジェクトを作成するには,次のようなコードを実行します.

Set adrstTest = New ADODB.Recordset
 次に引数に値を設定してOpenメソッドを実行するか,引数に対応したプロパティに値を設定してから,Openメソッドを実行します.今回は,説明の都合上,プロパティに値を設定してからOpenメソッドを実行することにします.

◆CusrorTypeプロパティ(省略可能)
 OLE DBプロバイダが使うカーソルの動作モードを設定します(表2).この中で一番高速なのが,前方スクロールタイプカーソル,汎用性が高い(あらゆるモードで使える)のは,静的カーソル,Openメソッドなどの再発行をせずに他のユーザーの変更値を取得したいときは動的カーソルを使うことになります.

表2:CursorTypeの設定値
CursorType 説明
AdOpenForwardOnly (デフォルト値)前方スクロールタイプカーソル.レコードを前方向のみにスクロールできることを除き,静的カーソルと同じ属性を持ちます.レコードセットを一度だけ読み込みならば,このカーソルにより性能が向上します
AdOpenKeyset キーセットカーソル.動的カーソルと同じような属性を持ちますが,他のユーザーが追加したレコードを表示せず,使用中のレコードセットから他のユーザーが削除したレコードへのアクセスを許可しません.他のユーザーが変更したデータは,表示することができます.つまり,レコード件数を変えないこと点が動的カーソルと異なっています
AdOpenDynamic 動的カーソル.他のユーザーによる追加,変更および削除を見ることができます
AdOpenStatic 静的カーソル.データの検索またはレポートの作成に使用するための,レコードの静的コピーです.他のユーザーによる追加,変更または削除は表示されません

◆LockTypeプロパティ(省略可能)
 OLE DBプロバイダが使うロック (同時実行制御) の種類を指定します(表3).

表3:LockTypeプロパティの設定値
LockType 説明
adLockReadOnly (デフォルト値)読み取り専用.データの変更はできません
adLockPessimistic 排他的ロック.通常,編集の際にプロバイダがデータソースでレコードをロックすることにより,確実にレコードを編集できます
adLockOptimistic 共有的ロック.Updateメソッドを呼び出した場合のみ,プロバイダが共有的ロックを使用してレコードをロックします
adLockBatchOptimistic 共有的バッチ更新.バッチ更新モードの場合に必要です.

◆CommandTypeプロパティ(省略可能)
 OpenメソッドのSource 引数が Command オブジェクト以外のときに,引数をOLE DBプロバイダが評価する方法を指定します(表4).

表4:CommandTypeプロパティの設定値
CommandType 説明
AdCmdText コマンド文字列として評価する.
AdCmdTable 指定値をテーブル名と評価して,テーブルからすべての行を返す SQL クエリーを生成する
AdCmdTableDirect テーブルからすべての行を返す
AdCmdStoredProc ストアド プロシージャとして評価する
AdCmdUnknown コマンド タイプが不明であることを指定します
AdCmdFile 指定値をファイル名として,そこに保存(永続化)されたRecordset を復元する
AdExecuteNoRecords 行を返さないコマンドまたはストアド プロシージャであることを示します

 各プロパティを設定したら,Openメソッドを実行します.

strSQL = "SELECT * FROM Titles"
adrstTest.Open strSQL,adcnnTest
 このように作成されたレコードセットは,DAOやRDOのように,MoveFirst,MoveLast,MoveNext,MovePreviousなどのカレント行移動メソッドやBof,Eofプロパティなどを使うことができます.

Fieldオブジェクト

 レコードセットのカレント行(現在行)の値を取得するためのオブジェクトがFieldオブジェクトになります.実際には,Fieldsコレクションの配列の添字を指定したり,連想配列としてFieldsコレクションを使って,フィールド名を指定して値を取得することが多いようです.

adrstTest.Fields(0).Value
adrstTest.Fields("Year Published").Value

Commandオブジェクト

 Commandオブジェクトは,OLE DBプロバイダに問い合わせて,Recordsetオブジェクトを返します.パラメタクエリーやストアドプロシージャの実行も可能です.

Set adcmdTest = New ADODB.Command
Set adcmdTest.ActiveConnection = adcnnTest
adcmdTest.CommandText = strSQL
adcmdTest.Execute
adrstTest.Open adcmdTest
 Commandオブジェクトの実行結果がレコードセットを返すときは,このオブジェクトを指定して,RecordsetオブジェクトのOpenメソッドを実行します.

Parameterオブジェクト

 Parameterオブジェクトは,Commandオブジェクトのパラメタを指定します.入力パラメータ,出力パラメータのどちらでも指定できます.

Set adprmTest = New ADODB.Parameter
adprmTest.Type = adInteger
adprmTest.Size = 3
adprmTest.Direction = adParamInput
adprmTest.Value = intRoyalty
adcmdTest.Parameters.Append adprmTest
 Typeプロパティによりパラメータの型を指定,Sizeプロパティでパラメータの長さを指定,Directionプロパティで入出力方向を指定,そして,Valueプロパティでパラメータの値を設定(もしくは取得)します.

ADO2.0のカーソル

 ADO2.0のカーソルには,ローカルカーソルライブラリとデータプロバイダカーソルがあります.カーソルの選択は,ConnectionオブジェクトのOpenメソッド実行前にCursorLocationプロパティに値を設定することで行ないます.
 ただし,バッチカーソルは,互換性のためにあるだけで,ADOでは,データプロバイダカーソルを宣言して,LockTypeプロパティでバッチ更新を指定します(リスト2).
 どちらのカーソルを選択するかについてですが,汎用性ならばローカルカーソル,性能ならばデータプロバイダカーソルを選択することがひとつの目安です.

リスト2:バッチ更新
Dim adrstTitles As ADODB.Recordset
Dim strCnn      As String

strCnn = "Provider=sqloledb;" & _
         "Data Source=srv;Initial Catalog=pubs;User Id=sa;Password=;"
Set adrstTitles = New ADODB.Recordset
adrstTitles.LockType = adUseServer
adrstTitles.CursorType = adOpenKeyset
adrstTitles.LockType = adLockBatchOptimistic
adrstTitles.Open "titles", strCnn, , , adCmdTable
adrstTitles.MoveFirst
Do Until adrstTitles.EOF
    adrstTitles.Fields("Type").Value = "self_help"
    adrstTitles.MoveNext
Loop
adrstTitles.UpdateBatch

ADO2.0の非同期処理
 非同期実行

 ADO2.0でもRDO1.0のように非同期実行ができます.対象は,Connection,RecordsetおよびCommandオブジェクトです.

Connectionオブジェクト

 OpenメソッドのOptions引数にadAsyncExecuteを設定することで接続の非同期実行ができます.

adcnnTest.Open , , ,adAsyncExecute
Do While adrstTest.State = adStateConnecting
    DoEvents
Loop

Recordsetオブジェクト

 OpenメソッドのOptions引数にadAsyncExecuteを設定することで,レコードセット作成の非同期実行ができます.

adrstTest.Open strSQL,adcnnTest, , ,adAsyncExecute
Do While adrstTest.State = adStateFetching
    DoEvents
Loop

Commandオブジェクト

 ExecuteメソッドのOptions引数にadAsyncExecuteを設定することでコマンドの非同期実行ができます.

adcmdTest.Execute, , ,adAsyncExecute
Do While adcmdTest.State = adStateExecuting
    DoEvents
Loop

ADO2.0の非同期処理
イベントドリブン

 ADO2.0では,ConnectionオブジェクトとRecordsetオブジェクトに対して,イベントドリブンによる非同期モードを用意しています(表5).イベントドリブンを有効にするためには,

Private WithEvents adcnnEvt  As ADODB.Connection
のように変数の宣言時に「WithEvents」を付加します.この変数宣言は,Declarations部にしか記述できません.それは,このイベントドリブン変数宣言が行なわれるとオブジェクトボックスにその変数名が追加されて,すぐに対応したイベントのプロシージャ部を記述できるようになるからです(図5).

図5:「With Event」で宣言した変数はオブジェクトボックスで参照可能になる
図5

表5:追加されるイベント
オブジェクト イベント 概要
Connection BeginTransComplete トランザクションの開始直前に発生
CommitTransComplete トランザクションの終了直後に発生
RollBackTransComplete ロールバック直後に発生
WillConnect 接続する直前に発生
ConnectComplete 接続成功(もしくは失敗)直後に発生
Disconnect 切断完了直後に発生
WillExecute コマンド開始直前に発生
ExecuteCompleate コマンド終了直後に発生
InfoMessage プロバイダから追加情報があるときに発生
Recordset FetchProcess レコード取得時に定期的に発生し,取得済みレコード数と取得予定レコード数が返却される.これにより取得率を求めたりプログレスバーに進行状況を表示可能
FetchComplete レコード取得が完了したときに発生
WillChangeField フィールド値の変更直前に発生
FieldChangeComplete フィールド値の変更直後に発生
WillMove カレント行の移動直前に発生
MoveComplete カレント行の移動直後に発生
EndOfRecordset レコードセット最後に達した直後に発生
WillChangeRecord カレント行のデータが変化する直前に発生
RecordChangeComplete カレント行のデータが変化した直後に発生
WillChangeRecordset 現在のレコードセットのデータが変化する直前に発生
RecordSetChangeCompleate 現在のレコードセットのデータが変化した直後に発生

ビジュアルデータマネージャ

 11月号に掲載した前編の図18で,ビジュアルデータマネージャに使われているミドルウェアがDAO/Jetのみであるように図示していますが,Visual Basic 6.0からは,DAOの他にADOをミドルウェアとして採用しています(図6).どのミドルウェアを使うかは,ビジュアルデータマネージャのツールバーで指定できます.

図6:Visual Basic 6.0のビジュアルデータマネージャが利用するミドルウェア
図6

★従来のDACを使うときは,このアイコンを選択して,該当するテーブルを開きます.DAC(データソース)とTextBoxコントロール(データコンシューマ)の連結も設計時に行なわれているため,とくに目新しい機能は使われていません.

★DACもADODCも使わずにDAO/Jetのレコードセットを直接使っています.フォームの中で

Public mrsFormRecordset As Recordset
と宣言したファイルがそのレコードセットになります.実際にレコードの内容をTextBoxコントロールに設定するのは,modVisDataモジュールのDisplayCurrentRecordサブルーチンの中になります.

★ADODCを使っています.ただし,ADOでオープンしたレコードセットを代入しています.ModVisDataモジュールのOpenTableプロシージャなどの中身を見るとその代入処理の場所ば見つかるでしょう(実際は,frmDataGridフォームのパブリック変数であるmrsFormRecordset変数を介してADOレコードセットとADODCを関連づけています).このモードで使われるOLE DBプロバイダは,AccessのときはOLE DB Provider for Microsoft Jet,その他を選択したときはMicrosoft OLE DB Provider for ODBCが選択されます.
 このビジュアルデータマネージャを題材にして,ADOの実用的プログラムを解説してゆきたいと考えていましたが,実際にプログラムを動作させてみると,ADODCを選択したときに,1レコード目の第一フィールドの値がクリアされてしまうことが判明しました.Bibilo.mdbのTitleテーブルをADODCで開こうとすると図7のように表示され,実際に空白がフィールドに設定されてしまいます.ADODCで開く前に他の方法で開いたときには図8のように正常に表示されるので,ADODCかDataGridコントロールもしくはこの2つを組み合わせたときのみ発生する障害のいずれかであると推測されます.そこで,路線を変更して,この障害がどこに起因するか,またどうすれば回避できるのかを探ってみたいと思います.

図7:Bibilo.mdbのTitleテーブルをADODCで開く
図7

図8:同じMDBファイルをADODC以外の方法で開いたところ
図8
図8

ビジュアルデータマネージャ
Titlesを正しく表示させるには

ADOを調査する

 まずは,ADOについて調査してみます.ビジュアルデータマネージャでは,ADOでOpenしたレコードセットをADODCのレコードセットに受け渡しています.ADOレベルで値が取れてきているかを判定するために,ADOがレコードセットを作成した直後に先頭レコードのフィールド値をデバッグ表示してみたいと思います.ADOがレコードセットを作成しているのは,modVisDataのOpenTableプロシージャです.

Set rsADOTmp = New ADODB.Recordset
With rsADOTmp
    .Open rsTmp.Name, _
          conADOConn, adOpenStatic, _
          adLockOptimistic, adCmdTable
End With
Set frmTmp.mrsFormRecordset = rsADOTmp
この直後に

Debug.Print rsADOTmp.Fields(0).Value
と記述して実行すると正しく値が取得できます.つまり,ADOからADODCにレコードセットを受け渡すところよりあと(受け渡しそのもの,ADODC内部,ADODCとDataGridコントロールの連結のどれか)に原因がありそうです.

ADODCを調査する

 ADOではきちんと値が取れてきていますので,次はADOのレコードセットをADODCに設定する部分を疑ってみましょう.場所は,frmDataGridのForm_Loadプロシージャです.

Set datDataCtl.Recordset = mrsFormRecordset
ここでも先ほどを同じように値が取れているかをデバッグ表示してみると

Debug.Print  datDataCtl.Recordset.Fields(0).Value
値が取れてきているのが分かります.

DataGridコントロールを調査する

 こうなってくるとADODCとDataGridの相性問題なのかもしれませんが,試しにレコードセットの設定や上記修正部分をコメントアウトして,設計時にプロパティを指定して実行すると一部表示が変ですが,値が表示されます(図9).

図9:設計時にプロパティを指定して実行すると値が表示される
図9

 ADODCとDataGridコントロールは,設計時にDataGridコントロールのDataSourceプロパティに値を設定しておくことで連結しています.これは,データコントロール(DAO)やRDC(RDO)時代からの連結方法です.普通は「昔からある方法なんだから,ここが変だとは考えられない」と思うかもしれません.しかし,使う側だけではなく,作成側やテスト側もそう思ってモノができ上がっている可能性だってあります.そこで,ADODCから新しく加わった実行時連結機能に切り替えてみます.まず,frmDataGridフォームのgrdDataGridコントロールのプロパティウィンドウで,DatSourceプロパティの値を空白クリアします.そして,同じフォームのForm_LoadプロシージャのADOのレコードセットをADODCに設定している行の次に,

Set grdDataGrid.DataSource = datDataCtl
と記述して実行すると,図9と同一の表示が表われます.どうやら,ADODCとDataGridコントロールは実行時連結することでデータを壊さずに表示できるようです.

完全な表示を求めて

 しかし,まだ完全に正しい表示は得られていません.
 引き続きいろいろ調査をしていると,偶然にもDataGridコントロールが最初にフォーカスを持っていないときは正しく表示されることがわかりました.これはDataGridコントロールが,セルがアクティブになったときセルに完全に表示されている単語までをハイライト表示するためのようです.そのため,DataGridコントロールを使う限りは回避できないものですが,先頭のセルをアクティブにしなくてもよいならば対応策はあります.
 この対応策をどうやって発見したかと言いますと,実行時連結のタイミングを見たいために,Form_Loadではなく,新たにコマンドボタンコントロールを貼り付けて,その中で実行時連結を行なったら正しく表示されたのです.さらに,この貼り付けたコントロールのTabIndexプロパティを「0」に設定すると,設計時連結したDataGridコントロールでも正しく表示されます(図10).しかし,画面に無造作にコマンドボタンコントロールが表示されているのも無粋ですし,表示後にDataGridコントロールにフォーカスが移っていないのも使いづらいでしょう.

図10:DataGridコントロールからフォーカスが外れている場合には正しく表示される
図10

 そこで,
  1. コマンドボタンコントロールをDataGridコントロールの後ろに配置します(図11).このとき,コントロールのロックがかかっていると配置メニューが有効になっていないので,そのようなときはコントロールのロックを外してください.

    図11:コマンドボタンコントロールをDataGridコントロールの後ろに配置する
    図11

  2. タイマーコントロールを貼り付けて,次のようなプロパティ値を設定します.
    (オブジェクト名) tmrLoad Enabled False Interva 1
  3. Form_Loadプロシージャの最後で

    tmrLoad_Enabled = True
    
    とします.

  4. tmrLoad_Timerプロシージャの中で

    tmrLoad.Enabled = False
    grdDataGrid.SetFocus
    
    と記述します.
しかし,この方法でも図9と同じ状態になってしまいます.解決方法は,DataGridコントロールにSetFocusせず,コマンドボタンコントロールのEnabledプロパティをFalseにすることで結果的にフォーカスがDataGridコントロールに移るように制御することです.tmrLoad_Timerプロシージャの中を

tmrLoad.Enabled = False
cmdDummy.Enabled = False
'  grdDataGrid.SetFocus
と変更すると図11のように正しい表示になります.

ビジュアルデータマネージャ
Title Authorsを正しく表示させるには

 Titlesテーブルは無事に表示することができました.しかし,ビジュアルデータマネージャには,Title Authorsテーブルのように間に空白が入っているテーブルだとADOで開けないという症状もあります(図12).

図12:データ内に空白が入っているテーブルだとADOで開けない
図12

 これは,ADOでレコードセットを作成しているところが原因です.Jetでは,空白を含む要素を使うときに[]で囲むことから類推して,modVisDataモジュールのOpenTableプロシージャの「.Open rsTmp.Name, conADOConn, ……」を「.Open "[" & rsTmp.Name & "]", conADOConn,……」と変更すると,TitleAuthorsテーブルも含めてすべてのテーブルが正しく表示できます.

RDS概要

 ADO2.0では,RDS(Remote Data Service)と言ってADO Recordsetオブジェクトを,同一マシン内の異なるプロセスやネットワークを介した異なるプロセス間でリモート処理することができます.この機能を使うとまるで(OracleのPL/SQL表のように)ストアドプロシージャの戻り値に複数レコードが格納されているような処理を行なうことができます.
 リモート処理できるのは,Recordsetオブジェクト以下の部分なので,呼び出し側用にADORライブラリ(Microsoft ActiveX Data Object Recordset 2.0 Library)が用意されています.このライブラリは,ADOライブラリと比べてCommand,ConnectionおよびParametersオブジェクトが含まれていない軽量なADOライブラリですから,呼び出し側が参照設定するのに最適なライブラリです.

RDSのオブジェクト

RDS.DataSpace

 RDS.DataSpaceは,RDSクライアントからRDSサーバーを呼び出す方法を提供しています.RDSサーバーは中間層(図13)にあるActiveX DLLやActiveX EXEのことで,RDSクライアントとの間は,http,https(HTTP Secure Socket),DCOM,COMプロトコルで接続されます.ここで注目したいのは,COM/DCOMだけではなくhttpやhttpsのプロトコルにも対応しているので,NTネットワークを構築しなくても利用も可能な点です.

図13:
図13

RDSServer.DataFactory

 RDSServer.DataFactoryは,RDSが標準で用意したRDSサーバープログラムです.このオブジェクトとRDSクライアントが接続することにより,指定されたデータソースの読み書きが可能です.通常はこのオブジェクトを使うことで事足りますが,httpやhttpsプロトコルを使うときは,ADISAPコンポーネントによりインスタンス化されるので,WebサーバーにInternet Information Serverを使ったイントラネット,インターネット,エクストラネットである必要があります.

RDS.DataControl

 RDS.DataControlは,ADODCのRDS版でRDS.DataSpaceおよびRDS.DataFactoryを自動的に呼び出します.

データソースクラスモジュールの作成

 クラスモジュールをデータソースとして指定できるようにするには,
  1. クラスモジュールのDataSourceBehavior プロパティにvbDataSourceを設定
  2. BindingCollectionオブジェクトのData Sourceプロパティにクラスモジュールを指定
  3. BindingCollectionオブジェクトにAddメソッドを使って,クラスモジュールをデータソースとして使いたいデータコンシューマを追加
という手順をとります.
 実際には,この他にデータコンシューマが新しいデータソースを要求するときにGetDataMemberイベントが発生するので,データソースとなるクラスモジュールのClass_GetDataMemberプロシージャに初期化ロジックをコーディングします.また,メソッドとしてデータソースの操作メソッドを公開したりします.

OLE DBプロバイダの作成

OLE DBプロバイダ

 Visual Basicを使用すると,「ActiveX DLLプロジェクトでDataSourceBehaviorプロパティをvbOLEDBProviderに設定することによって独自のOLE DBプロバイダを作成することができます」とありますが,クラスモジュールのInstancingプロパティにPrivate以外を設定してパブリッククラスとしても,DataSourceBehaviorプロパティにvbOLEDBProviderを設定することはできませんでした.

OLE-DB Simple Providerの利用

 OLE DBプロバイダを0から作成できるかは不明ですが,OLE-DB Simple Providerをインプリメントしてクラスモジュールを作成することで,半オーダーメードのOLE DBプロバイダを作成することはできます.OLE DBプロバイダとして実装しなくてはいけないメソッドが始めから定義されるので,こちらの方法の方が手軽です.
  1. 参照設定で
    Microsoft Data Source Interfaces
    Microsoft OLE DB Simple 1.5 Provider Library
    OLE DB Errors Type Library
    の3つを指定します

  2. クラスモジュールのDeclarationsに

    Implements OLEDBSimpleProvider
    
    を記述して,OLE DB Simple Providerをインプリメントすることで,クラスモジュール内にOLEDBSimpleProviderオブジェクトが追加され,インプリメントされたイベントがプロシージャとして追加されます(表6)

    表6:OLE DB Simple Providerのインターフェイス
    インターフェイス 記述する処理
    addOLEDBSimpleProviderListener [データ変更通知イベント]複数のselect文を一度に処理するなどの複数行セットを扱うときは,その個数分だけリスナーを追加する.通常は,リスナーがないようならば追加するだけ(扱う行セットはひとつ)でよい
    deleteRows 管理しているデータから情報を削除する.また,リスナーのaboutToDeleteRowメソッドdeletedRowメソッドでリスナーにも通知する
    find 検索処理,カレント行の移動,カレント位置を返却する
    getColumnCount カラム数を返却する
    getEstimatedRows 概算の行数を返却する(非同期実行の途中経過として使われる)
    getLocale RFC 1766に基づいたLCIDを返却する
    getRowCount 行数を返却する
    getRWStatus 指定行,列の読み書き可能/不可能を返却する
    getVariant 指定行,列の値を返却する
    insertRows 管理しているデータに情報を追加する.また,リスナーのaboutToInsertRowメソッドとinsertedRowメソッドでリスナーにも通知する
    inAsync 非同期実行をサポートするかを返却する
    removeOLEDBSimpleProviderListener 複数行セットを扱っているときの後処理としてリスナーを削除する
    setVariant 指定行,列の値を更新する.また,リスナーのaboutToChangeCellメソッドとcellChagedメソッドでリスナーにも通知する
    stopTransfer [実行完了イベント]非同期実行モードによる実行が完了したので,通常はとくに処理を記述する必要はない

  3. 追加されたプロシージャに処理を記述します
 BindingCollectionオブジェクトを使ってクラスモジュールをデータソースとして定義したときと異なり,このようにOLE DBプロバイダとしてクラスモジュールを定義することで,データアクセス機能をカプセル化することができます.

データアクセス系ウィザード
データオブジェクトウィザード

 データオブジェクトウィザードは,Data Environmentオブジェクトをデータソースとするクラスモジュールを自動生成します.データオブジェクトウィザードは,さらにその作成したデータソースクラスモジュール(ウィザードで作成しなくてもよい)を指定してユーザーコントロールを自動生成します.
 この2段階のウィザード操作により,データコントロールを使わないデータアクセスアプリケーション作成の最初の1歩の手間が軽減できます.生成されたモジュールを見るとヘッダコメントもきちんとつけられていて,比較的読みやすいコードになっていました.

データアクセス系ウィザード
データフォームウィザード

 オブジェクトを作らずにデータアクセスアプリケーションを作りたいときは,データフォームウィザードを使います.データフォームウィザードの起動は,アドインからではなく,フォームモジュールの追加から選択します(図14).

図14:「フォームモジュールの追加」からデータフォームウィザードを起動する
図14

生成できるフォームの種類は,
の4種類です.そして,データソースとしては,ADODC,ADO,クラスの3つから選択できます.

さいごに

 ADOをいろいろ触ってみて感じたのは,「遅い」ということです.また,プロパティがOLE DBプロバイダごとに追加可能という点も驚きでした.要は,DAOやRDOのように情報のアクセス手段をODBCという共通アクセスインターフェイスに閉じ込めるのではなく,それぞれに適したアクセス手段をOLE DBプロバイダで提供して,それをADOの動的プロパティとしてプログラムから使えるということです.Visual Basicのデータアクセスの歴史を見ると,「DAO/JetによるSQL文も含めた統一化」→「RDOによる呼び出しインターフェイスのみの統一化(SQL文はRDBMSごとに異なる)」→「ADOによる拡張性のある呼び出しインターフェイスの提供」のように変化してきています.
 たとえば,RDBMSアプリケーションを作り始めて,取りあえず動き始めるまでならば,RDBMSに左右されないADOが比較的優位です.ただ,ADOがDAOやRDOと異なるのは,パフォーマンスチューニングやRDBMSに特化した使い方をしたくなったとき,手間はかかりますが,どうにかなるようなプロパティが提供される余地が残っているところです.これはADOの優位な点です.ただし,この手間は,RDBMSネイティブなVisual Basic用インターフェイス(SQL ServerならばRDO,Oracleならば,Oracle Objects for OLE)と変わらないし,パフォーマンス的には,まだまだネイティブなインターフェイスの方が優れているようです.


VB Magazine ライブラリ| Visual Basic WorkGroup
int21 ホームページ| PCDN ホームページ

PCDN
Copyright (c) 1998 int21 CorporationAll Rights Reserved.
For questions or comments, please send mail to: pcdn@int21.co.jp