///
VC++
对
ADO
的扩展
///
对于
VC++
程序员而言,每次都要将
ADO
返回的数据转换成一般的
C++
数据类型,接着将数据存入一个类或结构总是一件枯燥的事。更讨厌的是这也带来了效率的低下。
因此,
ADO
提供了一个接口以支持将数据直接返回为一个本地化的
C/C++
数据类型而非
VARIANT
,并提供了一系列的预处理宏来方便使用这些接口。这样做的结果是一个复杂的工具可以很轻松的被使用并能获得很好的性能。
一个普通的
C/C++
客户场景是将一个
Recordset
中的一条记录绑定到一个包含本地
C/C++
数据类型的
C/C++
结构或类之上。如果通过
Variant
传递数据,这意味着要编写大量的转换代码,以在
VARIANT
和
C/C++
本地类型间进行数据转换。
VC++
对
ADO
的扩展出现的目的就是要简化这一过程。
如何使用
VC++
对
ADO
的扩展
IADORecordBinding
接口
VC++
对
ADO
的扩展联系或绑定了一个
Recordset
对象的各个字段到
C/C++
变量。当被绑定的
Recordset
的当前行改变时,其中所有被绑定的字段的值也同样会被拷贝到相应的
C/C++
变量中。如果需要,被拷贝的数据还会自动进行相应的数据类型转换。
IADORecordBinding
接口的
BindToRecordset
方法将字段绑定到
C/C++
变量之上。
AddNew
方法则是增加一个新的行到被绑定的
Recordset
。
Update
方法利用
C/C++
变量的值填充
Recordset
中新的行或更新已存在的行。
IADORecordBinding
接口由
Recordset
对象实现,你不需要自己编码进行实现。
绑定条目
VC++
对
ADO
的扩展在一个
Recordset
对象与一个
C/C++
变量间进行映像(
Map
)。一个字段与对应的一个变量间的映像被称作一个绑定条目。预定义的宏为数字、定长或不定长数据提供了绑定条目。所有的绑定条目与相应的
C/C++
变量都被封装、声明在一个从
VC++
扩展类
CADORecordBinding
派生的类中。这个
CADORecordBinding
类在内部由绑定条目宏定义。
在
ADO
内部,将所有宏的参数都映射在一个
OLE DB DBBINDING
结构中,并创建一个
OLE DB
访问子(
Accessor
)对象来管理所有的行为和字段与变量间的数据转换。
OLE DB
定义的数据由以下三部分组成:存储数据的缓冲区;一个状态值表示一个字段是否被成功地被存入缓冲区,或变量值是否被成功地存入字段;数据长度。(参见
OLE DB
程序员参考第
6
章:读写数据的更多信息)
所需的头文件
为了使用
VC++
对
ADO
的扩展,你得在你的应用中包含这个头文件:
#include <icrsint.h>
绑定
Recordset
的字段
要绑定
Recordset
的字段到
C/C++
变量,需要这样做:
1
.创建一个
CADORecordsetBinding
的派生类。
2
.在派生类中定义绑定条目和相应的
C/C++
变量。注意不要使用逗号、分号切断宏。每个宏都会自动地定义适当的分隔符。
为每个被映像的字段定义一个绑定条目。并注意根据不同情况选用
ADO_FIXED_LENGTH_ENTRY
、
ADO_NUMERIC_ENTRY
、
ADO_VARIABLE_LENGTH_ENTRY
中的某个宏。
3
.在你的应用中,创建一个该派生类的实例。从
Recordset
中获得
IADORecordBinding
接口,然后调用
BindToRecordset
方法将
Recordset
的所有字段绑定到对应的
C/C++
变量之上。
请参见示例程序以获得更多信息。
接口方法
IADORecordBinding
接口只有三个方法:
BindToRecordset, AddNew,
和
Update
。每个方法所需的唯一的参数就是一个
CADORecordBinding
派生类的实例指针。因此,
AddNew
和
Update
方法不能使用任何与它们同名的
ADO
方法中的参数。
语法
BindToRecordset
方法将字段绑定到
C/C++
变量之上。
BindToRecordset(CADORecordBinding *binding)
AddNew
方法则引用了它的同名
ADO
函数,来增加一个新的记录行。
AddNew(CADORecordBinding *binding)
Update
方法也引用了它的同名
ADO
函数,来更新
Recordset
。
Update(CADORecordBinding *binding)
绑定条目宏
绑定条目宏定义了一个
Recordset
字段与一个变量间的对应关系。每个条目的绑定宏由开始宏与结束宏组成并配对使用。
定长数据的宏适用于
adDate
,
adBoolean
等,数字的宏适用于
adTinyInt, adInteger
和
adDouble
等,变长数据的宏适用于
adChar, adVarChar
和
adVarBinary
等。所有的数字类型,除了
adVarNumeric
以外也是定长数据类型。每个宏的族之间都有不同的参数组,因此你可以排除不感兴趣的绑定信息。
参见
OLE DB
程序员参考附录
A
:数据类型的更多信息
开始绑定条目
BEGIN_ADO_BINDING(Class)
定长数据:
ADO_FIXED_LENGTH_ENTRY(Ordinal, DataType, Buffer, Status, Modify)
ADO_FIXED_LENGTH_ENTRY2(Ordinal, DataType, Buffer, Modify)
数字型数据:
ADO_NUMERIC_ENTRY(Ordinal, DataType, Buffer, Precision, Scale, Status, Modify)
ADO_NUMERIC_ENTRY2(Ordinal, DataType, Buffer, Precision, Scale, Modify)
变长数据:
ADO_VARIABLE_LENGTH_ENTRY(Ordinal, DataType, Buffer, Size, Status, Length, Modify)
ADO_VARIABLE_LENGTH_ENTRY2(Ordinal, DataType, Buffer, Size, Status, Modify)
ADO_VARIABLE_LENGTH_ENTRY3(Ordinal, DataType, Buffer, Size, Length, Modify)
ADO_VARIABLE_LENGTH_ENTRY4(Ordinal, DataType, Buffer, Size, Modify)
结束绑定
END_ADO_BINDING()
参数
|
描述
|
Class
|
派生类的名字。
|
Ordinal
|
从
1
开始的序号,对应于
Recordset
中的字段。
|
DataType
|
与
C/C++
变量对应的
ADO
数据类型
(
参见
DataTypeEnum
以获得有效数据类型的列表
)
。如果需要,字段的值会被转换成该类型的值。
|
Buffer
|
对应的
C/C++
变量的名字。
|
Size
|
该
C/C++
变量的最大字节数。如果是个变长字符串,使用
0
表示即可。
|
Status
|
指示变量的名字。该变量用以表示缓冲是否有效,数据转换是否成功。
值
adFldOK
意味着转换成功;
adFldNull
意味着该字段的值为空。其他可能的值见后面的状态值列表。
|
Modify
|
逻辑标志。
TRUE
意味着
ADO
允许利用变量值更新
Recordset
中的字段的值。
设置该值为
TRUE
将允许更新,如果你只想检查字段的值而不想改变它那么就设置为
FALSE
。
|
Precision
|
数字型变量的位数。
|
Scale
|
数字型变量的小数位数。
|
Length
|
一个
4
字节变量的名字。该变量将包含缓冲区中数据的实际长度。
|
状态值
变量
Status
的值指示了一个字段的值是否被成功的拷贝到了对应的变量中。写数据时,可以给
Status
赋值为
adFldNull
来指示该字段将被设置为
null
。
常量
|
值
|
描述
|
adFldOK
|
0
|
一个非空的字段值被返回。
|
adFldBadAccessor
|
1
|
绑定无效。
|
adFldCantConvertValue
|
2
|
值因为符号不匹配或超界外的原因导致无法被正确转换。
|
adFldNull
|
3
|
读字段值时,指示一个空值被返回。写字段值时,指示当字段自身无法编码
NULL
时该字段将被设置为
NULL
。
|
adFldTruncated
|
4
|
变长数据或数字被截断。
|
adFldSignMismatch
|
5
|
值是有符号数,而数据类型是无符号数。
|
adFldDataOverFlow
|
6
|
数据值超出界限。
|
adFldCantCreate
|
7
|
不知名的列类型和字段已经被打开。
|
adFldUnavailable
|
8
|
字段值无法确定。比如一个新的未赋值的无缺省值的字段。
|
adFldPermissionDenied
|
9
|
未被允许更新数据。
|
adFldIntegrityViolation
|
10
|
更新字段时值违反了列的完整性要求。
|
adFldSchemaViolation
|
11
|
更新字段时值违反了列的规范要求。
|
adFldBadStatus
|
12
|
更新字段时,无效的状态参数。
|
adFldDefault
|
13
|
更新字段时,使用缺省值。
|
使用
VC++
对
ADO
的扩展的示例
在这个例子中,还使用了
COM
专有的“智能指针”功能,它能自动处理
IADORecordBinding
接口的
QueryInterface
和引用计数。如果没有智能指针,你得这样编码:
IADORecordBinding *picRs = NULL;
...
TESTHR(pRs->QueryInterface(
__uuidof(IADORecordBinding), (LPVOID*)&picRs));
...
if (picRs) picRs->Release();
使用智能指针,你可以用这样的语句从
IADORecordBinding
接口派生
IADORecordBindingPtr
类型:
_COM_SMARTPTR_TYPEDEF(IADORecordBinding, __uuidof(IADORecordBinding));
然后这样实例化指针:
IADORecordBindingPtr picRs(pRs);
因为
VC++
的扩展由
Recordset
对象实现,因此智能指针
picRs
的构造函数使用了
_RecordsetPtr
类指针
pRs
。构造函数利用
pRs
调用
QueryInterface
来获得
IADORecordBinding
接口。
//
以下即是示例程序
#import "c:/Program Files/Common Files/System/ADO/msado15.dll" /
no_namespace rename("EOF", "EndOfFile")
#include <stdio.h>
#include <icrsint.h>
_COM_SMARTPTR_TYPEDEF(IADORecordBinding, __uuidof(IADORecordBinding));
inline void TESTHR(HRESULT _hr) { if FAILED(_hr) _com_issue_error(_hr); }
class CCustomRs : public CADORecordBinding
{
BEGIN_ADO_BINDING(CCustomRs)
ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_ch_fname,
sizeof(m_ch_fname), m_ul_fnameStatus, false)
ADO_VARIABLE_LENGTH_ENTRY2(4, adVarChar, m_ch_lname,
sizeof(m_ch_lname), m_ul_lnameStatus, false)
END_ADO_BINDING()
public:
CHAR m_ch_fname[22];
CHAR m_ch_lname[32];
ULONG m_ul_fnameStatus;
ULONG m_ul_lnameStatus;
};
void main(void)
{
::CoInitialize(NULL);
try
{
_RecordsetPtr pRs("ADODB.Recordset");
CCustomRs rs;
IADORecordBindingPtr picRs(pRs);
pRs->Open("SELECT * FROM Employee ORDER BY lname",
"dsn=pubs;uid=sa;pwd=;",
adOpenStatic, adLockOptimistic, adCmdText);
TESTHR(picRs->BindToRecordset(&rs));
while (!pRs->EndOfFile)
{
//
处理
CCustomRs
中的数据
printf("Name = %s %s/n",
(rs.m_ul_fnameStatus == adFldOK ? rs.m_ch_fname: "<Error>"),
(rs.m_ul_lnameStatus == adFldOK ? rs.m_ch_lname: "<Error>"));
//
移动到下一行,新行的值会被自动填充到对应的
CCustomRs
的变量中
pRs->MoveNext();
}
}
catch (_com_error &e )
{
printf("Error:/n");
printf("Code = %08lx/n", e.Error());
printf("Meaning = %s/n", e.ErrorMessage());
printf("Source = %s/n", (LPCSTR) e.Source());
printf("Description = %s/n", (LPCSTR) e.Description());
}
::CoUninitialize();
}