实现简单使用者
下面的主题显示如何编辑由“MFC 应用程序向导”和“ATL OLE DB 使用者向导”所创建的文件以创建简单的使用者。 此示例包括以下部分:
“用使用者检索数据”说明如何实现从数据库表中逐行读取所有数据的使用者中的代码。
“将书签支持添加到使用者”说明如何将书签支持添加到使用者。
“将 XML 支持添加到使用者”说明如何修改使用者代码以将检索到的行集合数据作为 XML 数据输出。
提示
可以使用本节中所描述的使用者应用程序来测试 MyProv 和 Provider 提供程序示例。
提示
若要生成使用者应用程序来测试 MyProv(增强简单的只读提供程序中所描述的同一提供程序),则必须包含书签支持(如“将书签支持添加到使用者”中所描述)。
提示
若要生成使用者应用程序来测试 Provider,则忽略书签支持(如“将书签支持添加到使用者”中所描述)并且跳到“将 XML 支持添加到使用者”。
用使用者检索数据
修改控制台应用程序以使用 OLE DB 使用者
在 MyCons.cpp 中,通过插入如下所示的粗体文本来更改主代码:
// MyCons.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "Products.h" ... int main(int argc, char* argv[]) { HRESULT hr = CoInitialize(NULL); // Instantiate rowset CProducts rs; hr = rs.OpenAll(); ATLASSERT( SUCCEEDED( hr ) ); hr = rs.MoveFirst(); // Iterate through the rowset while( SUCCEEDED(hr) && hr != DB_S_ENDOFROWSET ) { // Print out the column information for each row printf("Product ID: %d, Name: %s, Unit Price: %d, Quantity per Unit: %d, Units in Stock %d, Reorder Level %d\n", rs.m_ProductID, rs.m_ProductName, rs.m_UnitPrice, rs.m_QuantityPerUnit, rs.m_UnitsInStock, rs.m_ReorderLevel ); hr = rs.MoveNext(); } rs.Close(); rs.ReleaseCommand(); CoUninitialize(); return 0; }
将书签支持添加到使用者
书签是一个唯一标识表中的行的列。 它通常是键列,但不总是这样;它是提供程序特定的。 本节说明如何添加书签支持。 若要添加书签支持,需要在用户记录类中执行下面的操作:
实例化书签。 它们是 CBookmark 类型的对象。
通过设置 DBPROP_IRowsetLocate 属性从提供程序中请求书签列。
使用 BOOKMARK_ENTRY 宏向列映射添加一个书签项。
上面的步骤提供书签支持和要使用的书签对象。 此代码示例将演示书签,如下所示:
打开要编写的文件。
将行集合数据逐行输出到文件。
通过调用 MoveToBookmark 将行集合游标移动到书签。
输出由书签标记的行,将其追加到文件的结尾。
提示
如果使用此使用者应用程序来测试 Provider 提供程序应用程序示例,则忽略本节中所描述的书签支持。
实例化书签
访问器需要包含一个 CBookmark 类型的对象。 nSize 参数以字节为单位指定书签缓冲区的大小(通常,对于 32 位平台为 4,对于 64 位平台为 8)。 将下面的声明添加到用户记录类中的列数据成员:
////////////////////////////////////////////////////////////////////// // Products.h class CProductsAccessor { public: CBookmark<4> m_bookmark; // Add bookmark declaration LONG m_ProductID; ...
从提供程序中请求书签列
在用户记录类的 GetRowsetProperties 方法中添加下面的代码:
// Set the DBPROP_IRowsetLocate property. void GetRowsetProperties(CDBPropSet* pPropSet) { pPropSet->AddProperty(DBPROP_CANFETCHBACKWARDS, true, DBPROPOPTIONS_OPTIONAL); pPropSet->AddProperty(DBPROP_CANSCROLLBACKWARDS, true, DBPROPOPTIONS_OPTIONAL); // Add DBPROP_IRowsetLocate property to support bookmarks pPropSet->AddProperty(DBPROP_IRowsetLocate, true); }
将书签项添加到列映射
将下面的项添加到用户记录类中的列映射:
// Set a bookmark entry in the column map. BEGIN_COLUMN_MAP(CProductsAccessor) BOOKMARK_ENTRY(m_bookmark) // Add bookmark entry COLUMN_ENTRY_LENGTH_STATUS(1, m_ProductID, m_dwProductIDLength, m_dwProductIDStatus) COLUMN_ENTRY_LENGTH_STATUS(2, m_ProductName, m_dwProductNameLength, m_dwProductNameStatus) ... END_COLUMN_MAP()
在主代码中使用书签
在以前创建的控制台应用程序的 MyCons.cpp 文件中,将主代码更改为如下所示的样子。 若要使用书签,则主代码需要实例化自己的书签对象 (myBookmark);此书签不同于访问器中的书签 (m_bookmark)。
/////////////////////////////////////////////////////////////////////// // MyCons.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "Products.h" #include <iostream> #include <fstream> using namespace std; int _tmain(int argc, _TCHAR* argv[]) { HRESULT hr = CoInitialize(NULL); // Instantiate rowset CProducts rs; hr = rs.OpenAll(); hr = rs.MoveFirst(); // Cast CURRENCY m_UnitPrice to a long value LONGLONG lPrice = rs.m_UnitPrice.int64; // Open file output.txt for writing in overwrite mode ofstream outfile( "C:\\output.txt", ios::out ); if (!outfile) // Test for invalid file return -1; // Instantiate a bookmark object myBookmark for the main code CBookmark<4> myBookmark; int nCounter = 0; // Iterate through the rowset and output column data to output.txt row by row // In the file, mark the beginning of this set of data: outfile << "initial row dump" << endl; while( SUCCEEDED(hr) && hr != DB_S_ENDOFROWSET ) { nCounter++; if( nCounter == 5 ) myBookmark = rs.bookmark; // Output the column information for each row: outfile << rs.m_ProductID << rs.m_ProductName << lPrice << rs.m_QuantityPerUnit << rs.m_UnitsInStock << rs.m_ReorderLevel << endl; hr = rs.MoveNext(); } // Move cursor to bookmark hr = rs.MoveToBookmark(myBookmark); // Iterate through the rowset and output column data to output.txt row by row // In the file, mark the beginning of this set of data: outfile << "row dump starting from bookmarked row" << endl; while( SUCCEEDED(hr) && hr != DB_S_ENDOFROWSET ) { // Output the column information for each row outfile << rs.m_ProductID << rs.m_ProductName << lPrice << rs.m_QuantityPerUnit << rs.m_UnitsInStock << rs.m_ReorderLevel << endl; hr = rs.MoveNext(); } rs.CloseAll(); CoUninitialize(); return 0; }
有关书签的更多信息,请参见使用书签。 在更新行集合中也显示书签的示例。
将 XML 支持添加到使用者
如访问 XML 数据中所讨论的,有两种从数据源中检索 XML 数据的方法:使用 CStreamRowset 或使用 CXMLAccessor。 此示例使用 CStreamRowset,它更有效,但是要求在执行此应用程序示例的计算机上运行 SQL Server 2000。
修改命令类以从 CStreamRowset 继承
在以前创建的使用者应用程序中,更改 CCommand 声明以将 CStreamRowset 指定为行集合类,如下所示:
class CProducts : public CCommand<CAccessor<CProductsAccessor>, CStreamRowset >
修改主代码以检索并输出 XML 数据
在以前创建的控制台应用程序的 MyCons.cpp 文件中,将主代码更改为如下所示的样子:
/////////////////////////////////////////////////////////////////////// // MyCons.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "Products.h" #include <iostream> #include <fstream> using namespace std; int _tmain(int argc, _TCHAR* argv[]) { HRESULT hr = CoInitialize(NULL); // Instantiate rowset CProducts rs; // Add variable declarations for the Read method to handle sequential stream data CHAR buffer[1001]; // Pointer to buffer into which data stream is read ULONG cbRead; // Actual number of bytes read from the data stream hr = rs.OpenAll(); // Open file output.txt for writing in overwrite mode ofstream outfile( "C:\\output.txt", ios::out ); if (!outfile) // Test for invalid file return -1; // The following loop reads 1000 bytes of the data stream at a time // until it reaches the end of the data stream for (;;) { // Read sequential stream data into buffer HRESULT hr = rs.m_spStream->Read(buffer, 1000, &cbRead); if (FAILED (hr)) break; // Output buffer to file buffer[cbRead] = 0; outfile << buffer; // Test for end of data stream if (cbRead < 1000) break; } rs.CloseAll(); CoUninitialize(); return 0; }