Accesso ai dati con DAO e MFC


Home Page | Commenti | Articoli | Faq | Documenti | Ricerca | Download | Contribuire
A cura di Davide Bianchi Accedere ai dati direttamente con le MFC ed il DAO.
Tanti oggetti, cosi' poco tempo... L'accesso e la manipolazione dei database sono una delle attivita' "normali" per quasi qualunque programma, non conta tanto cosa il programma fa', in un modo o nell'altro una delle sue attivita' sara' quella di accedere a dei file per scrivere o leggere delle informazioni che dovranno poi essere rilette in seguito.

Una delle funzionalita' principale dei database e' per l'appunto quella di memorizzare le informazioni in modo che sia rapido ritrovarle in seguito.

Pochi sistemi pero' hanno questo conecetto di "database" cosi' pervasivo (l'AS 400 per esempio, per cui ogni file ha una struttura di record). In generale i programmi possono salvare le informazioni in molti modi diversi e leggerle in molti modi diversi.

Per accedere ad un database (di qualunque tipo) in ambiente Windows vi sono svariati modi. Il principale e' quello di utilizzare l'interfaccia generica di ODBC, che consente l'accesso ad un qualunque database per cui e' presente un driver di interfaccia.

ODBC e' un ottimo sistema di accesso, se non che le sue API di comunicazione sono piuttosto complesse, e non sono idonee per l'accesso tramite linguaggi come ASP o Visual Basic. Per ovviare a questo problema la Microsoft ha sviluppato ADO, Activex Data Object, che, come dice il nome, e' un componente di tipo ActiveX che consente l'accesso ai dati tramite interfaccia ad oggetti. Essendo un componente ActiveX, la sua interfaccia viene "esposta" tramite l'accesso ad oggetti di default. Quindi e' ottimo per il collegamento tramite Visual Basic ed altri linguaggi simili.

Per consentire un accesso ai dati anche in modo "remoto", Microsoft ha poi sviluppato l'interfaccia OLE DB, che consente un accesso diretto ai database mediante una serie di oggetti che possono essere richiamati e pilotati mediante OLE. Purtroppo l'OLE non e' una tecnologia cosi' tanto semplice ne' tanto "leggera" dal punto di vista del consumo di risorse.

Un altra via per accedere ai dati e' quella di utilizzare direttamente il DAO, Data Access Obiect. DAO si trova piu' o meno a meta' tra l'ADO e l'OLE DB. Si tratta infatti di un'insieme di oggetti connessi ed API di controllo che consentono l'accesso ai database di tipo Access e ODBC in modo diretto (come ADO), ma senza tutte le complicazioni dell'OLE.

La struttura di DAO Il diagramma seguente riporta (molto semplificata) la struttura di DAO, almeno per quanto riguarda gli oggetti di interesse piu' comune. Una trattazione completa del DAO e' disponibile sul sito Microsoft e nell'MSDN.


Il DAO

L'oggetto principale DBEngine e' il "motore" del database, il motore fondamentalmente non fa' altro che mantenere i collegamenti tra i vari oggetti "attivi" di DAO, cioe' i Database ed i vari WorkSpace.

DAO consente di avere attivi diversi WorkSpace, in ognuno dei quali possono essere attivi piu' database diversi. Ad ogni WorkSpace possono essere collegate diverse caratteristiche (Utenti, Password, parametri di controllo...).

Notare che gli oggetti Error sono connessi al DBEngine e non alle singole WorkSpace/Database.

Quando usare DAO ? DAO e' la soluzione ideale se si vuole accedre tramite C/C++ ad un database di tipo Access o ODBC, senza volere la complicazione delle API di ODBC (DAO usa ODBC automaticamente se e' istruito a farlo), inoltre, data la presenza di una serie di classi MFC di "incapsulamento" di DAO, l'utilizzo mediante le MFC e' notevolmente piu' semplice dell'uso delle OLE DB.

Come si usa DAO ? Ora inizia la parte interessante.

L'utilizzo di DAO e' notevolmente semplificato se si utilizzano le corrispondenti classi MFC, tali classi sono identificate dal prefisso CDao.

Per utilizzare le classi DAO occorre includere i file necessari per avere il supporto delle MFC, questo si puo' fare usando:

#include <afx.h>
#include <afxdao.h>

Questo e' sufficiente ad includere il supporto DAO e le MFC. Se utilizzate Visual C++, potete ottenere lo stesso risultato usando il Wizard per creare una applicazione che usa le MFC e poi aggiungere AFXDAO.H tra i file di inclusione.

Per utilizzare DAO, per prima cosa occorre "inizializzare" il motore del database, questo si fa' richiamando la funzione AfxDaoInit().

Se la AfxDaoInit non e' richiamata, essa verra' richiamata automaticamente non appena si accede ad una delle classi DAO, quindi strettamente parlando il richiamo non e' obbligatorio, pero' e' buona norma inserirla per chiarificare cio' che il codice si propone di fare (e per evitare problemi il giorno che Microsoft modifica le MFC).

Workspace e Database La prima cosa da fare e' quella di creare un'oggetto CDaoDatabase che ci permette di accedere al database che vogliamo gestire.

La creazione del CDaoDatabase puo' essere fatta in due modi: o usando CDaoWorkspace, oppure creando direttamente il CDaoDatabase, in questo secondo caso, un Workspace di default verra' creato automaticamente.

I Database DAO possono essere di tipo Access (normale) oppure fonti dati di ODBC. L'unica differenza consiste nella creazione dell'oggetto Database associato, che nel caso di Access richiede solo il nome (path completo) del file .MDB corrispondente, mentre nel secondo caso richiede la stringa di connessione completa per il database stesso.

DAO consente anche di creare il database da zero, questo si fa' usando l'apposito metodo Create dell'oggetto Database.

Cosa piuttosto strana, creando il database .MDB con DAO si ottiene un database leggermente piu' compatto che usando Access (60Kb contro 72).

Una volta che abbiamo il database abbiamo diverse possibilita' di gestione:

  • Accedere ai dati usando CDaoRecordset ed una query SQL
  • Inviare query di comando usando Execute()
  • Creare una QueryDef ed eseguirla successivamente
  • Utilizzare i metodi TableDef per interrogare la struttura del database

Leggere le informazioni con CDaoRecordset L'oggetto CDaoRecordset messoci a disposizione dalle MFC incapsula tutte le necessarie funzionalita' per accedere ai dati di una o piu' tabelle tramite una query SQL ed eseguire delle manipolazioni sugli stessi dati.

Ricordiamoci che una query (view) e' modificabile solo se e' fatta su non piu' di due tabelle, contiene tutti i campi obbligatori delle due tabelle (o tutti quelli che non hanno un default) e se le tabelle sono piu' di una, solo la tabella "figlia" e' modificabile. Per sintetizzare le possibilita' che una query con piu' di una tabella sia modificabile sono molto basse.

Per creare un Recordset occorrono due cose: un CDaoDatabase attivo ed una query SQL valida.

Dopo di che' usando:

CDaoRecordset recordset = CDaoRecordset( daoDb );
recordset.Open( type, sql, opt );

Il recordset viene aperto.

Il parametro type indica il tipo di recordset da aprire, i tipi supportati sono molteplici:

  • dbOpenDynaset Apre un recordset di tipo dynaset, con capacita' di scorrimento in entrambi i sensi. Questo tipo di recordset conserva solo un collegamento ai dati, che sono letti direttamente dal database alla bisogna. E' il piu' completo ma il piu' lento.
  • dbOpenTable Apre una tabella, in questo caso la stringa SQL si suppone che contenga solo il nome della tabella da aprire e niente altro. Il recordset e' scorribile in entrambe i sensi,
  • dbOpenSnapshot Uno snapshot e' un recordset non modificabile, con tutti i dati in memoria. E' il tipo piu' efficiente per operazioni di lettura dei dati.

Il parametro opt indica delle opzioni aggiuntive per il recordset da aprire.

  • dbAppendOnly indica che i dati possono solo essere aggiunti al recordset e non eliminati. Si applica solo ai recordset di tipo Dynaset.
  • dbForwardOnly limita le capacita' di scorrimendo del recordset in modo che possa essere scorso solo in avanti, questo si traduce in una minore occupazione di memoria del recordset.
  • dbSeeChanges questa opzione fa' si' che un'eccezzione sia inviata dal motore di database se un altro utente modifica i dati del recordset.
  • dbDenyWrite tenta di ottenere un lock sui record interessati in modo che altri utenti non possano modificarli. Se non riesce ad ottenere un lock, viene generata un'eccezzione.
  • dbDenyRead tenta di ottenere un lock sui record interessati in modo che altri utenti non possano ne' leggerli ne' modificarli. Se non riesce ad ottenere un lock, viene generata un'eccezzione.
  • dbReadOnly il recordset e' di sola lettura

Leggere i dati del recordset Una volta che abbiamo il recordset aperto ed attivo, possiamo leggerne i dati usando il metodo GetFieldValue(), muoverci tra i vari record usando MoveNext(), MovePrevious(), MoveFirst() e MoveLast().

L'accesso ai dati del recordset e' un po' complicato dal fatto che i dati sono memorizzati sotto forma di COleVariant, una particolare classe di variabili di tipo Variant che permette la memorizzazione di ogni tipo di valore, ma questo ci obbliga a "tradurre" ogni valore usando delle apposite macro-funzioni per poter accedere ai veri dati inseriti.

Le macro funzioni V_ permettono di "tradurre" i valori variant nell'equivalente valore "normale" che puo' essere poi gestito.

Per esempio, supponendo che il campo "x" contenga una stringa, per accedere al valore contenuto occorrera' fare:

CString campo;
COleVariant v;

/* reperisco il campo */
recordset.GetFieldValue( "x", v );

/* converto in stringa */
campo = V_BSTRT( &v );

A questo punto abbiamo il campo in una CString e possiamo gestirlo come piu' ci piace.

Vedere l'MSDN o la guida di Visual C per un'elenco completo delle macro V_ o per i dettagli sull'oggetto COleVariant.

Modificare i dati Il sistema piu' semplice per modificare i dati del database e' quello di inviare delle Query di comando al motore del database usando il metodo Execute dell'oggetto CDaoDatabase.

Attenzione: il metodo Execute non puo' essere usato con query che ritornano valori (SELECT).

Una funzione come la seguente:

daoDb.Execute( "DELETE * FROM Tabella;");

Esegue la cancellazione di tutti i record della tabella indicata.

Se la query inviata non e' corretta si verifichera' un'eccezione che dovra' essere recuperata e gestita, altrimenti il programma potrebbe crashare.

Terminare le operazioni Al termine delle operazioni e' buona norma chiudere tutti i Recordset ed il Database richiamando i metodi Close di entrambi, quindi terminare il "motore" di database richiamando la

AfxDaoTerm( );

Questo concludera' le operazioni di gestione ed eliminera' dalla memoria una serie di strutture dati utilizzate in precedenza.

Attenzione: il mancato richiamo della AfxDaoTerm() provoca il crash del programma alla terminazione (qualche schifezza dentro le MFC che non pulisce la memoria come dovrebbe), in un caso ha anche provocato il blocco della macchina con corrispondente reboot forzato.

Un semplice esempio Il codice di esempio presente consente la gestione di una mini-agenda, questo codice e' sviluppato come Console-Application, quindi niente finestre. La scelta di farlo Console-based ha permesso di concentrarsi sulle funzioni di gestione del database e di ignorare i problemi di gestione dell'interfaccia grafica.

Per verificare il programma occorre Visual C++ versione 4 o superiore, probabilmente il codice e' compilabile anche con Borland, ma non ho provato quindi non sono in grado di dire quali modifiche ci siano da apportare al codice (se ce ne sono).

Per compilare il programma create un nuovo progetto vuoto Win32 console, ed inserite i due sorgenti (DAO.CPP e DAO.H) nel progetto. Quindi dal menu' Project scegliete Propieta', selezionate il progetto e scegliete tra le Generalita' "Usa le MFC".

Compilate ed eseguite.

Il programma gestisce un database Access denominato DAODEMO.MDB, se il database non esiste, viene creato, viene creata una tabella chiamata Anagrafica con alcuni campi.

Le funzionalita' di gestione implementate sono l'elencazione dei record, l'inserimento di un nuovo record e l'eliminazione di un record presente. Non e' stata implementata la modifica di un record esistente.

DAO.CPP
DAO.H

Bibliografia e riferimenti Microsft MSDN:

Create an .MDB File for Microsoft Access Databases - Article ID: Q149558
Using CDaoRecordset::Seek - Article ID: Q149087
Using the DAO SDK dbDao Classes with Visual C++ 4.x - Article ID: Q149392
Using the MFC Database Classes in Console Applications - Article ID: Q152696


L'autore Davide Bianchi, pomposamente definito Software Engineer dal biglietto da visita, lavora per la Square bv, societa' olandese con sede a Roermond, che si occupa di sviluppo di software con orientamento grafico. Attualmente sta' lavorando in Java (e si diverte un sacco).

Commenti Vuoi mandare un commento su questo articolo ? Scrivi all'autore.
Contribuire Ti senti in grado di scrivere un articolo e vorresti vederlo pubblicato? Leggi come fare
Copyright Il presente sito e' frutto del sudore della mia fronte (e delle mie dita), se siete interessati a ripubblicare uno degli articoli, documenti o qualunque altra cosa presente in questo sito per cortesia datemene comunicazione, cosi' il giorno che faccio delle aggiunte potro' avvisarvi e magari mandarvi il testo aggiornato.


Questo sito e' composto interamente con VIM

Questo sito non e' ottimizzato per la visione con nessun browser particolare, ne' per nessuna risoluzione particolare ne' impone l'uso di font particolari. Siate liberi di vederlo come vi pare e piace.

Ultimo aggiornamento: 01 Novembre 2000