TN043:RFX 例程
注意
以下技术说明在首次包括在联机文档中后未更新。 因此,某些过程和主题可能已过时或不正确。 要获得最新信息,建议你在联机文档索引中搜索热点话题。
本说明介绍记录字段交换 (RFX) 体系结构。 此外介绍如何编写 RFX_ 过程。
记录字段交换概述
所有记录集字段函数都是用 C++ 代码执行的。 没有特殊的资源或神奇的宏。 该机制的核心是必须在每个派生记录集类中重写的虚拟函数。 它的形式始终如下:
void CMySet::DoFieldExchange(CFieldExchange* pFX)
{
//{{AFX_FIELD_MAP(CMySet)
<recordset exchange field type call>
<recordset exchange function call>
//}}AFX_FIELD_MAP
}
特殊格式 AFX 注释允许 ClassWizard 在此函数中查找和编辑代码。 与 ClassWizard 不兼容的代码应放置在特殊格式注释之外。
在上面的示例中,recordset_exchange_field_type_call 采用以下形式<>:
pFX->SetFieldType(CFieldExchange::outputColumn);
recordset_exchange_function_call 采用以下形式<>:
RFX_Custom(pFX, "Col2", m_Col2);
大多数 RFX_ 函数具有上面所示的三个参数,但有些函数(例如 RFX_Text
和 RFX_Binary
)具有额外的可选参数。
每个 DoDataExchange
函数可能包含多个 RFX_。
有关 MFC 提供的所有记录集字段交换例程的列表,请参阅“afxdb.h”。
记录集字段调用是注册内存位置(通常是数据成员)以存储 CMySet
类的字段数据的一种方式。
说明
记录集字段函数只能与 CRecordset
类一起使用。 它们通常不可由任何其他 MFC 类使用。
数据的初始值在标准 C++ 构造函数中设置,通常在具有 //{{AFX_FIELD_INIT(CMylSet)
和 //}}AFX_FIELD_INIT
注释的块中设置。
每个 RFX_ 函数必须支持各种操作,从返回字段的脏状态到存档字段以准备编辑字段。
每个调用 DoFieldExchange
的函数(例如 SetFieldNull
、IsFieldDirty
)都会围绕 DoFieldExchange
调用执行自身的初始化。
工作原理
无需了解以下知识即可使用记录字段交换。 但了解幕后工作原理有助于编写自己的交换过程。
DoFieldExchange
成员函数非常类似于 Serialize
成员函数 — 它负责为外部窗体(本例中为 ODBC 查询结果中的列)获取类中成员数据,或者将外部窗体数据设置为类中成员数据。 pFX 参数是执行数据交换的上下文,类似于 CObject::Serialize
的 CArchive 参数。 pFX(一个 CFieldExchange
对象)有一个操作指示符,该指示符类似于 CArchive 方向标志,但它是该标志的泛化。 RFX 函数可能必须支持以下操作:
BindParam
— 指示 ODBC 应在何处检索参数数据BindFieldToColumn
— 指示 ODBC 必须在何处检索/存放 outputColumn 数据Fixup
—设置CString/CByteArray
长度,设置 NULL 状态位MarkForAddNew
— 如果值自 AddNew 调用以来发生更改,则标记为脏MarkForUpdate
— 如果值自 Edit 调用以来发生更改,则标记为脏Name
— 为标记为脏的字段追加字段名称NameValue
— 为标记为脏的字段追加“<列名>=”Value
— 追加 "" 后接分隔符,例如“,”或“ ”SetFieldDirty
— 设置状态位脏(即已更改)字段SetFieldNull
— 设置状态位用于指示字段的 null 值IsFieldDirty
— 脏状态位的返回值IsFieldNull
— null 状态位的返回值IsFieldNullable
— 如果字段可以包含 NULL 值,则返回 TRUEStoreField
— 存档字段值LoadField
— 重新加载存档字段值GetFieldInfoValue
— 返回有关字段的一般信息GetFieldInfoOrdinal
— 返回有关字段的一般信息
用户扩展
可通过多种方式扩展默认 RFX 机制。 则可以
添加新数据类型。 例如:
CBookmark
添加新的交换过程 (RFX_)。
void AFXAPI RFX_Bigint(CFieldExchange* pFX, const char *szName, BIGINT& value);
让
DoFieldExchange
成员函数按条件包含其他 RFX 调用或任何其他有效的 C++ 语句。while (posExtraFields != NULL) { RFX_Text(pFX, m_listName.GetNext(posExtraFields), m_listValue.GetNext(posExtraValues)); }
注意
此类代码不能由 ClassWizard 编辑,只应在特殊格式注释之外使用。
编写自定义 RFX
若要编写自己的自定义 RFX 函数,建议复制现有的 RFX 函数并根据目的进行修改。 选择正确的 RFX 进行复制可以简化工作。 某些 RFX 函数具有一些独特的属性,在确定复制哪个函数时应考虑这些属性。
RFX_Long
和 RFX_Int
:最简单的 RFX 函数。 数据值不需要任何特殊解释,数据大小是固定的。
RFX_Single
和 RFX_Double
:与上面的 RFX_Long 和 RFX_Int 一样,这些函数非常简单,可以广泛使用默认实现。 但是,它们存储在 dbflt.cpp 而不是 dbrfx.cpp 中,以便仅在显式引用它们时才加载运行时浮点库。
RFX_Text
和 RFX_Binary
:这两个函数预先分配一个静态缓冲区来保存字符串/二进制信息,必须将这些缓冲区注册到 ODBC SQLBindCol,而不要注册 &value。 正因如此,这两个函数具有很多特殊情况代码。
RFX_Date
:ODBC 在自身的 TIMESTAMP_STRUCT 数据结构中返回日期和时间信息。 此函数动态分配一个 TIMESTAMP_STRUCT 作为发送和接收日期时间数据的“代理”。 各种操作必须在 C++ CTime
对象与 TIMESTAMP_STRUCT 代理之间传输日期和时间信息。 此函数因而变得相当复杂,但它很好地示范了如何使用代理进行数据传输。
RFX_LongBinary
:这是唯一一个不使用列绑定来接收和发送数据的类库 RFX 函数。 此函数忽略 BindFieldToColumn 操作,而是在 Fixup 操作期间分配存储来保存传入的 SQL_LONGVARCHAR 或 SQL_LONGVARBINARY 数据,然后执行 SQLGetData 调用以将值检索到分配的存储中。 准备将数据值发送回数据源时(例如 NameValue 和 Value 操作),此函数使用 ODBC 的 DATA_AT_EXEC 功能。 有关如何使用 SQL_LONGVARBINARY 和 SQL_LONGVARCHARs 的详细信息,请参阅技术说明 45。
当你编写自己的 RFX_ 函数时,通常可以使用 CFieldExchange::Default
来实现给定的操作。 查看相关操作的 Default 实现。 如果该实现执行你要在 RFX_ 函数中编写的操作,则可以委托给 CFieldExchange::Default
。 可以在 dbrfx.cpp 中查看调用 CFieldExchange::Default
的示例
必须在 RFX 函数的开头调用 IsFieldType
,并在它返回 FALSE 时立即返回。 此机制可防止对 outputColumns 执行参数操作,反之亦然(例如对 outputColumn 调用 BindParam
)。 此外,IsFieldType
会自动跟踪 outputColumns (m_nFields) 和参数 (m_nParams) 的计数。