Basic OLE DB Template Architecture

Now that you understand the basic architecture behind OLE DB, it's time to take a look at a specific implementation of the OLE DB interfaces (provided by the new OLE DB consumer and provider templates). Like most other COM-based technologies, OLE DB involves implementing a bunch of interfaces. Of course, just as with ActiveX Controls, you can choose to implement them by hand (often an inefficient approach—unless you're just trying to understand the technology inside-out), or you can find someone else to do most of the dirty work. While OLE DB is a rich and powerful data access technology, getting it up and running by hand is a somewhat tedious task.

Just as Visual C++ provides a template library (ATL) for implementing ActiveX Controls, Visual C++ also provides a template library that helps you manage OLE DB. The OLE DB template support provides classes that implement many of the commonly used OLE DB interfaces. In addition, Visual C++ provides great wizard support for generating code to apply to common scenarios.

From a high level, you can divide the classes in this template library into the two groups defined by OLE DB itself: the consumer classes and the provider classes. The consumer classes help you implement database client (consumer) applications, while the provider classes help you implement database server (provider) applications. Remember that OLE DB consumers are applications that call the COM interfaces exposed by OLE DB service providers (or regular providers) to access data. OLE DB providers are COM servers that provide data and services in a form that a consumer can understand.

OLE DB Consumer Template Architecture

Microsoft has kept the top layer classes in the OLE DB Consumer Templates as close to the OLE DB specification as possible. That is, OLE DB templates don't define another object model. Their purpose is simply to wrap the existing OLE DB object model. For each of the consumer-related components listed, you'll find a corresponding C++ template class. This design philosophy leverages the flexibility of OLE DB and allows more advanced features—such as multiple accessors on rowsets—to be available through the OLE DB Templates.

The OLE DB Templates are small and flexible. They are implemented using C++ templates and multiple inheritance. Because OLE DB templates are close to the metal (they wrap only the existing OLE DB architecture), each class mirrors an existing OLE DB component. For example, CDataSource corresponds to the data source object in OLE DB.

The OLE DB Consumer Template architecture can be divided into three parts: the general data source support classes, classes for supporting data access and rowset operations, and classes for handling tables and commands. Here's a quick summary of these classes.

General Data Source Support

A data source is the most fundamental concept to remember when talking about data access using OLE DB. That is, where is the data coming from? Of course, the OLE DB templates have support for data sources. General data source support comprises three classes as shown in this table.
ClassUse
CDataSourceThis class represents the data source component and manages the connection to a data source.
CEnumeratorThis class provides a way to select a provider by cycling through a list of providers. Its functionality is equivalent to the SQLBrowseConnect and SQLDriverConnect functions.
CSessionThis class handles transactions. You can use this class to create rowsets, commands, and many other objects. A CDataSource object creates a CSession object using the CSession::Open method.

Data Access and Rowset Support

The OLE DB templates provide binding and rowset support through several classes. The accessor classes talk to the data source while the rowset manages the data in tabular form. The data access and rowset components are implemented through the CAccessorRowset class. CAccessorRowset is a template class that's specialized on an accessor and a rowset. This class can handle multiple accessors of different types.

The OLE DB Template library defines the accessors in this table.

ClassUse
CAccessorThis class is used when a record is statically bound to a data source—it contains the pre-existing data buffer and understands the data format up front. CAccessor is used when you know the structure and the type of the database ahead of time.
CDynamicAccessorThis class is used for retrieving data from a source whose structure is not known at design time. This class uses IColumnsInfo::GetColumnInfo to get the database column information. CDynamicAccessor creates and manages the data buffer.
CDynamicParameterAccessorThis class is similar to CDynamicAccessor except that it's used with commands. When used to prepare commands, CDynamicParameterAccessor can get parameter information from the ICommandWithParameters interface, which is especially useful for handling unknown command types.
CManualAccessorThis class lets you access whatever data types you want as long as the provider can convert the type. CManualAccessor handles both result columns and command parameters.

Along with the accessors, the OLE DB templates define three types of rowsets: single fetching, bulk, and array. These are fairly self-explanatory descriptions. Clients use a function named MoveNext to navigate through the data. The difference between the single fetching, bulk, and array rowsets lies in the number of row handles retrieved when MoveNext is called. Single fetching rowsets retrieve a single rowset for each call to MoveNext while bulk rowsets fetch multiple rows. Array rowsets provide a convenient array syntax for fetching data. The OLE DB Templates provide the single row-fetching capability by default.

Table and Command Support

The final layer in the OLE DB Template consumer architecture consists of two more classes: table and command classes (CTable and CCommand). These classes are used to open the rowset, execute commands, and initiate bindings. Both classes derive from CAccessorRowset

The CTable class is a minimal class implementation that opens a table on a data source (which you can specify programmatically). Use this class when you need bare-bones access to a source, since CTable is designed for simple providers that do not support commands.

Other data sources also support commands. For those sources, you'll want to use the OLE DB Templates' CCommand class. As its name implies, CCommand is used mostly for executing commands. This class has a function named Open that executes singular commands. This class also has a function named Prepare for setting up a command to execute multiple times.

When using the CCommand class, you'll specialize it with three template arguments: an accessor, a rowset, and a third template argument (which defaults to CNoMultipleResults). If you specify CMultipleResults for this third argu- ment, the CCommand class will support the IMultipleResults interface for a command that returns multiple rowsets.

OLE DB Provider Template Architecture

Remember that OLE DB is really just a set of interfaces that specify a protocol for managing data. OLE DB defines several interfaces (some mandatory and others optional) for the following types of objects: data source, session, rowset, and command. Here's a description of each followed by a code snippet that shows how the templates bring in the correct functionality for each component.

InterfaceRequired?Implemented?
IDBInitialize MandatoryYes
IDBCreateSession MandatoryYes
IDBProperties MandatoryYes
IPersist MandatoryYes
IDBDataSourceAdmin OptionalNo
IDBInfo OptionalNo
IPersistFile OptionalNo
ISupportErrorInfoOptionalNo

Table 33-1. Data source object interface requirements.

Tables in this section were compiled from the Microsoft Visual Studio MSDN Online Help.

InterfaceRequired?Implemented?
IAccessorMandatoryYes
IColumnsInfoMandatoryYes
ICommandMandatoryYes
ICommandPropertiesMandatoryYes
ICommandTextMandatoryYes
IConvertTypeMandatoryYes
IColumnsRowsetOptionalNo
ICommandPrepareOptionalNo
ICommandWithParametersOptionalNo
ISupportErrorInfoOptionalNo

Table 33-2. Command object interfaces requirements.

InterfaceRequired?Implemented?
IGetDataSourceMandatoryYes
IOpenRowsetMandatoryYes
ISessionPropertiesMandatoryYes
IDBCreateCommandOptionalYes
IDBSchemaRowsetOptionalYes
IIndexDefinitionOptionalNo
ISupportErrorInfoOptionalNo
ITableDefinitionOptionalNo
ITransactionJoinOptionalNo
ITransactionLocalOptionalNo
ITransactionObjectOptionalNo

Table 33-3. Session object interfaces requirements.

InterfaceRequired?Implemented?
IAccessorMandatoryYes
IColumnsInfoMandatoryYes
IConvertTypeMandatoryYes
IRowsetMandatoryYes
IRowsetInfoMandatoryYes
IColumnsRowsetOptionalNo
IConnectionPointContainerOptionalYes, through ATL
IRowsetChangeOptionalNo
IRowsetIdentityRequired for Level 0 Yes
IRowsetLocateOptionalNo
IRowsetResynchOptionalNo
IRowsetScrollOptionalNo
IRowsetUpdateOptionalNo
ISupportErrorInfoOptionalNo

Table 33-4. Rowset object interfaces requirements.

How the Provider Parts Work Together

The use for the first part of the architecture—the data source—should be obvious. Every provider must include a data source object. When a consumer application needs data, the consumer calls CoCreateInstance to create the data source object and start the provider. Within the provider, it's the data source object's job to create a session object using the IDBCreateSession interface. The consumer uses this interface to connect to the data source object. In comparing this to how ODBC works, the data source object is equivalent to ODBC's HENV and the session object is the equivalent of ODBC's HDBC.

The command object does most of the work. To make the data provider actually do something, you'll modify the command class's Execute function.

Like most COM-based protocols, the OLE DB protocol makes sense once you've examined it for a little while. Also, like most COM-based protocols, the OLE DB protocol involves a good amount of code to get going—code that could be easily implemented by some sort of framework. That's what the Data Consumer and Data Provider templates are all about. The rest of the chapter shows you what you need to do to create Data Consumers and Data Providers.