修正 ADOX Table 对象的列序号






4.17/5 (6投票s)
2002年12月5日
4分钟阅读

97954

1437
获取 ADOX 表对象的正确列序数。
引言
ADOX Table
对象允许我们通过 Columns
集合轻松检索每个表的列。然而,该集合是按列/字段名排序的。在我的一个项目中,我需要支持以下功能:
- 以 MS Access 显示的相同顺序表示
columns
集合 - 能够在集合的任何位置插入新列(另一篇文章将讨论这一点)
仅使用 ADOX Columns
集合是不够的。因此,需要获取实际的列序数。
方法论
有两种方法可以获取列序数:
- 通过遍历 ADO
OpenSchema(adSchemaColumns,...)
返回的rowset
- 速度较慢 - 从 OLEDB
IColumnsInfo
接口获取DBCOLUMNINFO
数据 - 速度更快
本文讨论了如何从 IColumnsInfo
OLEDB 接口获取列序数。
IColumnsInfo
可从(由...实现)command
和 rowset
OLE DB 对象获得。我提出的方法基本由三个步骤组成:
- 从 ADOX
Table
创建 ADORecordset
- 将 ADO
Recordset
转换为 OLEDBIRowset
- 从
IRowset
获取IColumnsInfo
我们开始吧...
步骤 1. 从表中创建 ADO Recordset
// ADO recordset ADODB::_RecordsetPtr p_adoRecordset; p_adoRecordset.CreateInstance(__uuidof(ADODB::Recordset)); p_adoRecordset->Open(p_table->GetName(), _variant_t((IDispatch *)p_connection,true), ADODB::adOpenKeyset, ADODB::adLockReadOnly, ADODB::adCmdTable);
步骤 2. 将 ADO Recordset 转换为 IRowset
// First, get ADORecordsetConstruction interface from the the ADO Recordset ADODB::ADORecordsetConstruction * p_adoRecordsetConstruct; p_adoRecordset->QueryInterface(__uuidof(ADODB::ADORecordsetConstruction), (void **) &p_adoRecordsetConstruct); // From it, we can get the OLEDB <code>IRowset IRowset * p_rowset; p_adoRecordsetConstruct->get_Rowset((IUnknown **)&p_rowset); p_adoRecordsetConstruct->Release(); // don't need it anymore
步骤 3. 从 IRowset 获取 IColumnsInfo
从 IColumnsInfo
,我们可以获得包含我们所需信息的 DBCOLUMNINFO
数据。在此示例中,我创建了一个列名和序数的映射。
// The IColumnsInfo that contains ordinals CComPtr<IColumnsInfo> spColumns; // Determine if bookmark is supported (New in 3 Jun 2003) BOOL b_hasBookMark = FALSE; CComPtr<IRowsetInfo> spRowsetInfo; HRESULT hr = p_rowset->QueryInterface(IID_IRowsetInfo, (void**)&spRowsetInfo); if (hr == S_OK) { CDBPropIDSet set(DBPROPSET_ROWSET); set.AddPropertyID(DBPROP_BOOKMARKS); DBPROPSET* pPropSet = NULL; ULONG ulPropSet = 0; hr = spRowsetInfo->GetProperties(1, &set, &ulPropSet, &pPropSet); if (hr == S_OK) { b_hasBookMark = (pPropSet->rgProperties[0].vValue.boolVal == VARIANT_TRUE); } } // Get the the IColumnsInfo from IRowset interface p_rowset->QueryInterface(&spColumns); // At this point, we may now release p_rowset p_rowset->Release(); // IColumnsInfo will give us the DBCOLUMNINFO structure DBCOLUMNINFO * p_columnInfo = NULL; LPOLESTR pstr_stringBuffer = NULL; ULONG ul_numColumns; // Now get the DBCOLUMNINFO data spColumns->GetColumnInfo(&ul_numColumns, &p_columnInfo, &pstr_stringBuffer); // Let's create a mapping for columName and the actual ordinal typedef std::map<<_bstr_t, long>> ColumnNameToOrdinal; ColumnNameToOrdinal columnOrdinals; // Using the DBCOLUMNINFO, fill the name-to-ordinal map USES_CONVERSION; TCHAR * psz_fieldName; for (ULONG i = 0; i < ul_numColumns; i++) { psz_fieldName = OLE2T(p_columnInfo[i].pwszName); if (p_columnInfo[i].pwszName != NULL) { // iOrdinal of 0 is reserved for book mark, if supported columnOrdinals[_bstr_t(psz_fieldName)] = p_columnInfo[i].iOrdinal- b_hasBookMark; } } // Clean up CoTaskMemFree(p_columnInfo); CoTaskMemFree(pstr_stringBuffer);
使用代码
文件 ADOXColumnOrdinal.h 包含静态函数,您可能希望直接在项目中使用它们,或者将它们剪切并粘贴到您的类中。您可以像下面这样将该文件包含在您的 stdafx.h 中(粗体项是必需的)
// I assume you have this somewhere #import "C:\Program Files\Common Files\System\ado\msado15.dll" \ rename( "EOF", "adoEOF" ) rename("DataTypeEnum", "adoDataTypeEnum") #import "C:\Program Files\Common Files\System\ado\msadox.dll" \ rename( "EOF", "adoEOF" ) no_namespace rename("DataTypeEnum", "adoDataTypeEnum") // For _bstr_t and _variant_t #include <comdef.h> // OLEDB #include "oledb.h" // And of course, our code #include "ColumnOrdinals.h"注意: 如果尚未设置,则需要设置 /GX 编译器选项以支持异常处理。
数据结构
以下映射定义分别在下一节所述的函数中使用。请参阅函数说明和有关如何使用这些映射的注释。
typedef std::map<long, long> ColumnOrdinalToIndex typedef std::map<_bstr_t, long> ColumnNameToOrdinal typedef std::map<long, _bstr_t> ColumnOrdinalToName
实用函数
提供的三个实用程序作为静态函数,以便您可以轻松地将它们改编到您的类或库中。
GetColumnOrdinalToIndexMap
static BOOL GetColumnOrdinalToIndexMap(_TablePtr p_table,
ColumnOrdinalToIndex & columnOrdinals)
此函数将获取 ADOX 字段索引到正确序数的映射。
参数
_TablePtr p_table
- ADOXtable
对象ColumnOrdinalToIndex & columnOrdinals
- 一个映射的引用,定义为std::map<long, long>
,它将接收 ADOX 索引到序数的映射。
Returns
BOOL
- 如果没有遇到错误,则为TRUE
,否则为FALSE
。
注释
ColumnOrdinalToIndex
映射(我最喜欢的:-))在访问第 n 个字段时,对于确定 ADO 索引(用于 GetItem
的索引)非常有用。例如,按正确的顺序顺序列表式地列出字段。
ColumnOrdinalToIndex ordinals; GetColumnOrdinalToIndexMap(p_tableFromSomewhere, ordinals); _ColumnPtr p_column = NULL; for (long i; i < p_tableFromSomewhere->Columns->Count; i++) { p_column = p_tableFromSomewhere->Columns->GetItem(columnMap[i]); ATLTRACE(_T("%d.) %s\n"), i, p_column->GetName()); }
GetColumnNameToOrdinalMap
static BOOL GetColumnNameToOrdinalMap(_TablePtr p_table,
ColumnNameToOrdinal & columnOrdinals)
此函数将获取字段名到正确序数的映射。
参数
_TablePtr p_table
- ADOXtable
对象ColumnNameToOrdinal & columnOrdinals
- 一个映射的引用,定义为std::map<_bstr_t, long>
,它将接收列名到序数的映射
Returns
BOOL
- 如果没有遇到错误,则为TRUE
,否则为FALSE
注释
如果您知道字段名并想获取序数,ColumnNameToOrdinal
映射就很有用。例如:
ColumnNameToOrdinal ordinals; GetColumnNameToOrdinalMap(p_tableFromSomewhere, ordinals); ATLTRACE(_T("The ordinal of %s is %d\n"), _T("myField"), ordinals[_T("myField")]);
GetColumnOrdinalToNameMap
static BOOL GetColumnOrdinalToNameMap(_TablePtr p_table,
ColumnOrdinalToName & columnOrdinals)
此函数将获取 ADOX 字段索引到正确序数的映射。
参数
_TablePtr p_table
- ADOXtable
对象ColumnOrdinalToName & columnOrdinals
- 一个映射的引用,定义为std::map<long, _bstr_t>
,它将接收序数到字段名的映射。
Returns
BOOL
- 如果没有遇到错误,则为TRUE
,否则为FALSE
注释
GetColumnOrdinalToNameMap
映射在获取第 n 个字段的名称时很有用。例如,按正确的顺序顺序列表式地列出字段。
ColumnOrdinalToIndex ordinals; GetColumnOrdinalToIndexMap(p_tableFromSomewhere, ordinals); for (long i; i < p_tableFromSomewhere->Columns->Count; i++) { ATLTRACE(_T("%d.) %s\n"), i, ordinals[i]); }
实践中
以上函数批量获取序数。每次都获取序数是不切实际的,例如,当您想显示一个字段时。这可能会极大地减慢您的应用程序速度。
正确的实现方式是将上述一个映射作为您文档、对话框或包装器类的成员。这样,您将只在您的对象创建时获取一次序数。
示例应用程序
示例项目是一个简单的 ATL 应用程序。您可以看到一些关于:
- 一起使用 ADO 和 ADOX
- 使用 ADOX
Catalog
、Table
和Column
对象 - 扫描 OLEDB 提供程序错误
- 子类化
ListView
控件的基本方法 - WTL 中的反射、通知处理
- 在 ATL/WTL 中使用 DDX
注释
- 基础 ATL 应用程序是使用 ATL 7 的 AppWizard 生成的。
- 经验教训: 编译发布模式时,我必须删除项目设置中定义的
_ATL_MIN_CRT
以解决链接错误:LNK2001 符号 '_main' 未找到
。STL 需要main()
入口点。 - 打开
/GX
选项以支持异常处理 - 如果您想用 ATL 版本 3 编译它,请在 ADOXColumnOrdinal.h 文件中注释掉带有
AtlInitCommonControls()
的行。
最后
最后,我想说——这里的代码是在我做晚饭的时候写的:-)。它可能不完美,但我希望它能帮助我说明我的观点。
历史
- 2003 年 6 月 3 日 - 动态检测行集是否支持书签
- 2002 年 12 月 5 日 - 初始发布