65.9K
CodeProject 正在变化。 阅读更多。
Home

修正 ADOX Table 对象的列序号

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.17/5 (6投票s)

2002年12月5日

4分钟阅读

viewsIcon

97954

downloadIcon

1437

获取 ADOX 表对象的正确列序数。

Sample Image - ADOXColumnOrdinal.gif

引言

ADOX Table 对象允许我们通过 Columns 集合轻松检索每个表的列。然而,该集合是按列/字段名排序的。在我的一个项目中,我需要支持以下功能:

  1. 以 MS Access 显示的相同顺序表示 columns 集合
  2. 能够在集合的任何位置插入新列(另一篇文章将讨论这一点)

仅使用 ADOX Columns 集合是不够的。因此,需要获取实际的列序数。

方法论

有两种方法可以获取列序数:

  • 通过遍历 ADO OpenSchema(adSchemaColumns,...) 返回的 rowset - 速度较慢
  • 从 OLEDB IColumnsInfo 接口获取 DBCOLUMNINFO 数据 - 速度更快

本文讨论了如何从 IColumnsInfo OLEDB 接口获取列序数。

IColumnsInfo 可从(由...实现)commandrowset OLE DB 对象获得。我提出的方法基本由三个步骤组成:

  1. 从 ADOX Table 创建 ADO Recordset
  2. 将 ADO Recordset 转换为 OLEDB IRowset
  3. 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 - ADOX table 对象
  • 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 - ADOX table 对象
  • 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 - ADOX table 对象
  • 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 CatalogTableColumn 对象
  • 扫描 OLEDB 提供程序错误
  • 子类化 ListView 控件的基本方法
  • WTL 中的反射、通知处理
  • 在 ATL/WTL 中使用 DDX

注释

  1. 基础 ATL 应用程序是使用 ATL 7 的 AppWizard 生成的。
  2. 经验教训: 编译发布模式时,我必须删除项目设置中定义的 _ATL_MIN_CRT 以解决 链接错误:LNK2001 符号 '_main' 未找到。STL 需要 main() 入口点。
  3. 打开 /GX 选项以支持异常处理
  4. 如果您想用 ATL 版本 3 编译它,请在 ADOXColumnOrdinal.h 文件中注释掉带有 AtlInitCommonControls() 的行。

最后

最后,我想说——这里的代码是在我做晚饭的时候写的:-)。它可能不完美,但我希望它能帮助我说明我的观点。

历史

  • 2003 年 6 月 3 日 - 动态检测行集是否支持书签
  • 2002 年 12 月 5 日 - 初始发布
© . All rights reserved.