Programming OleDB Consumer in ATL

Using ATL (Active Template Library) and the use of attributes, you can create the OleDB consumer in a very short time and above that allow you to connect to various databases. In our example we will work on a provider for SQL Server, but the same principle can be valid for any type of archive, you just have registered in your system for the client OleDB connection.
To start we will see how to enter values​within a table, called “Customers” and contains the fields “CustomerID” an integer counter, “Code” text type with a length of 20 characters, “Name” type text with a length of 150 characters and “Last Name” text type with a length of 150 characters. For this example we create a new project C/C++ in Visual Studio with support ATL and inserting in preprocessor directive _ATL_ATTRIBUTES. It should create a file to contain the declaration of the consumer class ATL table manipulator “Customers”, in my case the file was called “CustSet.h”

#pragma once

[
   db_command(L"SELECT * FROM Customers WHERE IDCustomer = ?")
]
class CCustomersSet
{
public:
   [ db_accessor(0, true) ];
   [ db_column(ordinal=L"IDCostumer",
               status=L"m_dwIDCustomerStatus") ]
               long m_IDCustomer;
   [ db_accessor(1, true) ];
   [ db_column(ordinal=L"Code", status=L"m_dwCodeStatus",
               length=L"m_dwCodeLength") ] char m_Code[21];
   [ db_column(ordinal=L"Name", status=L"m_dwNameStatus",
               length=L"m_dwNameLength") ] char m_Name[151];
   [ db_column(ordinal=L"Surname", status=L"m_dwSurnameStatus",
               length=L"m_dwSurnameLength") ] char m_Surname[150];

   [ db_param(ordinal=L"1", paramtype=L"DBPARAMIO_INPUT") ]
               long m_paramIDCustomer;

   DBSTATUS m_dwIDCustomerStatus;
   DBSTATUS m_dwCodeStatus;
   DBSTATUS m_dwNameStatus;
   DBSTATUS m_dwSurnameStatus;

   DBLENGTH m_dwCodeLength;
   DBLENGTH m_dwNameLength;
   DBLENGTH m_dwSurnameLength;

   void GetRowsetProperties(CDBPropSet* pPropSet)
   {
       pPropSet->AddProperty(DBPROP_CANFETCHBACKWARDS,
               true, DBPROPOPTIONS_OPTIONAL);
       pPropSet->AddProperty(DBPROP_SERVERDATAONINSERT,
               true, DBPROPOPTIONS_OPTIONAL);
       pPropSet->AddProperty(DBPROP_CANSCROLLBACKWARDS,
               true, DBPROPOPTIONS_OPTIONAL);
       pPropSet->AddProperty(DBPROP_ABORTPRESERVE,
               true, DBPROPOPTIONS_OPTIONAL);
       pPropSet->AddProperty(DBPROP_COMMITPRESERVE,
               true, DBPROPOPTIONS_OPTIONAL);
       pPropSet->AddProperty(DBPROP_IRowsetChange,
               true, DBPROPOPTIONS_OPTIONAL);
       pPropSet->AddProperty(DBPROP_UPDATABILITY,
               DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_INSERT
               | DBPROPVAL_UP_DELETE);
   }
};

In this case we have created two accessor to distinguish the value “CustomerID” that need not be given because of the increment and a parameter that allows the customer to choose according to his number. The properties of the rowset are input, update and removal and other minor concern that the forward and backward movement of the cursor and transaction management.
Here is a sample code of a console program that allows you to connect to the database and enter a customer:

#include <iostream>
#include <atldbcli.h>
#include "CustSet.h"
using namespace std;

int main(int argc, char** argv)
{
   CDataSource data;
   CSession session;

   HRESULT hr = CoInitialize(NULL);
   if (hr != S_OK)
   {
      cout << "Can not initialize COM!" << endl;
      return -1;
   }

   hr = data.Open();
   if (hr != S_OK)
   {
      cout << "Can't connect to server!" << endl;
      return -1;
   }

   hr = session.Open(data);
   if (hr != S_OK)
   {
      data.Close();
      cout << "Can not open session!" << endl;
      return -1;
   }

   CCustomersSet cSet;
   cSet.m_paramIDCliente = 0;
   hr = cSet.OpenRowset(session);
   if (hr != S_OK)
   {
      session.Close();
      data.Close();
      cout << "Can not open customers table!" << endl;
      return -1;
   }

   char sCode[21];
   char sName[151];
   char sSurname[151];

   cout << "Insert customer code: ";    cin >> sCode;
   cout << "Insert customer name: ";    cin >> sName;
   cout << "insert customer surname: ";    cin >> sSurname;

   cSet.m_dwCodeStatus = DBSTATUS_S_OK;
   cSet.m_dwCodeLength = strlen(sCode);
   strcpy_s(cSet.m_Code, sCode);
   cSet.m_dwNameStatus = DBSTATUS_S_OK;
   cSet.m_dwNameLength = strlen(sName);
   strcpy_s(cSet.m_Name, sName);
   cSet.m_dwSurnameStatus = DBSTATUS_S_OK;
   cSet.m_dwSurnameLength = strlen(sSurname);
   strcpy_s(cSet.m_Surname, sSurname);

   hr = cSet.Insert(1, true);
   if (hr == S_OK)
     hr = cSet.GetData(0);
   if (hr != S_OK)
   {
      cSet.Close();
      cSet.ReleaseCommand();
      session.Close();
      data.Close();
      cout << "Can not insert new customer!" << endl;
      return -1;
   }

   cSet.Close();
   cSet.ReleaseCommand();
   session.Close();
   data.Close();

   cout << "Customer " << sCode << " - " << sName << " "
        << sSurname << " inserted!" << endl;

   CoUninitialize();

   return 0;
}

The first step is to load the handler will perform COM, then you open the datasource, is connected to the session for a possible transaction management (if the provider specifies that its’ use) and the open end of the rowset to enter the new value, after we close and release all resources. If we wanted to show list of clients included should write:

#include <iostream>
#include <atldbcli.h&gt;
#include "CustSet.h"
using namespace std;

int main(int argc, char** argv)
{
   CDataSource data;
   CSession session;

   HRESULT hr = CoInitialize(NULL);
   if (hr != S_OK)
   {
      cout << "Can not initialize COM!" << endl;
      return -1;
   }

   hr = data.Open();
   if (hr != S_OK)
   {
      cout << "Can not connect to server!" << endl;
      return -1;
   }

   hr = session.Open(data);
   if (hr != S_OK)
   {
      data.Close();
      cout << "Can't open session!" << endl;
          return -1;
   }

   CCustomersSet cSet;
   TCHAR strSQL[100] = _T("SELECT * FROM Customers \
         WHERE IDCustomer > ?");

   cSet.m_paramIDCustomer = 0;
   hr = cSet.OpenRowset(session, strSQL);
   if (hr != S_OK)
   {
      session.Close();
      data.Close();
      cout << "Can't open customers table!" << endl;
      return -1;
   }

   hr = cSet.MoveFirst();
   while (hr == S_OK)
   {
      cout << cSet.m_Code;
      cout << cSet.m_Name;
      cout << cSet.m_Surname << endl;

      hr = cSet.MoveNext();
   }

   cSet.Close();
   cSet.ReleaseCommand();
   session.Close();
   data.Close();

   CoUninitialize();

   return 0;
}

As you can see it is very easy to retrieve data from database, I think that the proposed code does not need further comment, given the simplicity of the task performed. The positive factor is the OleDB that allows you to manage data in any database, the only requirement to have the right provider, would also be possible to use an ODBC provider, but personally I love this type of connection because the provider is specialized for SQL Server for this type of archive and is certainly the most optimized of an old ODBC.

This entry was posted in Programming. Bookmark the permalink.