Skip to content

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:

  1. le classi SQL e Server.NET
  2. 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.

Image

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):

Image

Si seleziona l’icona Origine dati ODBC:

Image

7.2. Le due modalità di utilizzo di una fonte dati

La piattaforma .NET consente di utilizzare una fonte dati in due modi diversi:

  1. modalità connessa
  2. modalità offline

In modalità connessa, l'applicazione

  1. apre una connessione con la fonte dati
  2. opera con la fonte dati in modalità lettura/scrittura
  3. chiude la connessione

In modalità offline, l'applicazione

  1. apre una connessione con la fonte di dati
  2. ottiene una copia in memoria di tutti o parte dei dati della fonte
  3. chiude la connessione
  4. lavora con la copia in memoria dei dati in lettura/scrittura
  5. 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:

Image

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)

Image

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

Image

  • 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.

Image

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

Image

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.

Image

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

54321

Image

  • 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:

  1. Connessione al database
  2. Invio delle query SQL al database
  3. Ricezione ed elaborazione dei risultati di tali query
  4. 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:

uid
nome di un utente che accederà al database
password
password di tale utente
dsn
nome del database DSN, se presente
data source
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
  1. CommandType.Text: la proprietà CommandText contiene il testo di una query SQL (valore predefinito)
  2. CommandType.StoredProcedure: la proprietà CommandText contiene il nome di una procedura memorizzata nel database
  3. 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.

  1. Viene eseguito un join con tutte le tabelle che seguono la parola chiave from
  2. Vengono conservate solo le colonne che seguono la parola chiave "select"
  3. Vengono conservate solo le righe che soddisfano la condizione della parola chiave `where`
  4. 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:

FieldCount
il numero di colonne della tabella
Item
Item(i) rappresenta la colonna n. i della riga corrente del risultato
XXX GetXXX(i)
il valore della colonna n. i della riga corrente, rappresentato come tipo XXX (Int16, Int32, Int64, Double, String, Boolean, ...)
string GetName(i)
nome della colonna n. i
Close()
chiude l'oggetto OdbcdataReader e libera le risorse associate
bool Read()
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.

                 ' chiusura lettore
                myReader.Close()
                 ' chiusura della connessione
                articlesConn.Close()

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 :