Attributed OLE DB Programming
Just as you can write ActiveX controls using attributed ATL, you can also write OLE DB templates using attributes. Six attributes apply to OLE DB consumer template programming. These are described in Table 27-5.
Attribute | Description |
---|---|
db_accessor | Binds columns in a rowset and binds them to the corresponding accessor maps |
db_column | Binds a specified column to the rowset |
db_command | Executes an OLE DB command |
db_param | Associates the specified member variable with an input or output parameter |
db_source | Creates and encapsulates a connection, through a provider, to a data source |
db_table | Opens an OLE DB table |
Database development is another type of programming that involves a great deal of boilerplate code, so it's another great candidate for attributed programming. Remember that with attributed programming, you declare program features using attributes, and the compiler and linker inject code into your project. For example, here's an OLE DB consumer template for using the Titles table in the Biblio.mdb database:
// Titles.h : Declaration of the CTitles
#pragma once
[
db_source(![]()
),
db_table(L"Titles")
]
class CTitles
{
public:
// This table/command contains column(s) that can be accessed
// via an ISequentialStream interface. Not all providers, however,
// support this feature, and even those that do support it, are
// often limited to just one ISequentialStream per rowset.
// If you want to use streams in this accessor, use the sample
// line(s) of code below, and set the DBPROP_ISequentialStream
// rowset property to true. You can than use the Read() method
// to read the data. For more information on
// ISequentialStream binding see the documentation
// [ db_column(8, status=m_dwCommentsStatus,
// length=m_dwCommentsLength) ] ISequentialStream* m_Comments;
[ db_column(8, status=m_dwCommentsStatus,
length=m_dwCommentsLength) ] TCHAR m_Comments[8000];
[ db_column(5, status=m_dwDescriptionStatus,
length=m_dwDescriptionLength) ] TCHAR m_Description[51];
[ db_column(3, status=m_dwISBNStatus,
length=m_dwISBNLength) ] TCHAR m_ISBN[21];
[ db_column(6, status=m_dwNotesStatus,
length=m_dwNotesLength) ] TCHAR m_Notes[51];
[ db_column(4, status=m_dwPubIDStatus,
length=m_dwPubIDLength) ] LONG m_PubID;
[ db_column(7, status=m_dwSubjectStatus,
length=m_dwSubjectLength) ] TCHAR m_Subject[51];
[ db_column(1, status=m_dwTitleStatus,
length=m_dwTitleLength) ] TCHAR m_Title[256];
[ db_column(2, status=m_dwYearPublishedStatus,
length=m_dwYearPublishedLength) ] SHORT m_YearPublished;
// The following wizard-generated data members contain status
// values for the corresponding fields. You
// can use these values to hold NULL values that the database
// returns or to hold error information when the compiler returns
// errors. See Field Status Data Members in Wizard-Generated
// Accessors in the Visual C++ documentation for more information
// on using these fields.
// NOTE: You must initialize these fields before
// setting/inserting data!
DBSTATUS m_dwCommentsStatus;
DBSTATUS m_dwDescriptionStatus;
DBSTATUS m_dwISBNStatus;
DBSTATUS m_dwNotesStatus;
DBSTATUS m_dwPubIDStatus;
DBSTATUS m_dwSubjectStatus;
DBSTATUS m_dwTitleStatus;
DBSTATUS m_dwYearPublishedStatus;
// The following wizard-generated data members contain length
// values for the corresponding fields.
// NOTE: For variable-length columns, you must initialize these
// fields before setting/inserting data!
DBLENGTH m_dwCommentsLength;
DBLENGTH m_dwDescriptionLength;
DBLENGTH m_dwISBNLength;
DBLENGTH m_dwNotesLength;
DBLENGTH m_dwPubIDLength;
DBLENGTH m_dwSubjectLength;
DBLENGTH m_dwTitleLength;
DBLENGTH m_dwYearPublishedLength;
void GetRowsetProperties(CDBPropSet* pPropSet)
{
pPropSet->AddProperty(DBPROP_CANFETCHBACKWARDS,
true, DBPROPOPTIONS_OPTIONAL);
pPropSet->AddProperty(DBPROP_CANSCROLLBACKWARDS,
true, DBPROPOPTIONS_OPTIONAL);
// pPropSet->AddProperty(DBPROP_ISequentialStream, true);
pPropSet->AddProperty(DBPROP_IRowsetChange,
true, DBPROPOPTIONS_OPTIONAL);
pPropSet->AddProperty(DBPROP_UPDATABILITY,
DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_INSERT
| DBPROPVAL_UP_DELETE);
}
};
The code is a C++ class representing the Titles table in the Biblio database. Using attributes shortens the code somewhat. Notice that the COLUMN_MAP that is present in the classic ATL OLE DB consumer template is missing; it's replaced by member variables of the CTitles class, preceded by the db_column attribute. Also notice the absence of a CTitlesAccessor class. (In the classic OLE DB consumer template, the CAuthorsAccessor class and the CAuthors class were separate entities.) The accessor information and the CAuthors class are wrapped up into one class: the CTitles class. Also notice that the database connection information is included as a set of attributes preceding the entire CTitles class. (In the classic OLE DB consumer example with the Authors table shown earlier, the database connection information was hard-coded into the OpenDataSource method.)Using the attributed CTitles class is similar to using the CAuthors class. Here's how to use the attributed database consumer class:
Declare an instance of CTitles wherever you need to use it:class CUseTitles : public CDialog {
CTitles m_titles;
};
Open the database by calling Open on the database consumer object:CUseTitles::OnInitDialog() {
m_titles.Open();
}
Use member functions to navigate through and manipulate the database. Here's a sampling of some of the things you can do:CUseTitles::OnNext() {
m_titles.MoveNext();
}
CUseTitles::OnFirst() {
m_titles.MoveFirst();
}
CUseTitles::OnLast() {
m_titles.MoveLast();
}
CUseTitles::OnInsert() {
m_titles.Insert();
}
As you navigate through the database, the data will end up in the member variables. For example, if you want to find out the name of the next title in the table, the code will look like this:m_titles.MoveNext();
m_strTitle = m_titles.m_Title;
Using attributed OLE DB consumer templates greatly reduces the programming area you work with when you access OLE DB data sources.