テクニカル ノート 53: DAO データベース クラス用カスタム DFX ルーチン
Note
DAO は、Access データベースで使用され、Office 2013 を介してサポートされています。 DAO 3.6 は最終バージョンであり、古いバージョンと見なされます。 Visual C++ 環境およびウィザードでは DAO はサポートされていません (ただし、DAO クラスは含まれており、引き続き使用できます)。 Microsoft は、新しいプロジェクトには OLE DB テンプレートまたは ODBC および MFC を使用することをお勧めします。 DAO は、既存のアプリケーションを保守するためにのみ使用してください。
このテクニカル ノートでは、DAO レコード フィールド エクスチェンジ (DFX) メカニズムについて説明します。 DFX ルーチンで何が起こっているかを理解するために、DFX_Text
関数を例として取り上げ詳しく説明します。 このテクニカル ノートの追加情報として、他の個々の DFX 関数のコードを調査できます。 カスタム DFX ルーチンは、カスタム RFX ルーチン (ODBC データベース クラスで使用される) ほど必要とはされない可能性があります。
このテクニカル ノートの内容は次のとおりです。
DFX の概要
DAO レコード フィールド エクスチェンジと動的バインドを使用する例
DFX の概要
DAO レコード フィールド エクスチェンジ (DFX) メカニズムは、CDaoRecordset
クラスを使用するときにデータを取得および更新する手順を簡略化するために使用されます。 このプロセスは、CDaoRecordset
クラスのデータ メンバーを使用して簡略化されます。 CDaoRecordset
から派生させることで、テーブルまたはクエリの各フィールドを表す派生クラスにデータ メンバーを追加できます。 この "静的バインド" メカニズムは単純ですが、どのアプリケーションでも選択できるデータ フェッチ/更新方法というわけではありません。 DFX は、現在のレコードが変更されるたびに、バインドされたすべてのフィールドを取得します。 通貨が変更された場合にすべてのフィールドをフェッチする必要がない、パフォーマンスを重視するアプリケーションを開発している場合は、CDaoRecordset::GetFieldValue
および CDaoRecordset::SetFieldValue
を介した "動的バインド" を、データ アクセス方法として選択できます。
Note
DFX と動的バインドは相互排他的ではないので、静的バインドと動的バインドはハイブリッドで使用できます。
例 1 - DAO レコード フィールド エクスチェンジのみの使用
(CDaoRecordset
— 派生クラスCMySet
が既に開いていると想定します)
// Add a new record to the customers table
myset.AddNew();
myset.m_strCustID = _T("MSFT");
myset.m_strCustName = _T("Microsoft");
myset.Update();
例 2 - 動的バインドのみを使用する
(CDaoRecordset
クラス、rs
を使用すると想定され、既に開いている)
// Add a new record to the customers table
COleVariant varFieldValue1 (_T("MSFT"),
VT_BSTRT);
//Note: VT_BSTRT flags string type as ANSI,
instead of UNICODE default
COleVariant varFieldValue2 (_T("Microsoft"),
VT_BSTRT);
rs.AddNew();
rs.SetFieldValue(_T("Customer_ID"),
varFieldValue1);
rs.SetFieldValue(_T("Customer_Name"),
varFieldValue2);
rs.Update();
例 3 - DAO レコード フィールド エクスチェンジと動的バインドの使用
(CDaoRecordset
- 派生クラスemp
を使用して従業員データを参照すると想定します)
// Get the employee's data so that it can be displayed
emp.MoveNext();
// If user wants to see employee's photograph,
// fetch it
COleVariant varPhoto;
if (bSeePicture)
emp.GetFieldValue(_T("photo"),
varPhoto);
// Display the data
PopUpEmployeeData(emp.m_strFirstName,
emp.m_strLastName,
varPhoto);
DFX のしくみ
DFX メカニズムは、MFC ODBC クラスで使用されるレコード フィールド エクスチェンジ (RFX) メカニズムと同様の方法で動作します。 DFX と RFX の原則は同じですが、内部的な違いは多数あります。 DFX 関数は、事実上すべてのコードが個々の DFX ルーチンによって共有される設計でした。 最高レベルの DFX は、わずかなことしか実行できません。
DFX は、必要に応じて SQL SELECT 句と SQL PARAMETERS 句を構築します。
DFX は、DAO の
GetRows
関数によって使用されるバインディング構造を構築します (詳細については後述します)。DFX は、ダーティ フィールドの検出に使用されるデータ バッファーを管理します (ダブル バッファリングが使用されている場合)。
DFX は NULL および DIRTY 状態配列を管理し、更新時に必要に応じて値を設定します。
DFX メカニズムの中心は、CDaoRecordset
派生クラスの DoFieldExchange
関数です。 この関数は、適切な操作の種類の個々の DFX 関数への呼び出しをディスパッチします。 DoFieldExchange
を呼び出す前に、内部 MFC 関数は操作の種類を設定します。 次の一覧は、さまざまな操作の種類と簡単な説明を示しています。
操作 | 説明 |
---|---|
AddToParameterList |
PARAMETERS 句をビルドします |
AddToSelectList |
SELECT 句をビルドします |
BindField |
バインド構造体を設定します |
BindParam |
パラメーター値を設定します |
Fixup |
NULL 状態を設定します |
AllocCache |
ダーティ チェック用のキャッシュを割り当てます |
StoreField |
現在のレコードをキャッシュに保存します |
LoadField |
キャッシュをメンバー値に復元します |
FreeCache |
キャッシュを解放します |
SetFieldNull |
フィールドの状態と値を NULL に設定します |
MarkForAddNew |
疑似 NULL ではない場合にフィールドをダーティとしてマークします |
MarkForEdit |
キャッシュと一致しない場合にフィールドをダーティとしてマークします |
SetDirtyField |
ダーティとしてマークされたフィールド値を設定します |
次のセクションでは、DFX_Text
の各操作について詳しく説明します。
DAO レコード フィールド エクスチェンジ プロセスについて理解するべき最も重要な機能は、CDaoRecordset
オブジェクトの GetRows
関数を使用するという点です。 DAO GetRows
関数は、いくつかの方法で動作することが可能です。 このテクニカル ノートでは、GetRows
については対象範囲外であるため、簡単にしか説明していません。
DAO GetRows
は、いくつかの方法で動作することが可能です。
複数のレコードと複数のデータ フィールドを同時にフェッチできます。 これにより、データ アクセスを高速化し、大規模なデータ構造、各フィールドへの適切なオフセット、構造体内のデータの各レコードを複合的に処理できます。 MFC では、この複数のレコード フェッチ メカニズムは利用されません。
GetRows
のもう 1 つの動作として、プログラマはデータの 1 レコードに対して、各フィールドの取得されたデータのバインド アドレスを指定できます。また、DAO は、呼び出し元がメモリを割り当てられるようにするために、可変長列の呼び出し元に "コールバック" します。 この 2 つ目の機能は、データのコピー数を最小限に抑えるだけでなく、データをクラス (
CDaoRecordset
派生クラス) のメンバーに直接格納できるという利点があります。 この 2 つ目のメカニズムは、MFC が派生クラスのデータ メンバーにバインドするためにCDaoRecordset
使用するメソッドです。
カスタム DFX ルーチンの機能
この説明から明らかなのは、DFX 関数で実装される最も重要な操作は、GetRows
を正常に呼び出す必須のデータ構造を設定する機能でなければならないということです。 DFX 関数がサポートする必要がある操作は他にも多数存在しますが、GetRows
呼び出しを正しく準備するほど重要なものや複雑なものはありません。
DFX の使用については、オンライン ドキュメントで説明されています。 基本的に、2 つの要件があります。 まず、バインドされたフィールドとパラメーターごとに、CDaoRecordset
派生クラスにメンバーを追加する必要があります。 これに続いて CDaoRecordset::DoFieldExchange
をオーバーライドする必要があります。 メンバーのデータ型が重要であることに注意してください。 データベース内のフィールドからのデータと一致するか、少なくともその型に変換可能である必要があります。 たとえば、長整数などのデータベース内の数値フィールドは常にテキストに変換して CString
メンバーにバインドできますが、データベース内のテキスト フィールドは必ずしも長整数などの数値表現に変換され、長い整数メンバーにバインドされるとは限りません。 DAO と Microsoft Jet データベース エンジンは、(MFC ではなく) 変換を担当します。
DFX_Text の詳細
前述のように、DFX のしくみを説明する最善の方法は、例を使って作業することです。 この目的のために、DFX_Text
の内部動作を調べることは、少なくとも DFX の基本的な理解を得るのにとても役立ちます。
AddToParameterList
この操作により、Jet で必要とされる SQL PARAMETERS 句 ("
Parameters <param name>, <param type> ... ;
") が作成されます。 各パラメーターは名前が付けられ型指定されます (RFX 呼び出しで指定)。 個々の型の名前を確認するには、CDaoFieldExchange::AppendParamType
関数をご覧ください。DFX_Text
の場合、使用される型は text です。AddToSelectList
SQL SELECT 句をビルドします。 DFX 呼び出しで指定された列名が追加されるだけであるので ("
SELECT <column name>, ...
")、これは非常に簡単です。BindField
最も複雑な操作です。 前に説明したように、ここで、
GetRows
によって使用される DAO バインド構造が設定されます。DFX_Text
のコードからわかるとおり、構造体内の情報の型には、使用される DAO の種類が含まれます (DFX_Text
の場合は DAO_CHAR または DAO_WCHAR)。 さらに、使用されるバインドの種類も設定されます。 前のセクションでは、GetRows
については簡単に説明しましたが、MFC で使用されるバインドの種類は、常に直接アドレス バインド (DAOBINDING_DIRECT) であるとの説明で十分です。 さらに、可変長列バインド (DFX_Text
など) に加え、コールバック バインドが使用され、MFC でメモリ割り当てを制御し、正しい長さのアドレスを指定できます。 これはつまり、MFC は DAO に対して常に "どこに" データを配置するかを指示して、メンバー変数に直接バインドできることを意味します。 バインド構造の残りの部分には、メモリ割り当てのコールバック関数のアドレスや、列のバインドの型 (列名によるバインド) などの情報が格納されます。BindParam
これは、パラメーター メンバーに指定されたパラメーター値を使用して
SetParamValue
を呼び出す単純な操作です。Fixup
各フィールドの NULL 状態を入力します。
SetFieldNull
この操作では、各フィールドの状態を NULL としてマークするだけで、メンバー変数の値が PSEUDO_NULLに設定されます。
SetDirtyField
ダーティとマークされた各フィールドに対して
SetFieldValue
を呼び出します。
残りのすべての操作では、データ キャッシュの使用のみが扱われます。 データ キャッシュは、特定の項目をより簡単にするために使用される、現在のレコード内のデータの余分なバッファーです。 たとえば、"ダーティ" フィールドは自動的に検出できます。 オンライン ドキュメントで説明されているように、完全にまたはフィールド レベルでオフにすることができます。 バッファーの実装には、マップを利用します。 このマップは、データの動的に割り当てられたコピーを "バインドされた" フィールド (または CDaoRecordset
派生データ メンバー) のアドレスと照合するために使用されます。
AllocCache
キャッシュされたフィールド値を動的に割り当てて、それをマップに追加します。
FreeCache
キャッシュされたフィールド値を削除し、それをマップから削除します。
StoreField
現在のフィールド値をデータ キャッシュにコピーします。
LoadField
キャッシュされた値をフィールド メンバーにコピーします。
MarkForAddNew
現在のフィールド値が非 NULL かどうかを確認し、必要に応じてダーティとしてマークします。
MarkForEdit
現在のフィールド値とデータ キャッシュを比較し、必要に応じてダーティとしてマークします。
ヒント
標準データ型の既存の DFX ルーチンでカスタム DFX ルーチンをモデル化します。