Programmazione di OleDB Consumer in ATL

Tramite ATL ( Active Template Library ) e l’uso di attributi, è possibile creare dei consumer OleDB in pochissimo tempo e soprattutto che consentono di collegarsi a varie base dati. In questo nostro esempio lavoreremo su un provider per SQL Server, ma lo stesso principio può essere valido per qualsiasi altro tipo di archivio, basta solo aver registrato nel proprio sistema il client OleDB per la connessione.
Per iniziare vedremo come inserire dei valori all’interno di una tabella, che chiameremo “ClientiCorso” e che contiene i campi: “IDCliente” di tipo intero contatore, “Codice” di tipo testo con lunghezza di 20 caratteri, “Nome” di tipo testo con lunghezza di 150 caratteri e “Cognome” di tipo testo con lunghezza di 150 caratteri. Per questo esempio dobbiamo creare un nuovo progetto C/C++ in Visual Studio, con supporto ATL ed inserendo nel preprocessore la direttiva _ATL_ATTRIBUTES. Occorre creare un file in cui inserire la dichiarazione della classe consumer ATL per gestire la tabella “ClientiCorso”, nel mio caso il file è stato chiamato “ClientiSet.h”:

#pragma once

[
   db_command(L"SELECT * FROM ClientiCorso WHERE IDCliente = ?")
]
class CClientiSet
{
public:
   [ db_accessor(0, true) ];
   [ db_column(ordinal=L"IDCliente", status=L"m_dwIDClienteStatus") ]
               long m_IDCliente;
   [ db_accessor(1, true) ];
   [ db_column(ordinal=L"Codice", status=L"m_dwCodiceStatus",
               length=L"m_dwCodiceLength") ] char m_Codice[21];
   [ db_column(ordinal=L"Nome", status=L"m_dwNomeStatus",
               length=L"m_dwNomeLength") ] char m_Nome[151];
   [ db_column(ordinal=L"Cognome", status=L"m_dwCognomeStatus",
               length=L"m_dwCognomeLength") ] char m_Cognome[150];

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

   DBSTATUS m_dwIDClienteStatus;
   DBSTATUS m_dwCodiceStatus;
   DBSTATUS m_dwNomeStatus;
   DBSTATUS m_dwCognomeStatus;

   DBLENGTH m_dwCodiceLength;
   DBLENGTH m_dwNomeLength;
   DBLENGTH m_dwCognomeLength;

   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 questo caso abbiamo creato due accessor per distinguere il valore “IDCliente” che non occorre inserire perché di autoincremento ed un parametro che consentirà di poter scegliere il cliente in base al suo numero identificativo. Le proprietà del rowset sono di inserimento, di aggiornamento e di cancellazione ed altre di secondaria importanza che riguardano il movimento in avanti ed indietro del cursore e la gestione delle transazioni.
Ecco un esempio di codice di un programma console che consente di collegarsi alla base dati ed inserire un cliente:

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

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

   HRESULT hr = CoInitialize(NULL);
   if (hr != S_OK)
   {
      cout << "Non è stato possibile inizializzare COM!" << endl;
      return -1;
   }

   hr = data.Open();
   if (hr != S_OK)
   {
      cout << "Non è stato possibile collegarsi al server!" << endl;
      return -1;
   }

   hr = session.Open(data);
   if (hr != S_OK)
   {
      data.Close();
      cout << "Non è stato possibile aprire una sessione!" << endl;
      return -1;
   }

   CClientiSet cSet;
   cSet.m_paramIDCliente = 0;
   hr = cSet.OpenRowset(session);
   if (hr != S_OK)
   {
      session.Close();
      data.Close();
      cout << "Non è stato possibile aprire la \
           tabella dei clienti!" << endl;
      return -1;
   }

   char sCodice[21];
   char sNome[151];
   char sCognome[151];

   cout << "Inserire il codice del cliente: ";    cin >> sCodice;
   cout << "Inserire il nome del cliente: ";    cin >> sNome;
   cout << "Inserire il cognome del cliente: ";    cin >> sCognome;

   cSet.m_dwCodiceStatus = DBSTATUS_S_OK;
   cSet.m_dwCodiceLength = strlen(sCodice);
   strcpy_s(cSet.m_Codice, sCodice);
   cSet.m_dwNomeStatus = DBSTATUS_S_OK;
   cSet.m_dwNomeLength = strlen(sNome);
   strcpy_s(cSet.m_Nome, sNome);
   cSet.m_dwCognomeStatus = DBSTATUS_S_OK;
   cSet.m_dwCognomeLength = strlen(sCognome);
   strcpy_s(cSet.m_Cognome, sCognome);

   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 << "Non è stato possibile inserire il valore \
            del nuovo cliente!" << endl;
      return -1;
   }

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

   cout << "Il cliente " << sCodice << " - " << sNome << " "
        << sCognome << " è stato inserito con successo!" << endl;

   CoUninitialize();

   return 0;
}

La prima operazione da compirere è caricare il gestore di COM, poi si apre il datasource, si collega alla session per una possibile gestione delle transazioni ( se il provider ne prevede l’uso ) ed alla fine apriamo il rowset per inserire il nuovo valore, al termine chiudiamo il tutto e rilasciamo le risorse. Se volessimo visualizzare la lista dei clienti inseriti dovremmo scrivere:

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

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

   HRESULT hr = CoInitialize(NULL);
   if (hr != S_OK)
   {
      cout << "Non è stato possibile inizializzare COM!" << endl;
      return -1;
   }

   hr = data.Open();
   if (hr != S_OK)
   {
      cout << "Non è stato possibile collegarsi al server!" << endl;
      return -1;
   }

   hr = session.Open(data);
   if (hr != S_OK)
   {
      data.Close();
      cout << "Non è stato possibile aprire una sessione!" << endl;
          return -1;
   }

   CClientiSet cSet;
   TCHAR strSQL[100] = _T("SELECT * FROM ClientiCorso \
         WHERE IDCliente > ?");

   cSet.m_paramIDCliente = 0;
   hr = cSet.OpenRowset(session, strSQL);
   if (hr != S_OK)
   {
      session.Close();
      data.Close();
      cout << "Non è stato possibile aprire la tabella \
            dei clienti!" << endl;
      return -1;
   }

   hr = cSet.MoveFirst();
   while (hr == S_OK)
   {
      cout << cSet.m_Codice;
      cout << cSet.m_Nome;
      cout << cSet.m_Cognome << endl;

      hr = cSet.MoveNext();
   }

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

   CoUninitialize();

   return 0;
}

Come potete vedere è molto semplice recuperare i dati, penso che il codice proposto non abbia bisogno di ulteriori commenti, vista la semplicità delle operazioni svolte. Il fattore positivo di OleDB è che consente di gestire i dati presenti in qualsiasi base dati, unico requisito avere il provider giusto; sarebbe anche possibile utilizzare un provider ODBC, ma personalmente non amo molto questo tipo di connessioni perché il provider per SQL Server è specializzato per questo tipo di archivio ed è certamente più ottimizzato di un vecchio provider ODBC. Se a qualcuno interessa approfondire l’uso della libreria ATL anche per creare dei provider OleDB, come ad esempio uno specifico per MySQL, può richiederlo o nei commenti o nel forum.

Informazioni su Giampaolo Rossi

Sviluppatore di software gestionale da oltre 28 anni.
Questa voce è stata pubblicata in Database, Programmazione, VC/C++ e contrassegnata con . Contrassegna il permalink.