7. Accesso alle banche dati
7.1. Informazioni generali
Esistono numerose banche dati per le piattaforme Windows. Per accedervi, le applicazioni utilizzano programmi chiamati driver.
![]() |
Nello schema sopra riportato, il driver presenta due interfacce:
- l'interfaccia I1, che si presenta all'applicazione
- l'interfaccia I2 verso il database
Per evitare che un'applicazione scritta per un database B1 debba essere riscritta in caso di migrazione verso un database B2 diverso, è stato effettuato un lavoro di standardizzazione sull'interfaccia I1. Se si utilizzano database che impiegano driver “normalizzati”, il database B1 verrà fornito con un driver P1, il database B2 con un driver P2, e l’interfaccia I1 di questi due driver sarà identica. Pertanto non sarà necessario riscrivere l’applicazione. Sarà così possibile, ad esempio, migrare un database ACCESS verso un database MySQL senza modificare l'applicazione.
Esistono due tipi di driver standardizzati:
- i driver ODBC (Open DataBase Connectivity)
- i driver OLE e DB (Object Linking and Embedding DataBase)
I driver ODBC consentono l'accesso ai database. Le fonti di dati per i driver OLE e DB sono più varie: database, sistemi di messaggistica, rubriche, ... Non ci sono limiti. Qualsiasi fonte di dati può essere oggetto di un driver OLE o DB, se lo decide un editore. Il vantaggio è ovviamente notevole: si ottiene un accesso uniforme a una grande varietà di dati.
La piattaforma .NET viene fornita con due tipi di classi di accesso ai dati:
- le classi SQL e Server.NET
- le classi OLE e Db.NET
Le prime classi consentono l’accesso diretto al server Microsoft SGBD SQL senza driver intermedi. Le seconde consentono l’accesso alle fonti di dati OLE DB.

La piattaforma .NET viene fornita (maggio 2002) con tre driver OLE DB rispettivamente per: SQL Server, Oracle e Microsoft Jet (Access). Se si desidera lavorare con un database dotato di un driver ODBC ma privo dei driver OLE e DB, non è possibile. Pertanto non è possibile lavorare con SGBD e MySQL che (maggio 2002) non forniscono i driver OLE e DB. Esiste tuttavia una serie di classi che consentono l’accesso alle fonti di dati ODBC, ovvero le classi odbc.net. Queste non sono fornite di serie con il SDK e devono essere scaricate dal sito di Microsoft. Negli esempi che seguiranno, utilizzeremo principalmente queste classi ODBC poiché la maggior parte dei database su Windows viene fornita con un driver di questo tipo. Ecco, ad esempio, un elenco dei driver ODBC installati su un computer con Windows 2000 (Menu Start/Impostazioni/Pannello di controllo/Strumenti di amministrazione):

Si seleziona l’icona Origine dati ODBC:

7.2. Le due modalità di utilizzo di una fonte dati
La piattaforma .NET consente di utilizzare una fonte dati in due modi diversi:
- modalità connessa
- modalità offline
In modalità connessa, l'applicazione
- apre una connessione con la fonte dati
- opera con la fonte dati in modalità lettura/scrittura
- chiude la connessione
In modalità offline, l'applicazione
- apre una connessione con la fonte di dati
- ottiene una copia in memoria di tutti o parte dei dati della fonte
- chiude la connessione
- lavora con la copia in memoria dei dati in lettura/scrittura
- una volta terminato il lavoro, apre una connessione, invia i dati modificati alla fonte dei dati affinché vengano aggiornati, chiude la connessione
In entrambi i casi, è l’operazione di elaborazione e aggiornamento dei dati a richiedere tempo. Immaginiamo che questi aggiornamenti vengano effettuati da un utente che inserisce dati: questa operazione può richiedere decine di minuti. Per tutto questo tempo, in modalità connessa, la connessione con il database viene mantenuta e le modifiche vengono immediatamente sincronizzate. In modalità disconnessa, non c’è connessione al database durante l’aggiornamento dei dati. Le modifiche vengono apportate esclusivamente sulla copia in memoria. Vengono sincronizzate con la fonte dei dati in un’unica operazione una volta completato il tutto.
Quali sono i vantaggi e gli svantaggi dei due metodi?
- Una connessione richiede molte risorse di sistema. Se ci sono molte connessioni simultanee, la modalità offline consente di ridurne la durata al minimo. È il caso delle applicazioni web con migliaia di utenti.
- Lo svantaggio della modalità offline è la gestione complessa degli aggiornamenti simultanei. L'utente U1 ottiene i dati al momento T1 e inizia a modificarli. Al momento T2, anche l’utente U2 accede alla fonte dei dati e ottiene gli stessi dati. Nel frattempo, l’utente U1 ha modificato alcuni dati ma non li ha ancora trasmessi alla fonte dei dati. U2 lavora quindi con dati di cui alcuni sono errati. Le classi .NET offrono soluzioni per gestire questo problema, ma non è semplice da risolvere.
- In modalità connessa, l’aggiornamento simultaneo dei dati da parte di più utenti non pone normalmente alcun problema. Poiché la connessione al database viene mantenuta, è il database stesso a gestire questi aggiornamenti simultanei. Pertanto, Oracle blocca una riga del database non appena un utente la modifica. Essa rimarrà bloccata, e quindi inaccessibile agli altri utenti, fino a quando chi l’ha modificata non confermerà (commit) la modifica o la annullerà (rollback).
- Se i dati devono circolare in rete, è necessario scegliere la modalità disconnessa. Essa consente di ottenere un’istantanea dei dati in un oggetto denominato dataset, che rappresenta di per sé un database. Questo oggetto può circolare in rete tra i computer.
Esaminiamo innanzitutto la modalità connessa.
7.3. Accesso ai dati in modalità connessa
7.3.1. I database dell’esempio
Consideriamo un database ACCESS denominato articles.mdb e contenente una sola tabella denominata ARTICLES con la seguente struttura:
nome | tipo |
codice | codice articolo di 4 caratteri |
nome | il suo nome (stringa di caratteri) |
prezzo | il suo prezzo (effettivo) |
stock_actuel | la sua disponibilità attuale (numero intero) |
stock_minimum | la quantità minima (numero intero) al di sotto della quale è necessario rifornire l'articolo |
Il suo contenuto iniziale è il seguente:

Utilizzeremo questa base sia tramite un pilota ODBC cheun driver OLE DB per mostrare la somiglianza dei due approcci e poiché disponiamo di entrambi i tipi di driver per ACCESS.
Utilizzeremo inoltre un database MySQL DBARTICLES contenente la stessa unica tabella ARTICLES, lo stesso contenuto e accessibile tramite un driver ODBC, al fine di dimostrare che l’applicazione scritta per utilizzare il database ACCESS non deve essere modificata per utilizzare il database MySQL. Il database DBARTICLES è accessibile a un utente denominato admarticles con la password mdparticles. La seguente schermata mostra il contenuto del database MySQL:
C:\mysql\bin>mysql --database=dbarticles --user=admarticles --password=mdparticles
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 3 to server version: 3.23.49-max-debug
Type 'help' for help.
mysql> show tables;
+----------------------+
| Tables_in_dbarticles |
+----------------------+
| articles |
+----------------------+
1 row in set (0.01 sec)
mysql> select * from articles;
+------+--------------------------------+------+--------------+---------------+
| code | nom | prix | stock_actuel | stock_minimum |
+------+--------------------------------+------+--------------+---------------+
| a300 | vÚlo | 2500 | 10 | 5 |
| b300 | pompe | 56 | 62 | 45 |
| c300 | arc | 3500 | 10 | 20 |
| d300 | flÞches - lot de 6 | 780 | 12 | 20 |
| e300 | combinaison de plongÚe | 2800 | 34 | 7 |
| f300 | bouteilles d'oxygÞne | 800 | 10 | 5 |
+------+--------------------------------+------+--------------+---------------+
6 rows in set (0.02 sec)
mysql> describe articles;
+---------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------+-------------+------+-----+---------+-------+
| code | text | YES | | NULL | |
| nom | text | YES | | NULL | |
| prix | double | YES | | NULL | |
| stock_actuel | smallint(6) | YES | | NULL | |
| stock_minimum | smallint(6) | YES | | NULL | |
+---------------+-------------+------+-----+---------+-------+
5 rows in set (0.00 sec)
mysql> exit
Bye
Per impostare la base ACCESS come fonte dati ODBC, procedere come segue:
- aprire l’amministratore delle fonti dati ODBC come illustrato in precedenza e selezionare la scheda User DSN (DSN=Data Source Name)

- aggiungete una fonte tramite il pulsante Add, specificate che questa fonte è accessibile tramite un driver Access ed eseguite Terminer:

- Assegnate il nome articles-access alla fonte dei dati, inserite una descrizione a scelta e utilizzate il pulsante Sélectionner per indicare il file .mdb presente nel database. Concludete con OK.

La nuova fonte dati apparirà quindi nell’elenco delle fonti DSN dell’utente:

Per impostare il database MySQL DBARTICLES come fonte dati ODBC, procedere come segue:
- aprire l’amministratore delle fonti dati ODBC come illustrato in precedenza e selezionare la scheda «Utente» DSN. Aggiungere una nuova fonte dati con Add e selezionare il driver ODBC di MySQL.

- Eseguire Terminer. Verrà visualizzata una pagina di configurazione della fonte MySQL:
54321

- in (1) si assegna un nome alla nostra fonte di dati ODBC
- al punto (2) si indica il computer su cui si trova il server MySQL. Qui inseriamo localhost per indicare che si trova sulla stessa macchina della nostra applicazione. Se il server MySQL si trovasse su una macchina remota M, inseriremmo lì il suo nome e la nostra applicazione funzionerebbe quindi con un database remoto senza alcuna modifica.
- In (3) si inserisce il nome del database. In questo caso si chiama DBARTICLES.
- al punto (4) si inserisce il nome utente admarticles e al punto (5) la password mdparticles.
7.3.2. Utilizzo di un driver ODBC
In un’applicazione che utilizza un database in modalità connessa, si seguono generalmente i seguenti passaggi:
- Connessione al database
- Invio delle query SQL al database
- Ricezione ed elaborazione dei risultati di tali query
- Chiusura della connessione
Le fasi 2 e 3 vengono eseguite ripetutamente, mentre la chiusura della connessione avviene solo al termine dell’utilizzo del database. Si tratta di uno schema relativamente classico, a cui potreste essere abituati se avete già utilizzato un database in modo interattivo. Queste fasi sono le stesse sia che il database venga utilizzato tramite un driver ODBC sia tramite un driver OLE o DB. Di seguito presentiamo un esempio con le classi .NET per la gestione delle fonti di dati ODBC. Il programma si chiama liste e accetta come parametro il nome DSN di una fonte dati ODBC contenente una tabella ARTICLES. Visualizza quindi il contenuto di tale tabella:
dos>liste
syntaxe : pg dsnArticles
dos>liste articles-access
----------------------------------------
code,nom,prix,stock_actuel,stock_minimum
----------------------------------------
a300 vélo 2500 10 5
b300 pompe 56 62 45
c300 arc 3500 10 20
d300 flèches - lot de 6 780 12 20
e300 combinaison de plongée 2800 34 7
f300 bouteilles d'oxygène 800 10 5
dos>liste mysql-artices
Erreur d'exploitation de la base de données (ERROR [IM002] [Microsoft][ODBC Driver Manager] Data source name not found and no default driver specified)
dos>liste mysql-articles
----------------------------------------
code,nom,prix,stock_actuel,stock_minimum
----------------------------------------
a300 vélo 2500 10 5
b300 pompe 56 62 45
c300 arc 3500 10 20
d300 flèches - lot de 6 780 12 20
e300 combinaison de plongée 2800 34 7
f300 bouteilles d'oxygène 800 10 5
Dai risultati sopra riportati, si nota che il programma ha elencato sia il contenuto del database ACCESS sia quello del database MySQL. Esaminiamo ora il codice di questo programma:
' opzioni
Option Explicit On
Option Strict On
' spazi dei nomi
Imports System
Imports System.Data
Imports Microsoft.Data.Odbc
Imports Microsoft.VisualBasic
Module db1
Sub main(ByVal args As String())
' applicazione console
' visualizza il contenuto di una tabella ARTICLES di un database DSN
' il cui nome è passato come parametro
Const syntaxe As String = "syntaxe : pg dsnArticles"
Const tabArticles As String = "articles" ' la table des articles
' verifica dei parametri
' è presente un parametro?
If args.Length <> 1 Then
' messaggio di errore
Console.Error.WriteLine(syntaxe)
' fine
Environment.Exit(1)
End If
' si recupera il parametro
Dim dsnArticles As String = args(0) ' la base DSN
' preparazione della connessione al database
Dim articlesConn As OdbcConnection = Nothing ' la connexion
Dim myReader As OdbcDataReader = Nothing ' le lecteur de données
' si tenta di accedere al database
Try
' stringa di connessione al database
Dim connectString As String = "DSN=" + dsnArticles + ";"
articlesConn = New OdbcConnection(connectString)
articlesConn.Open()
' esecuzione di un comando SQL
Dim sqlText As String = "select * from " + tabArticles
Dim myOdbcCommand As New OdbcCommand(sqlText)
myOdbcCommand.Connection = articlesConn
myReader = myOdbcCommand.ExecuteReader()
' Elaborazione della tabella recuperata
' visualizzazione delle colonne
Dim ligne As String = ""
Dim i As Integer
For i = 0 To (myReader.FieldCount - 1) - 1
ligne += myReader.GetName(i) + ","
Next i
ligne += myReader.GetName(i)
Console.Out.WriteLine((ControlChars.Lf + "".PadLeft(ligne.Length, "-"c) + ControlChars.Lf + ligne + ControlChars.Lf + "".PadLeft(ligne.Length, "-"c) + ControlChars.Lf))
' visualizzazione dei dati
While myReader.Read()
' Elaborazione della riga corrente
ligne = ""
For i = 0 To myReader.FieldCount - 1
ligne += myReader(i).ToString + " "
Next i
Console.WriteLine(ligne)
End While
Catch ex As Exception
Console.Error.WriteLine(("Erreur d'exploitation de la base de données " + ex.Message + ")"))
Environment.Exit(2)
Finally
' Chiusura del lettore
myReader.Close()
' Chiusura della connessione
articlesConn.Close()
End Try
End Sub
End Module
Le classi di gestione delle fonti ODBC si trovano nello spazio dei nomi Microsoft.Data.Odbc, che deve quindi essere importato. Inoltre, alcune classi si trovano nello spazio dei nomi System.Data.
Imports System.Data
Imports Microsoft.Data.Odbc
Gli spazi dei nomi utilizzati dal programma si trovano in diversi assembly. Il programma viene compilato con il seguente comando:
dos>vbc /r:microsoft.data.odbc.dll /r:microsoft.visualbasic.dll /r:system.dll /r:system.data.dll db1.vb
7.3.2.1. La fase di connessione
Una connessione ODBC utilizza la classe OdbcConnection. Il costruttore di questa classe accetta come parametro la cosiddetta stringa di connessione. Si tratta di una stringa di caratteri che definisce tutti i parametri necessari affinché la connessione al database possa avvenire. Questi parametri possono essere molto numerosi e quindi la stringa può risultare complessa. La stringa ha il formato "param1=valeur1;param2=valeur2;...;paramj=valeurj;". Ecco alcuni possibili parametri paramj:
nome di un utente che accederà al database | |
password di tale utente | |
nome del database DSN, se presente | |
nome del database a cui si accede | |
Se si definisce una fonte dati come fonte dati ODBC tramite l'amministratore delle fonti dati ODBC, questi parametri sono già stati specificati e salvati. È quindi sufficiente passare il parametro DSN che fornisce il nome DSN della fonte dati. È ciò che viene fatto qui:
' preparazione della connessione al database
Dim articlesConn As OdbcConnection = Nothing ' la connexion
Dim myReader As OdbcDataReader = Nothing ' le lecteur de données
Try
' si sta tentando di accedere al database
' stringa di connessione al database
Dim connectString As String = "DSN=" + dsnArticles + ";"
articlesConn = New OdbcConnection(connectString)
articlesConn.Open()
Una volta creato l'oggetto OdbcConnection, si apre la connessione con il metodo Open. L'apertura della connessione può fallire come qualsiasi altra operazione sul database. Per questo motivo, l'intero codice di accesso al database è contenuto in un try-catch. Una volta stabilita la connessione, è possibile inviare richieste SQL al database.
7.3.2.2. Inviare query SQL
Per inviare richieste SQL, è necessario un oggetto Command, in questo caso più precisamente un oggetto OdbcCommand poiché si utilizza una fonte di dati ODBC. La classe OdbcCommand dispone di diversi costruttori:
- OdbcCommand(): crea un oggetto Command vuoto. Per utilizzarlo, sarà necessario specificare in seguito diverse proprietà:
- CommandText: il testo della query SQL da eseguire
- Connessione: l’oggetto OdbcConnection che rappresenta la connessione al database su cui verrà eseguita la query
- CommandType: il tipo della query SQL. Sono possibili tre valori
- CommandType.Text: la proprietà CommandText contiene il testo di una query SQL (valore predefinito)
- CommandType.StoredProcedure: la proprietà CommandText contiene il nome di una procedura memorizzata nel database
- CommandType.TableDirect: la proprietà CommandText contiene il nome di una tabella T. Equivalente a `select * from T`. Esiste solo per i driver OLE e DB.
- OdbcCommand(string sqlText): il parametro sqlText verrà assegnato alla proprietà CommandText. Questo è il testo della query SQL da eseguire. La connessione dovrà essere specificata nella proprietà Connection.
- OdbcCommand(string sqlText, OdbcConnection connessione): il parametro sqlText verrà assegnato alla proprietà CommandText e il parametro connexion alla proprietà Connection.
Per eseguire la query SQL, sono disponibili due metodi:
- OdbcdataReader ExecuteReader(): invia la query SELECT da CommandText alla connessione Connection e crea un oggetto OdbcDataReader che consente l’accesso a tutte le righe della tabella dei risultati di select
- int ExecuteNOnQuery(): invia la richiesta di aggiornamento (INSERT, UPDATE, DELETE) da CommandText alla connessione Connection e restituisce il numero di righe interessate da tale aggiornamento.
Nel nostro esempio, dopo aver aperto la connessione al database, inviamo una query SQL SELECT per ottenere il contenuto della tabella ARTICLES:
' esecuzione di un comando SQL
Dim sqlText As String = "select * from " + tabArticles
Dim myOdbcCommand As New OdbcCommand(sqlText)
myOdbcCommand.Connection = articlesConn
myReader = myOdbcCommand.ExecuteReader()
Una query di interrogazione è solitamente del tipo:
select col1, col2,... from table1, table2,...
where condition
order by expression
...
Solo le parole chiave della prima riga sono obbligatorie, le altre sono facoltative. Esistono altre parole chiave non riportate qui.
- Viene eseguito un join con tutte le tabelle che seguono la parola chiave from
- Vengono conservate solo le colonne che seguono la parola chiave "select"
- Vengono conservate solo le righe che soddisfano la condizione della parola chiave `where`
- Le righe risultanti, ordinate secondo l’espressione della parola chiave `order by`, costituiscono il risultato della query.
Il risultato di un select è una tabella. Se si considera la tabella ARTICLES precedente e si desiderano i nomi degli articoli il cui stock attuale è inferiore alla soglia minima, si scriverà:
select nom from articles where stock_actuel<stock_minimum
Se si desidera ordinarli in ordine alfabetico in base ai nomi, si scriverà:
select nom from articles where stock_actuel<stock_minimum order by nom
7.3.2.3. Analisi del risultato di una query SELECT
Il risultato di una query SELECT in modalità non connessa è un oggetto DataReader, in questo caso un oggetto OdbcDataReader. Questo oggetto consente di ottenere in sequenza tutte le righe del risultato e di ottenere informazioni sulle colonne di tali risultati. Esaminiamo alcune proprietà e metodi di questa classe:
il numero di colonne della tabella | |
Item(i) rappresenta la colonna n. i della riga corrente del risultato | |
il valore della colonna n. i della riga corrente, rappresentato come tipo XXX (Int16, Int32, Int64, Double, String, Boolean, ...) | |
nome della colonna n. i | |
chiude l'oggetto OdbcdataReader e libera le risorse associate | |
avanza di una riga nella tabella dei risultati. Restituisce falso se ciò non è possibile. La nuova riga diventa la riga corrente del lettore. |
L'elaborazione del risultato di un select è tipicamente un'elaborazione sequenziale analoga a quella dei file di testo: è possibile solo avanzare nella tabella, non tornare indietro:
While myReader.Read()
' si ha una riga - la si elabora
....
' riga successiva
end while
Queste spiegazioni sono sufficienti per comprendere il codice seguente del nostro esempio:
' Elaborazione della tabella recuperata
' visualizzazione delle colonne
Dim ligne As String = ""
Dim i As Integer
For i = 0 To (myReader.FieldCount - 1) - 1
ligne += myReader.GetName(i) + ","
Next i
ligne += myReader.GetName(i)
Console.Out.WriteLine((ControlChars.Lf + "".PadLeft(ligne.Length, "-"c) + ControlChars.Lf + ligne + ControlChars.Lf + "".PadLeft(ligne.Length, "-"c) + ControlChars.Lf))
' visualizzazione dei dati
While myReader.Read()
' elaborazione riga corrente
ligne = ""
For i = 0 To myReader.FieldCount - 1
ligne += myReader(i).ToString + " "
Next i
Console.WriteLine(ligne)
End While
L'unica difficoltà risiede nell'istruzione in cui vengono concatenati i valori delle diverse colonne della riga corrente:
For i = 0 To myReader.FieldCount - 1
ligne += myReader(i).ToString + " "
Next i
La notazione riga+=myReader(i).ToString viene tradotta come riga+=myReader.Item(i).ToString() dove Item(i) è il valore della colonna i della riga corrente.
7.3.2.4. Rilascio delle risorse
Le classi OdbcReader e OdbcConnection dispongono entrambe di un metodo Close() che libera le risorse associate agli oggetti così chiusi.
7.3.3. Utilizzo di un driver OLE DB
Riprendiamo lo stesso esempio, questa volta con un database a cui si accede tramite un driver OLE DB. La piattaforma .NET fornisce un driver di questo tipo per i database ACCESS. Utilizzeremo quindi lo stesso database articles.mdb di prima. Vogliamo dimostrare qui che, anche se le classi cambiano, i concetti rimangono gli stessi:
- la connessione è rappresentata da un oggetto OleDbConnection
- viene emessa una richiesta SQL tramite un oggetto OleDbCommand
- se questa richiesta è una clausola SELECT, si otterrà in risposta un oggetto OleDbDataReader per accedere alle righe della tabella dei risultati
Queste classi si trovano nello spazio dei nomi System.Data.OleDb. Il programma precedente può essere facilmente modificato per gestire un database OLE DB:
- si sostituisce ovunque OdbcXX con OleDbXX
- si modifica la stringa di connessione. Per un database ACCESS senza login/password, la stringa di connessione è Provider=Microsoft.JET.OLEDB.4.0;Data Source=[fichier.mdb]. La parte configurabile di questa stringa è il nome del file ACCESS da utilizzare. Modificheremo il nostro programma in modo che accetti come parametro il nome di questo file.
- Lo spazio dei nomi da importare è ora System.Data.OleDb.
Il nostro programma diventa il seguente:
' opzioni
Option Explicit On
Option Strict On
' spazi dei nomi
Imports System
Imports System.Data
Imports Microsoft.Data.Odbc
Imports Microsoft.VisualBasic
Imports System.Data.OleDb
Module db2
Public Sub Main(ByVal args() As String)
' applicazione console
' visualizza il contenuto di una tabella ARRTICLES di un database DSN
' il cui nome è passato come parametro
Const syntaxe As String = "syntaxe : pg base_access_articles"
Const tabArticles As String = "articles" ' la table des articles
' verifica dei parametri
' è presente un parametro?
If args.Length <> 1 Then
' messaggio di errore
Console.Error.WriteLine(syntaxe)
' fine
Environment.Exit(1)
End If
' recupero del parametro
Dim dbArticles As String = args(0) ' la base de données
' preparazione della connessione al database
Dim articlesConn As OleDbConnection = Nothing ' la connexion
Dim myReader As OleDbDataReader = Nothing ' le lecteur de données
' si tenta di accedere al database
Try
' stringa di connessione al database
Dim connectString As String = "Provider=Microsoft.JET.OLEDB.4.0;Data Source=" + dbArticles + ";"
articlesConn = New OleDbConnection(connectString)
articlesConn.Open()
' esecuzione di un comando SQL
Dim sqlText As String = "select * from " + tabArticles
Dim myOleDbCommand As New OleDbCommand(sqlText)
myOleDbCommand.Connection = articlesConn
myReader = myOleDbCommand.ExecuteReader()
' Analisi della tabella recuperata
' visualizzazione delle colonne
Dim ligne As String = ""
Dim i As Integer
For i = 0 To (myReader.FieldCount - 1) - 1
ligne += myReader.GetName(i) + ","
Next i
ligne += myReader.GetName(i)
Console.Out.WriteLine((ControlChars.Lf + "".PadLeft(ligne.Length, "-"c) + ControlChars.Lf + ligne + ControlChars.Lf + "".PadLeft(ligne.Length, "-"c) + ControlChars.Lf))
' visualizzazione dei dati
While myReader.Read()
' Elaborazione della riga corrente
ligne = ""
For i = 0 To myReader.FieldCount - 1
ligne += myReader(i).ToString + " "
Next i
Console.WriteLine(ligne)
End While
Catch ex As Exception
Console.Error.WriteLine(("Erreur d'exploitation de la base de données (" + ex.Message + ")"))
Environment.Exit(2)
Finally
' Chiusura del lettore
myReader.Close()
' Chiusura della connessione
articlesConn.Close()
End Try
' fine
Environment.Exit(0)
End Sub
End Module
Risultati ottenuti:
dos>vbc liste.vb
E:\data\serge\MSNET\vb.net\adonet\6>dir
07/05/2002 15:09 2 325 liste.CS
07/05/2002 15:09 4 608 liste.exe
20/08/2001 11:54 86 016 ARTICLES.MDB
dos>liste articles.mdb
----------------------------------------
code,nom,prix,stock_actuel,stock_minimum
----------------------------------------
a300 vélo 2500 10 5
b300 pompe 56 62 45
c300 arc 3500 10 20
d300 flèches - lot de 6 780 12 20
e300 combinaison de plongée 2800 34 7
f300 bouteilles d'oxygène 800 10 5
7.3.4. Aggiornamento di una tabella
Gli esempi precedenti si limitavano a elencare il contenuto di una tabella. Modifichiamo il nostro programma di gestione del database degli articoli in modo che possa modificarlo. Il programma si chiama sql. Come parametro gli viene passato il nome DSN del database degli articoli da gestire. L’utente digita direttamente i comandi SQL sulla tastiera e il programma li esegue, come mostrano i risultati seguenti ottenuti sul database degli articoli MySQL:
dos>vbc /r:microsoft.data.odbc.dll sql.vb
dos>sql mysql-articles
Requête SQL (fin pour arrêter) : select * from articles
----------------------------------------
code,nom,prix,stock_actuel,stock_minimum
----------------------------------------
a300 vélo 2500 10 5
b300 pompe 56 62 45
c300 arc 3500 10 20
d300 flèches - lot de 6 780 12 20
e300 combinaison de plongée 2800 34 7
f300 bouteilles d'oxygène 800 10 5
Requête SQL (fin pour arrêter) : select * from articles where stock_actuel<stock_minimum
----------------------------------------
code,nom,prix,stock_actuel,stock_minimum
----------------------------------------
c300 arc 3500 10 20
d300 flèches - lot de 6 780 12 20
Requête SQL (fin pour arrêter) : insert into articles values ("1","1",1,1,1)
Il y a eu 1 ligne(s) modifiée(s)
Requête SQL (fin pour arrêter) : select * from articles
----------------------------------------
code,nom,prix,stock_actuel,stock_minimum
----------------------------------------
a300 vélo 2500 10 5
b300 pompe 56 62 45
c300 arc 3500 10 20
d300 flèches - lot de 6 780 12 20
e300 combinaison de plongée 2800 34 7
f300 bouteilles d'oxygène 800 10 5
1 1 1 1 1
Requête SQL (fin pour arrêter) : update articles set nom="2" where nom="1"
Il y a eu 1 ligne(s) modifiée(s)
Requête SQL (fin pour arrêter) : select * from articles
----------------------------------------
code,nom,prix,stock_actuel,stock_minimum
----------------------------------------
a300 vélo 2500 10 5
b300 pompe 56 62 45
c300 arc 3500 10 20
d300 flèches - lot de 6 780 12 20
e300 combinaison de plongée 2800 34 7
f300 bouteilles d'oxygène 800 10 5
1 2 1 1 1
Requête SQL (fin pour arrêter) : delete from articles where code="1"
Il y a eu 1 ligne(s) modifiée(s)
Requête SQL (fin pour arrêter) : select * from articles
----------------------------------------
code,nom,prix,stock_actuel,stock_minimum
----------------------------------------
a300 vélo 2500 10 5
b300 pompe 56 62 45
c300 arc 3500 10 20
d300 flèches - lot de 6 780 12 20
e300 combinaison de plongée 2800 34 7
f300 bouteilles d'oxygène 800 10 5
Requête SQL (fin pour arrêter) : select * from articles order by nom asc
----------------------------------------
code,nom,prix,stock_actuel,stock_minimum
----------------------------------------
c300 arc 3500 10 20
f300 bouteilles d'oxygène 800 10 5
e300 combinaison de plongée 2800 34 7
d300 flèches - lot de 6 780 12 20
b300 pompe 56 62 45
a300 vélo 2500 10 5
Requête SQL (fin pour arrêter) : fin
Il programma è il seguente:
' opzioni
Option Explicit On
Option Strict On
' spazi dei nomi
Imports System
Imports System.Data
Imports Microsoft.Data.Odbc
Imports System.Data.OleDb
Imports System.Text.RegularExpressions
Imports System.Collections
Imports Microsoft.VisualBasic
Module db3
Public Sub Main(ByVal args() As String)
' console dell'applicazione
' esegue le richieste SQL digitate da tastiera su una
' tabella ARTICLES di un database DSN il cui nome è passato come parametro
Const syntaxe As String = "syntaxe : pg dsnArticles"
' verifica dei parametri
' ci sono 2 parametri
If args.Length <> 1 Then
' messaggio di errore
Console.Error.WriteLine(syntaxe)
' fine
Environment.Exit(1)
End If 'if
' si recupera il parametro
Dim dsnArticles As String = args(0)
' stringa di connessione al database
Dim connectString As String = "DSN=" + dsnArticles + ";"
' preparazione della connessione al database
Dim articlesConn As OdbcConnection = Nothing
Dim sqlCommand As OdbcCommand = Nothing
Try
' si tenta di accedere al database
articlesConn = New OdbcConnection(connectString)
articlesConn.Open()
' si crea un oggetto command
sqlCommand = New OdbcCommand("", articlesConn)
'try
Catch ex As Exception
' messaggio di errore
Console.Error.WriteLine(("Erreur d'exploitation de la base de données (" + ex.Message + ")"))
' liberazione delle risorse
Try
articlesConn.Close()
Catch
End Try
Environment.Exit(2)
End Try 'catch
' si crea un dizionario dei comandi SQL accettati
Dim commandesSQL() As String = {"select", "insert", "update", "delete"}
Dim dicoCommandes As New Hashtable
Dim i As Integer
For i = 0 To commandesSQL.Length - 1
dicoCommandes.Add(commandesSQL(i), True)
Next i
' lettura ed esecuzione dei comandi SQL digitati dalla tastiera
Dim requête As String = Nothing ' texte de la requête SQL
Dim champs() As String ' les champs de la requête
Dim modèle As New Regex("\s+")
' ciclo di immissione ed esecuzione dei comandi SQL digitati dalla tastiera
While True
' Nessun errore all'inizio
Dim erreur As Boolean = False
' richiesta della query
Console.Out.Write(ControlChars.Lf + "Requête SQL (fin pour arrêter) : ")
requête = Console.In.ReadLine().Trim().ToLower()
' finito?
If requête = "fin" Then
Exit While
End If
' si scompone la richiesta in campi
champs = modèle.Split(requête)
' richiesta valida?
If champs.Length = 0 Or Not dicoCommandes.ContainsKey(champs(0)) Then
' messaggio di errore
Console.Error.WriteLine("Requête invalide. Utilisez select, insert, update, delete")
' richiesta successiva
erreur = True
End If
If Not erreur Then
' preparazione dell'oggetto Command per eseguire la richiesta
sqlCommand.CommandText = requête
' esecuzione della richiesta
Try
If champs(0) = "select" Then
executeSelect(sqlCommand)
Else
executeUpdate(sqlCommand)
End If
Catch ex As Exception
' messaggio di errore
Console.Error.WriteLine(("Erreur d'exploitation de la base de données (" + ex.Message + ")"))
End Try
End If
End While
' liberazione delle risorse
Try
articlesConn.Close()
Catch
End Try
Environment.Exit(0)
End Sub
' esecuzione di una richiesta di aggiornamento
Sub executeUpdate(ByVal sqlCommand As OdbcCommand)
' esegue sqlCommand, richiesta di aggiornamento
Dim nbLignes As Integer = sqlCommand.ExecuteNonQuery()
' visualizzazione
Console.Out.WriteLine(("Il y a eu " & nbLignes & " ligne(s) modifiée(s)"))
End Sub
' esecuzione di una query SELECT
Sub executeSelect(ByVal sqlCommand As OdbcCommand)
' esegue sqlCommand, query SELECT
Dim myReader As OdbcDataReader = sqlCommand.ExecuteReader()
' Elaborazione della tabella recuperata
' visualizzazione delle colonne
Dim ligne As String = ""
Dim i As Integer
For i = 0 To (myReader.FieldCount - 1) - 1
ligne += myReader.GetName(i) + ","
Next i
ligne += myReader.GetName(i)
Console.Out.WriteLine((ControlChars.Lf + "".PadLeft(ligne.Length, "-"c) + ControlChars.Lf + ligne + ControlChars.Lf + "".PadLeft(ligne.Length, "-"c) + ControlChars.Lf))
' Visualizzazione dei dati
While myReader.Read()
' Elaborazione della riga corrente
ligne = ""
For i = 0 To myReader.FieldCount - 1
ligne += myReader(i).ToString + " "
Next i
' Visualizzazione
Console.WriteLine(ligne)
End While
' liberazione delle risorse
myReader.Close()
End Sub
End Module
Qui commentiamo solo le novità rispetto al programma precedente:
- Creiamo un dizionario dei comandi SQL accettati:
' si crea un dizionario dei comandi SQL accettati
Dim commandesSQL() As String = {"select", "insert", "update", "delete"}
Dim dicoCommandes As New Hashtable
Dim i As Integer
For i = 0 To commandesSQL.Length - 1
dicoCommandes.Add(commandesSQL(i), True)
Next i
il che ci permette poi di verificare facilmente se la prima parola (campo [0]) della query digitata è uno dei quattro comandi accettati:
' Richiesta valida?
If champs.Length = 0 Or Not dicoCommandes.ContainsKey(champs(0)) Then
' messaggio di errore
Console.Error.WriteLine("Requête invalide. Utilisez select, insert, update, delete")
' Richiesta successiva
erreur = True
End If 'if
- In precedenza, la query era stata scomposta in campi utilizzando il metodo Split della classe RegEx:
Dim modèle As New Regex("\s+")
....
' si scompone la query in campi
champs = modèle.Split(requête)
Le parole che compongono la query possono essere separate da un numero qualsiasi di spazi.
- L'esecuzione di una query select non utilizza lo stesso metodo di una query di aggiornamento (insert, update, delete). È quindi necessario effettuare un test ed eseguire una funzione diversa per ciascuno di questi due casi:
' preparazione dell'oggetto Command per l'esecuzione della query
sqlCommand.CommandText = requête
' esecuzione della richiesta
Try
If champs(0) = "select" Then
executeSelect(sqlCommand)
Else
executeUpdate(sqlCommand)
End If 'try
Catch ex As Exception
' messaggio di errore
Console.Error.WriteLine(("Erreur d'exploitation de la base de données (" + ex.Message + ")"))
End Try
L'esecuzione di una richiesta SQL può generare un'eccezione che viene gestita in questa sede.
- La funzione executeSelect riprende tutto ciò che è stato visto negli esempi precedenti.
- La funzione executeUpdate utilizza il metodo ExecuteNonQuery della classe OdbcCommand, che restituisce il numero di righe interessate dal comando.
7.3.5. IMPOTS
Riprendiamo l'oggetto impôt creato in un capitolo precedente:
' opzioni
Option Strict On
Option Explicit On
' spazi dei nomi
Imports System
Public Class impôt
' dati necessari per il calcolo dell'imposta
' provengono da una fonte esterna
Private limites(), coeffR(), coeffN() As Decimal
' produttore
Public Sub New(ByVal LIMITES() As Decimal, ByVal COEFFR() As Decimal, ByVal COEFFN() As Decimal)
' si verifica che le 3 tabelle abbiano le stesse dimensioni
Dim OK As Boolean = LIMITES.Length = COEFFR.Length And LIMITES.Length = COEFFN.Length
If Not OK Then
Throw New Exception("Les 3 tableaux fournis n'ont pas la même taille(" & LIMITES.Length & "," & COEFFR.Length & "," & COEFFN.Length & ")")
End If
' va bene
Me.limites = LIMITES
Me.coeffR = COEFFR
Me.coeffN = COEFFN
End Sub
' calcolo dell'imposta
Public Function calculer(ByVal marié As Boolean, ByVal nbEnfants As Integer, ByVal salaire As Integer) As Long
' calcolo del numero di quote
Dim nbParts As Decimal
If marié Then
nbParts = CDec(nbEnfants) / 2 + 2
Else
nbParts = CDec(nbEnfants) / 2 + 1
End If
If nbEnfants >= 3 Then
nbParts += 0.5D
End If
' calcolo del reddito imponibile e del quoziente familiare
Dim revenu As Decimal = 0.72D * salaire
Dim QF As Decimal = revenu / nbParts
' calcolo dell'imposta
limites((limites.Length - 1)) = QF + 1
Dim i As Integer = 0
While QF > limites(i)
i += 1
End While
' restituzione del risultato
Return CLng(revenu * coeffR(i) - nbParts * coeffN(i))
End Function
End Class
Aggiungiamo un nuovo costruttore che consente di inizializzare gli array limites, coeffR, coeffN a partire da un database ODBC:
Imports System.Data
Imports Microsoft.Data.Odbc
Imports System.Collections
...
' funzione 2
Public Sub New(ByVal DSNimpots As String, ByVal Timpots As String, ByVal colLimites As String, ByVal colCoeffR As String, ByVal colCoeffN As String)
' inizializza le tre tabelle dei limiti, coeffR, coeffN a partire
' dal contenuto della tabella Timpots del database ODBC DSNimpots
' colLimites, colCoeffR, colCoeffN sono le tre colonne di questa tabella
' può generare un'eccezione
Dim connectString As String = "DSN=" + DSNimpots + ";" ' chaîne de connexion à la base
Dim impotsConn As OdbcConnection = Nothing ' la connexion
Dim sqlCommand As OdbcCommand = Nothing ' la commande SQL
' la query SELECT
Dim selectCommand As String = "select " + colLimites + "," + colCoeffR + "," + colCoeffN + " from " + Timpots
' tabelle per recuperare i dati
Dim tLimites As New ArrayList
Dim tCoeffR As New ArrayList
Dim tCoeffN As New ArrayList
' si tenta di accedere al database
impotsConn = New OdbcConnection(connectString)
impotsConn.Open()
' si crea un oggetto command
sqlCommand = New OdbcCommand(selectCommand, impotsConn)
' si esegue la query
Dim myReader As OdbcDataReader = sqlCommand.ExecuteReader()
' Analisi della tabella recuperata
While myReader.Read()
' i dati della riga corrente vengono inseriti nelle tabelle
tLimites.Add(myReader(colLimites))
tCoeffR.Add(myReader(colCoeffR))
tCoeffN.Add(myReader(colCoeffN))
End While
' liberazione delle risorse
myReader.Close()
impotsConn.Close()
' gli array dinamici vengono inseriti in array statici
Me.limites = New Decimal(tLimites.Count) {}
Me.coeffR = New Decimal(tLimites.Count) {}
Me.coeffN = New Decimal(tLimites.Count) {}
Dim i As Integer
For i = 0 To tLimites.Count - 1
limites(i) = Decimal.Parse(tLimites(i).ToString())
coeffR(i) = Decimal.Parse(tCoeffR(i).ToString())
coeffN(i) = Decimal.Parse(tCoeffN(i).ToString())
Next i
End Sub
Il programma di test è il seguente: riceve come argomenti i parametri da trasmettere al costruttore della classe impôt. Dopo aver creato un oggetto impôt, esegue i calcoli dell'imposta da pagare:
Option Explicit On
Option Strict On
' spazi dei nomi
Imports System
Imports Microsoft.VisualBasic
' pagina di test
Module testimpots
Sub Main(ByVal arguments() As String)
' programma interattivo per il calcolo delle imposte
' l'utente digita tre dati dalla tastiera: coniugato nbEnfants stipendio
' il programma visualizza quindi l’imposta da pagare
Const syntaxe1 As String = "pg DSNimpots tabImpots colLimites colCoeffR colCoeffN"
Const syntaxe2 As String = "syntaxe : marié nbEnfants salaire" + ControlChars.Lf + "marié : o pour marié, n pour non marié" + ControlChars.Lf + "nbEnfants : nombre d'enfants" + ControlChars.Lf + "salaire : salaire annuel en F"
' verifica dei parametri del programma
If arguments.Length <> 5 Then
' messaggio di errore
Console.Error.WriteLine(syntaxe1)
' fine
Environment.Exit(1)
End If 'if
' si recuperano gli argomenti
Dim DSNimpots As String = arguments(0)
Dim tabImpots As String = arguments(1)
Dim colLimites As String = arguments(2)
Dim colCoeffR As String = arguments(3)
Dim colCoeffN As String = arguments(4)
' creazione di un oggetto imposta
Dim objImpôt As impôt = Nothing
Try
objImpôt = New impôt(DSNimpots, tabImpots, colLimites, colCoeffR, colCoeffN)
Catch ex As Exception
Console.Error.WriteLine(("L'erreur suivante s'est produite : " + ex.Message))
Environment.Exit(2)
End Try
' ciclo infinito
While True
' all'inizio nessun errore
Dim erreur As Boolean = False
' vengono richiesti i parametri per il calcolo dell'imposta
Console.Out.Write("Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :")
Dim paramètres As String = Console.In.ReadLine().Trim()
' Cosa fare?
If paramètres Is Nothing Or paramètres = "" Then
Exit While
End If
' verifica del numero di argomenti nella riga inserita
Dim args As String() = paramètres.Split(Nothing)
Dim nbParamètres As Integer = args.Length
If nbParamètres <> 3 Then
Console.Error.WriteLine(syntaxe2)
erreur = True
End If
Dim marié As String
Dim nbEnfants As Integer
Dim salaire As Integer
If Not erreur Then
' Verifica della validità dei parametri
' coniugato
marié = args(0).ToLower()
If marié <> "o" And marié <> "n" Then
Console.Error.WriteLine((syntaxe2 + ControlChars.Lf + "Argument marié incorrect : tapez o ou n"))
erreur = True
End If
' nbEnfants
nbEnfants = 0
Try
nbEnfants = Integer.Parse(args(1))
If nbEnfants < 0 Then
Throw New Exception
End If
Catch
Console.Error.WriteLine(syntaxe2 + "\nArgument nbEnfants incorrect : tapez un entier positif ou nul")
erreur = True
End Try
' stipendio
salaire = 0
Try
salaire = Integer.Parse(args(2))
If salaire < 0 Then
Throw New Exception
End If
Catch
Console.Error.WriteLine(syntaxe2 + "\nArgument salaire incorrect : tapez un entier positif ou nul")
erreur = True
End Try
End If
If Not erreur Then
' i parametri sono corretti - si calcola l'imposta
Console.Out.WriteLine(("impôt=" & objImpôt.calculer(marié = "o", nbEnfants, salaire).ToString + " F"))
End If
End While
End Sub
End Module
Il database utilizzato è un database MySQL denominato DSN mysql-impots:
C:\mysql\bin>mysql --database=impots --user=admimpots --password=mdpimpots
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 5 to server version: 3.23.49-max-debug
Type 'help' for help.
mysql> show tables;
+------------------+
| Tables_in_impots |
+------------------+
| timpots |
+------------------+
mysql> select * from timpots;
+---------+--------+---------+
| limites | coeffR | coeffN |
+---------+--------+---------+
| 12620 | 0 | 0 |
| 13190 | 0.05 | 631 |
| 15640 | 0.1 | 1290.5 |
| 24740 | 0.15 | 2072.5 |
| 31810 | 0.2 | 3309.5 |
| 39970 | 0.25 | 4900 |
| 48360 | 0.3 | 6898 |
| 55790 | 0.35 | 9316.5 |
| 92970 | 0.4 | 12106 |
| 127860 | 0.45 | 16754 |
| 151250 | 0.5 | 23147.5 |
| 172040 | 0.55 | 30710 |
| 195000 | 0.6 | 39312 |
| 0 | 0.65 | 49062 |
+---------+--------+---------+
L'esecuzione del programma di test fornisce i seguenti risultati:
dos>D:\data\devel\vbnet\poly\chap6\impots>vbc /r:system.data.dll /r:microsoft.data.odbc.dll /r:system.dll /t:library impots.vb
dos>vbc /r:impots.dll testimpots.vb
dos>test mysql-impots timpots limites coeffr coeffn
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :o 2 200000
impôt=22506 F
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :n 2 200000
impôt=33388 F
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :o 3 200000
impôt=16400 F
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :n 3 300000
impôt=50082 F
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :n 3 200000
impôt=22506 F
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :
