Skip to content

7. Dostęp do baz danych

7.1. Informacje ogólne

Istnieje wiele baz danych przeznaczonych dla platform Windows. Aby uzyskać do nich dostęp, aplikacje korzystają z programów zwanych sterownikami (drivers).

Na powyższym schemacie sterownik posiada dwa interfejsy:

  • interfejs I1 udostępniany aplikacji
  • interfejs I2 skierowany do bazy danych

Aby uniknąć konieczności przepisywania aplikacji napisanej dla bazy danych B1 w przypadku migracji do innej bazy danych B2, podjęto działania normalizacyjne dotyczące interfejsu I1. W przypadku korzystania z baz danych wykorzystujących „znormalizowane” sterowniki baza B1 zostanie dostarczona wraz ze sterownikiem P1, baza B2 wraz ze sterownikiem P2, a interfejs I1 obu tych sterowników będzie identyczny. Dzięki temu nie będzie konieczne przepisywanie aplikacji. W ten sposób będzie można na przykład przenieść bazę danych ACCESS do bazy danych MySQL bez konieczności wprowadzania zmian w aplikacji.

Istnieją dwa rodzaje standardowych sterowników:

  • sterowniki ODBC (Open DataBase Connectivity)
  • sterowniki OLE i DB (Object Linking and Embedding DataBase)

Sterowniki ODBC umożliwiają dostęp do baz danych. Źródła danych dla sterowników OLE i DB są bardziej zróżnicowane: bazy danych, systemy pocztowe, katalogi... Nie ma żadnych ograniczeń. Każde źródło danych może stać się przedmiotem sterownika OLE lub DB, jeśli zdecyduje się na to wydawca. Korzyści są oczywiście ogromne: uzyskujemy jednolity dostęp do szerokiej gamy danych.

Platforma .NET jest dostarczana z dwoma typami klas dostępu do danych:

  1. klasy SQL i Server.NET
  2. klasy OLE i Db.NET

Pierwsze klasy umożliwiają bezpośredni dostęp do serwera Microsoft SGBD SQL bez pośredniego sterownika. Drugie klasy umożliwiają dostęp do źródeł danych OLE DB.

Image

Platforma .NET jest dostarczana (maj 2002) z trzema sterownikami OLE, DB przeznaczonymi odpowiednio dla: SQL Server, Oracle i Microsoft Jet (Access). Jeśli chcemy pracować z bazą danych, która posiada sterownik ODBC, ale nie posiada sterowników OLE ani DB, nie jest to możliwe. Nie można zatem pracować z SGBD ani MySQL, które (w maju 2002 r.) nie zapewniają sterowników OLE ani DB. Istnieje jednak zestaw klas umożliwiających dostęp do źródeł danych, takich jak ODBC i odbc.net. Nie są one dostarczane w standardzie wraz z SDK i należy je pobrać ze strony internetowej firmy Microsoft. W poniższych przykładach będziemy korzystać głównie z tych klas ODBC, ponieważ większość baz danych w systemie Windows jest dostarczana z takim sterownikiem. Oto na przykład lista sterowników ODBC zainstalowanych na komputerze z systemem Windows 2000 (Menu Start/Ustawienia/Panel sterowania/Narzędzia administracyjne):

Image

Wybieramy ikonę Źródło danych ODBC:

Image

7.2. Dwa tryby korzystania ze źródła danych

Platforma .NET umożliwia korzystanie ze źródła danych na dwa różne sposoby:

  1. tryb online
  2. tryb offline

W trybie online aplikacja

  1. otwiera połączenie ze źródłem danych
  2. współpracuje ze źródłem danych w trybie odczytu/zapisu
  3. zamyka połączenie

W trybie offline aplikacja

  1. nawiązuje połączenie ze źródłem danych
  2. pobiera kopię pamięciową wszystkich lub części danych ze źródła
  3. zamyka połączenie
  4. pracuje z kopią danych w pamięci, umożliwiającą odczyt i zapis
  5. gdy praca zostanie zakończona, otwiera połączenie, wysyła zmodyfikowane dane do źródła danych, aby zostały uwzględnione, a następnie zamyka połączenie

W obu przypadkach to właśnie operacja przetwarzania i aktualizacji danych zajmuje najwięcej czasu. Wyobraźmy sobie, że aktualizacje te są wprowadzane przez użytkownika wprowadzającego dane – operacja ta może trwać kilkadziesiąt minut. Przez cały ten czas, w trybie online, połączenie z bazą danych jest utrzymywane, a zmiany są natychmiast odzwierciedlane. W trybie offline podczas aktualizacji danych nie ma połączenia z bazą danych. Zmiany są wprowadzane wyłącznie w kopii pamięciowej. Są one odzwierciedlane w źródle danych jednorazowo, gdy wszystko zostanie zakończone.

Jakie są zalety i wady obu metod?

  • Połączenie wymaga znacznych zasobów systemowych. Jeśli występuje wiele jednoczesnych połączeń, tryb offline pozwala skrócić ich czas trwania do minimum. Dotyczy to aplikacji internetowych obsługujących tysiące użytkowników.
  • Wadą trybu offline jest skomplikowane zarządzanie równoczesnymi aktualizacjami. Użytkownik U1 pobiera dane w momencie T1 i zaczyna je modyfikować. W momencie T2 użytkownik U2 również uzyskuje dostęp do źródła danych i pobiera te same dane. W międzyczasie użytkownik U1 zmodyfikował niektóre dane, ale nie przesłał ich jeszcze do źródła danych. U2 pracuje zatem z danymi, z których część jest błędna. Klasy .NET oferują rozwiązania pozwalające zarządzać tym problemem, jednak nie jest to łatwe do rozwiązania.
  • W trybie połączonym jednoczesna aktualizacja danych przez wielu użytkowników zazwyczaj nie stanowi problemu. Ponieważ połączenie z bazą danych jest utrzymywane, to sama baza danych zarządza tymi równoczesnymi aktualizacjami. W ten sposób Oracle blokuje wiersz w bazie danych, gdy tylko użytkownik go zmodyfikuje. Pozostanie on zablokowany, a więc niedostępny dla innych użytkowników, dopóki użytkownik, który go zmodyfikował, nie zatwierdzi (commit) swojej zmiany lub jej nie cofnie (rollback).
  • Jeśli dane muszą być przesyłane przez sieć, należy wybrać tryb odłączony. Pozwala on na uzyskanie migawki danych w obiekcie zwanym zestawem danych (dataset), który sam w sobie stanowi bazę danych. Obiekt ten może być przesyłany przez sieć między komputerami.

Najpierw omówimy tryb połączony.

7.3. Dostęp do danych w trybie połączonym

7.3.1. Bazy danych z przykładu

Rozważamy bazę danych o nazwie ACCESS, zwaną articles.mdb, zawierającą tylko jedną tabelę o nazwie ARTICLES o następującej strukturze:

nazwa
typ
kod
4-znakowy kod artykułu
nazwa
nazwa (ciąg znaków)
cena
jego cena (rzeczywista)
stock_actuel
aktualny stan magazynowy (liczba całkowita)
stock_minimum
minimalny stan magazynowy (liczba całkowita), poniżej którego należy uzupełnić zapasy danego artykułu

Jego początkowa zawartość jest następująca:

Image

Będziemy korzystać z tej bazy zarówno w ramach projektu pilotażowego ODBC, jak isterownika OLE DB, aby pokazać podobieństwo obu podejść oraz dlatego, że dysponujemy tymi dwoma typami sterowników dla ACCESS.

Wykorzystamy również bazę danych o nazwie MySQL i DBARTICLES, zawierającą tę samą, jedyną tabelę o nazwie ARTICLES, o tej samej zawartości i dostępnej za pośrednictwem sterownika ODBC, aby pokazać, że aplikacja napisana z myślą o korzystaniu z bazy ACCESS nie wymaga modyfikacji, aby korzystać z bazy MySQL. Dostęp do bazy DBARTICLES ma użytkownik o nazwie admarticles z hasłem mdparticles. Poniższy zrzut ekranu przedstawia zawartość bazy 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

Aby zdefiniować bazę ACCESS jako źródło danych ODBC, wykonaj następujące czynności:

  • uruchom menedżera źródeł danych ODBC zgodnie z powyższą instrukcją i wybierz zakładkę „User” DSN (DSN = nazwa źródła danych)

Image

  • dodaj źródło za pomocą przycisku Add, wskaż, że to źródło jest dostępne za pośrednictwem sterownika Access i wykonaj Terminer:

Image

  • Nadaj źródłu danych nazwę articles-access, wprowadź dowolny opis i użyj przycisku Sélectionner, aby wskazać plik .mdb z bazy. Zakończ na OK.

Image

Nowe źródło danych pojawi się wówczas na liście źródeł użytkownika DSN:

Image

Aby zdefiniować bazę danych MySQL DBARTICLES jako źródło danych ODBC, wykonaj następujące czynności:

  • uruchom menedżera źródeł danych ODBC zgodnie z powyższą instrukcją i wybierz zakładkę „User” (Użytkownik) DSN. Dodaj nowe źródło danych o nazwie Add i wybierz sterownik ODBC z MySQL.

Image

  • Wprowadź Terminer. Pojawi się wówczas strona konfiguracji źródła MySQL:

54321

Image

  • w polu (1) nadajemy nazwę naszemu źródłu danych ODBC
  • w polu (2) należy wskazać komputer, na którym znajduje się serwer MySQL. W tym miejscu wpisujemy localhost, aby wskazać, że znajduje się on na tym samym komputerze co nasza aplikacja. Gdyby serwer MySQL znajdował się na zdalnym komputerze M, wpisalibyśmy tutaj jego nazwę, a nasza aplikacja działałaby wówczas z zdalną bazą danych bez konieczności wprowadzania zmian.
  • W polu (3) wpisujemy nazwę bazy danych. Tutaj nazywa się ona DBARTICLES.
  • W polu (4) wpisujemy nazwę użytkownika admarticles, a w polu (5) hasło mdparticles.

7.3.2. Korzystanie ze sterownika ODBC

W aplikacji korzystającej z bazy danych w trybie połączonym zazwyczaj wykonuje się następujące kroki:

  1. Połączenie z bazą danych
  2. Wysyłanie zapytań SQL do bazy
  3. Odbiór i przetwarzanie wyników tych zapytań
  4. Zamknięcie połączenia

Kroki 2 i 3 są wykonywane wielokrotnie, a zamknięcie połączenia następuje dopiero po zakończeniu pracy z bazą danych. Jest to stosunkowo klasyczny schemat, z którym być może są Państwo zaznajomieni, jeśli korzystali Państwo z bazy danych w trybie interaktywnym. Kroki te są takie same niezależnie od tego, czy baza danych jest wykorzystywana za pośrednictwem sterownika ODBC, czy też sterownika OLE lub DB. Poniżej przedstawiamy przykład z wykorzystaniem klas .NET do zarządzania źródłami danych ODBC. Program nosi nazwę liste i przyjmuje jako parametr nazwę DSN źródła danych ODBC zawierającego tabelę ARTICLES. Następnie wyświetla zawartość tej tabeli:

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

Na podstawie powyższych wyników widać, że program wyświetlił zawartość zarówno bazy ACCESS, jak i bazy MySQL. Przyjrzyjmy się teraz kodowi tego programu:


' opcje
Option Explicit On 
Option Strict On

' przestrzenie nazw
Imports System
Imports System.Data
Imports Microsoft.Data.Odbc
Imports Microsoft.VisualBasic

Module db1
    Sub main(ByVal args As String())
        ' konsola aplikacji
        ' wyświetla zawartość tabeli ARTICLES z bazy DSN 
        ',  której nazwa została przekazana jako parametr
        Const syntaxe As String = "syntaxe : pg dsnArticles"
        Const tabArticles As String = "articles"        ' la table des articles

        ' sprawdzanie parametrów
        ' czy mamy 1 parametr
        If args.Length <> 1 Then
            ' komunikat o błędzie
            Console.Error.WriteLine(syntaxe)
            ' koniec
            Environment.Exit(1)
        End If

        ' pobieranie parametru
        Dim dsnArticles As String = args(0)                ' la base DSN
        ' przygotowanie połączenia z bazą danych
        Dim articlesConn As OdbcConnection = Nothing        ' la connexion
        Dim myReader As OdbcDataReader = Nothing            ' le lecteur de données

        ' próba uzyskania dostępu do bazy danych
        Try
            ' ciąg połączenia z bazą danych
            Dim connectString As String = "DSN=" + dsnArticles + ";"
            articlesConn = New OdbcConnection(connectString)
            articlesConn.Open()

            ' wykonanie polecenia SQL
            Dim sqlText As String = "select * from " + tabArticles
            Dim myOdbcCommand As New OdbcCommand(sqlText)
            myOdbcCommand.Connection = articlesConn
            myReader = myOdbcCommand.ExecuteReader()

            ' Analiza pobranej tabeli
            ' wyświetlanie kolumn
            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))

            ' wyświetlanie danych
            While myReader.Read()
                ' przetwarzanie bieżącego wiersza
                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
            ' zamknięcie czytnika
            myReader.Close()
            ' zamknięcie połączenia
            articlesConn.Close()
        End Try
    End Sub
End Module

Klasy służące do zarządzania źródłami ODBC znajdują się w przestrzeni nazw Microsoft.Data.Odbc, którą należy zatem zaimportować. Ponadto pewna liczba klas znajduje się w przestrzeni nazw System.Data.


Imports System.Data
Imports Microsoft.Data.Odbc

Przestrzenie nazw używane przez program znajdują się w różnych zestawach. Program kompiluje się za pomocą następującego polecenia:

dos>vbc /r:microsoft.data.odbc.dll /r:microsoft.visualbasic.dll /r:system.dll /r:system.data.dll db1.vb

7.3.2.1. Faza nawiązywania połączenia

Połączenie o nazwie ODBC wykorzystuje klasę OdbcConnection. Konstruktor tej klasy przyjmuje jako parametr tzw. ciąg połączenia. Jest to ciąg znaków, który określa wszystkie parametry niezbędne do nawiązania połączenia z bazą danych. Parametrów tych może być bardzo wiele, dlatego ciąg ten może być złożony. Ciąg ma postać "param1=valeur1;param2=valeur2;...;paramj=valeurj;". Oto kilka możliwych parametrów paramj:

uid
nazwa użytkownika, który będzie miał dostęp do bazy danych
password
hasło tego użytkownika
dsn
nazwa bazy danych DSN, jeśli taka istnieje
data source
nazwa bazy danych, do której uzyskano dostęp
...
 

Jeśli zdefiniuje się źródło danych jako źródło danych ODBC za pomocą menedżera źródeł danych ODBC, parametry te zostały już podane i zapisane. Wystarczy wówczas przekazać parametr DSN, który określa nazwę źródła danych DSN. Tak właśnie postąpiono w tym przypadku:


         ' przygotowanie połączenia z bazą danych
        Dim articlesConn As OdbcConnection = Nothing        ' la connexion
        Dim myReader As OdbcDataReader = Nothing        ' le lecteur de données
        Try
             ' próba uzyskania dostępu do bazy danych
             ' ciąg połączenia z bazą danych
            Dim connectString As String = "DSN=" + dsnArticles + ";"
            articlesConn = New OdbcConnection(connectString)
            articlesConn.Open()

Po utworzeniu obiektu OdbcConnection nawiązuje się połączenie za pomocą metody Open. Nawiązanie połączenia może zakończyć się niepowodzeniem, podobnie jak każda inna operacja wykonywana w bazie danych. Dlatego cały kod dostępu do bazy danych znajduje się w obiekcie try-catch. Po nawiązaniu połączenia można wysyłać zapytania SQL do bazy danych.

7.3.2.2. Wysyłanie zapytań SQL

Aby wysłać zapytania SQL, potrzebujemy obiektu Command, a dokładniej obiektu OdbcCommand, ponieważ korzystamy ze źródła danych ODBC. Klasa OdbcCommand posiada kilka konstruktorów:

  • OdbcCommand(): tworzy pusty obiekt Command. Aby z niego skorzystać, należy później określić różne właściwości:
    • CommandText: tekst zapytania SQL, które ma zostać wykonane
    • Connection: obiekt OdbcConnection reprezentujący połączenie z bazą danych, w której zostanie wykonane zapytanie
    • CommandType: typ zapytania SQL. Istnieją trzy możliwe wartości
  1. CommandType.Text: właściwość CommandText zawiera tekst zapytania SQL (wartość domyślna)
  2. CommandType.StoredProcedure: właściwość CommandText zawiera nazwę procedury przechowywanej w bazie danych
  3. CommandType.TableDirect: właściwość CommandText zawiera nazwę tabeli T. Odpowiada zapytaniu select * from T. Istnieje tylko dla sterowników OLE i DB.
  • OdbcCommand(string sqlText): parametr sqlText zostanie przypisany do właściwości CommandText. Jest to tekst zapytania SQL, które ma zostać wykonane. Połączenie należy określić we właściwości Connection.
  • OdbcCommand(string sqlText, OdbcConnection połączenie): parametr sqlText zostanie przypisany do właściwości CommandText, a parametr connexion do właściwości Connection.

Aby wysłać zapytanie SQL, dostępne są dwie metody:

  • OdbcdataReader ExecuteReader(): wysyła zapytanie SELECT z CommandText do połączenia Connection i tworzy obiekt OdbcDataReader umożliwiającydostęp do wszystkich wierszy tabeli wynikowej select
  • int ExecuteNOnQuery(): wysyła żądanie aktualizacji (INSERT, UPDATE, DELETE) z CommandText do połączenia Connection i zwraca liczbę wierszy, których dotyczy ta aktualizacja.

W naszym przykładzie, po otwarciu połączenia z bazą danych, wysyłamy zapytanie SQL SELECT w celu uzyskania zawartości tabeli ARTICLES:


             ' wykonanie polecenia SQL
            Dim sqlText As String = "select * from " + tabArticles
            Dim myOdbcCommand As New OdbcCommand(sqlText)
            myOdbcCommand.Connection = articlesConn
            myReader = myOdbcCommand.ExecuteReader()

Zapytanie jest zazwyczaj zapytaniem typu:


    select col1, col2,... from table1, table2,...
    where condition
    order by expression
    ...

Obowiązkowe są tylko słowa kluczowe z pierwszego wiersza, pozostałe są opcjonalne. Istnieją również inne słowa kluczowe, które nie zostały tutaj przedstawione.

  1. Dokonuje się połączenia ze wszystkimi tabelami, które znajdują się za słowem kluczowym „from”
  2. Zachowane są wyłącznie kolumny występujące po słowie kluczowym „select”
  3. Zachowane są tylko te wiersze, które spełniają warunek określony przez słowo kluczowe „where”
  4. Wynikowe wiersze uporządkowane zgodnie z wyrażeniem zawartym w słowie kluczowym „order by” tworzą wynik zapytania.

Wynikiem zapytania select jest tabela. Jeśli weźmiemy pod uwagę poprzednią tabelę ARTICLES i chcemy uzyskać nazwy artykułów, których aktualny stan magazynowy jest poniżej minimalnego progu, wpiszemy:


    select nom from articles where stock_actuel<stock_minimum

Jeśli chcemy uzyskać wyniki w porządku alfabetycznym według nazw, wpiszemy:


    select nom from articles where stock_actuel<stock_minimum order by nom

7.3.2.3. Wykorzystanie wyniku zapytania SELECT

Wynikiem zapytania SELECT w trybie offline jest obiekt DataReader, a w tym przypadku obiekt OdbcDataReader. Obiekt ten pozwala na sekwencyjne pobieranie wszystkich wierszy wyniku oraz uzyskanie informacji o kolumnach tych wyników. Przyjrzyjmy się kilku właściwościom i metodom tej klasy:

FieldCount
liczba kolumn w tabeli
Item
Item(i) reprezentuje kolumnę nr i w bieżącym wierszu wyniku
XXX GetXXX(i)
wartość kolumny nr i w bieżącym wierszu przedstawiona jako typ XXX (Int16, Int32, Int64, Double, String, Boolean, ...)
string GetName(i)
nazwa kolumny nr i
Close()
zamyka obiekt OdbcdataReader i zwalnia powiązane zasoby
bool Read()
przechodzi o jeden wiersz dalej w tabeli wyników. Zwraca wartość fałsz, jeśli nie jest to możliwe. Nowy wiersz staje się bieżącym wierszem czytnika.

Przetwarzanie wyniku funkcji select odbywa się zazwyczaj sekwencyjnie, podobnie jak w przypadku plików tekstowych: można jedynie przesuwać się do przodu w tabeli, a nie do tyłu:


While myReader.Read()
    ' mamy wiersz – przetwarzamy go
    ....
     ' kolejny wiersz
end while

Te wyjaśnienia wystarczą, aby zrozumieć poniższy kod z naszego przykładu:


            ' Przetwarzanie pobranej tabeli
            ' wyświetlanie kolumn
            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))

            ' wyświetlanie danych
            While myReader.Read()
                ' przetwarzanie bieżącego wiersza
                ligne = ""
                For i = 0 To myReader.FieldCount - 1
                    ligne += myReader(i).ToString + " "
                Next i
                Console.WriteLine(ligne)
            End While

Jedyna trudność dotyczy instrukcji, w której łączone są wartości z różnych kolumn bieżącego wiersza:


                For i = 0 To myReader.FieldCount - 1
                    ligne += myReader(i).ToString + " "
                Next i

Zapis wiersz+=myReader(i).ToString jest przekształcany na wiersz+=myReader.Item(i).ToString(), gdzie Item(i) jest wartością kolumny i bieżącego wiersza.

7.3.2.4. Zwolnienie zasobów

Klasy OdbcReader i OdbcConnection posiadają metodę Close(), która zwalnia zasoby powiązane z zamkniętymi obiektami.

                 ' zamknięcie czytnika
                myReader.Close()
                 ' zamknięcie połączenia
                articlesConn.Close()

7.3.3. Korzystanie ze sterownika OLE DB

Wróćmy do tego samego przykładu, tym razem z bazą danych, do której dostęp uzyskuje się za pomocą sterownika OLE DB. Platforma .NET udostępnia taki sterownik dla baz danych ACCESS. Wykorzystamy więc tę samą bazę danych articles.mdb, co poprzednio. Chcemy tutaj pokazać, że nawet jeśli zmieniają się klasy, koncepcje pozostają takie same:

  • Połączenie jest reprezentowane przez obiekt OleDbConnection
  • zapytanie SQL jest wysyłane za pomocą obiektu OleDbCommand
  • jeśli zapytanie to jest klauzulą o nazwie SELECT, w odpowiedzi otrzymamy obiekt o nazwie OleDbDataReader, umożliwiający dostęp do wierszy tabeli wynikowej

Klasy te znajdują się w przestrzeni nazw System.Data.OleDb. Powyższy program można łatwo zmodyfikować, aby obsługiwał bazę OLE DB:

  • wszędzie zastępuje się OdbcXX na OleDbXX
  • zmienia się ciąg połączenia. W przypadku bazy danych o nazwie ACCESS bez loginu i hasła ciąg połączenia wygląda następująco: Provider=Microsoft.JET.OLEDB.4.0;Data Source=[fichier.mdb]. Częścią tego ciągu, którą można dostosować, jest nazwa pliku ACCESS, który ma zostać użyty. Zmodyfikujemy nasz program tak, aby przyjmował jako parametr nazwę tego pliku.
  • Przestrzeń nazw, którą należy zaimportować, to teraz System.Data.OleDb.

Nasz program wygląda teraz następująco:


' opcje
Option Explicit On 
Option Strict On

' przestrzenie nazw
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)

        ' konsola aplikacji
        ' wyświetla zawartość tabeli ARRTICLES z bazy DSN 
        ',  której nazwa została przekazana jako parametr
        Const syntaxe As String = "syntaxe : pg base_access_articles"
        Const tabArticles As String = "articles"        ' la table des articles

        ' sprawdzanie parametrów
        ' czy mamy 1 parametr
        If args.Length <> 1 Then
            ' komunikat o błędzie
            Console.Error.WriteLine(syntaxe)
            ' koniec
            Environment.Exit(1)
        End If

        ' pobieranie parametru
        Dim dbArticles As String = args(0)        ' la base de données

        ' przygotowanie połączenia z bazą danych
        Dim articlesConn As OleDbConnection = Nothing        ' la connexion
        Dim myReader As OleDbDataReader = Nothing        ' le lecteur de données

        ' próba uzyskania dostępu do bazy danych

        Try
            ' ciąg połączenia z bazą danych
            Dim connectString As String = "Provider=Microsoft.JET.OLEDB.4.0;Data Source=" + dbArticles + ";"
            articlesConn = New OleDbConnection(connectString)
            articlesConn.Open()

            ' wykonanie polecenia SQL
            Dim sqlText As String = "select * from " + tabArticles
            Dim myOleDbCommand As New OleDbCommand(sqlText)
            myOleDbCommand.Connection = articlesConn
            myReader = myOleDbCommand.ExecuteReader()

            ' Analiza pobranej tabeli
            ' wyświetlanie kolumn
            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))
            ' wyświetlanie danych
            While myReader.Read()
                ' przetwarzanie bieżącego wiersza
                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
            ' zamknięcie czytnika
                myReader.Close()
                ' zamknięcie połączenia
                articlesConn.Close()
        End Try
        ' koniec
        Environment.Exit(0)
    End Sub
End Module

Uzyskane wyniki:

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. Aktualizacja tabeli

W poprzednich przykładach ograniczaliśmy się do wyświetlania zawartości tabeli. Modyfikujemy nasz program do zarządzania bazą artykułów, aby umożliwić mu wprowadzanie zmian w tej bazie. Program nosi nazwę sql. Jako parametr przekazujemy mu nazwę DSN bazy artykułów, którą ma zarządzać. Użytkownik wpisuje bezpośrednio z klawiatury polecenia SQL, które program wykonuje, jak pokazują poniższe wyniki uzyskane na bazie artykułów 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

Program przedstawia się następująco:


' opcje
Option Explicit On 
Option Strict On

' przestrzenie nazw
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)

        ' konsola aplikacji
        ' wykonuje zapytania SQL wpisane z klawiatury w 
        ' tabeli ARTICLES w bazie DSN, której nazwa jest przekazywana jako parametr
        Const syntaxe As String = "syntaxe : pg dsnArticles"

        ' weryfikacja parametrów
        ' czy mamy 2 parametry
        If args.Length <> 1 Then
            ' komunikat o błędzie
            Console.Error.WriteLine(syntaxe)
            ' koniec
            Environment.Exit(1)
        End If        'if
        ' pobieramy parametr
        Dim dsnArticles As String = args(0)
        ' ciąg połączenia z bazą danych
        Dim connectString As String = "DSN=" + dsnArticles + ";"

        ' przygotowanie połączenia z bazą danych
        Dim articlesConn As OdbcConnection = Nothing
        Dim sqlCommand As OdbcCommand = Nothing
        Try
            ' próba uzyskania dostępu do bazy danych
            articlesConn = New OdbcConnection(connectString)
            articlesConn.Open()
            ' tworzy się obiekt polecenia
            sqlCommand = New OdbcCommand("", articlesConn)
            'try
        Catch ex As Exception
            ' komunikat o błędzie
            Console.Error.WriteLine(("Erreur d'exploitation de la base de données (" + ex.Message + ")"))
            ' zwolnienie zasobów
            Try
                articlesConn.Close()
            Catch
            End Try
            Environment.Exit(2)
        End Try        'catch
        ' tworzy się słownik akceptowanych poleceń SQL
        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

        ' odczyt i wykonanie poleceń SQL wpisanych z klawiatury
        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+")
        ' pętla wprowadzania i wykonywania poleceń SQL wpisanych z klawiatury
        While True
            ' na początku nie ma błędów
            Dim erreur As Boolean = False
            ' wysłanie zapytania
            Console.Out.Write(ControlChars.Lf + "Requête SQL (fin pour arrêter) : ")
            requête = Console.In.ReadLine().Trim().ToLower()
            ' koniec?
            If requête = "fin" Then
                Exit While
            End If
            ' rozbijamy zapytanie na pola
            champs = modèle.Split(requête)
            ' czy zapytanie jest prawidłowe?
            If champs.Length = 0 Or Not dicoCommandes.ContainsKey(champs(0)) Then
                ' komunikat o błędzie
                Console.Error.WriteLine("Requête invalide. Utilisez select, insert, update, delete")
                ' kolejne zapytanie
                erreur = True
            End If
            If Not erreur Then
                ' przygotowywanie obiektu Command do wykonania zapytania
                sqlCommand.CommandText = requête
                ' wykonanie zapytania
                Try
                    If champs(0) = "select" Then
                        executeSelect(sqlCommand)
                    Else
                        executeUpdate(sqlCommand)
                    End If
                Catch ex As Exception
                    ' komunikat o błędzie
                    Console.Error.WriteLine(("Erreur d'exploitation de la base de données (" + ex.Message + ")"))
                End Try
            End If
        End While
        ' zwolnienie zasobów
        Try
            articlesConn.Close()
        Catch
        End Try
        Environment.Exit(0)
    End Sub

    ' wykonanie zapytania o aktualizację
    Sub executeUpdate(ByVal sqlCommand As OdbcCommand)
        ' wykonuje sqlCommand, zapytanie o aktualizację
        Dim nbLignes As Integer = sqlCommand.ExecuteNonQuery()
        ' wyświetlanie
        Console.Out.WriteLine(("Il y a eu " & nbLignes & " ligne(s) modifiée(s)"))
    End Sub

    ' wykonanie zapytania SELECT
    Sub executeSelect(ByVal sqlCommand As OdbcCommand)
        ' wykonuje sqlCommand, zapytanie SELECT
        Dim myReader As OdbcDataReader = sqlCommand.ExecuteReader()
        ' Przetwarzanie pobranej tabeli
        ' wyświetlanie kolumn
        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))
        ' wyświetlanie danych
        While myReader.Read()
            ' przetwarzanie bieżącego wiersza
            ligne = ""
            For i = 0 To myReader.FieldCount - 1
                ligne += myReader(i).ToString + " "
            Next i
            ' wyświetlanie
            Console.WriteLine(ligne)
        End While
        ' zwolnienie zasobów
        myReader.Close()
    End Sub
End Module

W tym miejscu omówimy jedynie zmiany w stosunku do poprzedniego programu:

  • Tworzymy słownik akceptowanych poleceń SQL:

         ' tworzenie słownika akceptowanych poleceń SQL
        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

co pozwala nam następnie w prosty sposób sprawdzić, czy pierwsze słowo (pole [0]) wpisanej zapytania jest jednym z czterech akceptowanych poleceń:


             ' czy zapytanie jest prawidłowe?
            If champs.Length = 0 Or Not dicoCommandes.ContainsKey(champs(0)) Then
                ' komunikat o błędzie
                Console.Error.WriteLine("Requête invalide. Utilisez select, insert, update, delete")
                 ' kolejne zapytanie
                erreur = True
            End If            'if
  • Wcześniej zapytanie zostało podzielone na pola za pomocą metody Split klasy RegEx:

        Dim modèle As New Regex("\s+")
....
            ' rozbijanie zapytania na pola
            champs = modèle.Split(requête)

Słowa składające się na zapytanie mogą być oddzielone dowolną liczbą spacji.

  • Wykonanie zapytania select nie wykorzystuje tej samej metody, co zapytanie dotyczące aktualizacji (insert, update, delete). Należy zatem przeprowadzić test i wykonać inną funkcję dla każdego z tych dwóch przypadków:

                 ' przygotowywanie obiektu Command do wykonania zapytania
                sqlCommand.CommandText = requête
                 ' wykonanie zapytania
                Try
                    If champs(0) = "select" Then
                        executeSelect(sqlCommand)
                    Else
                        executeUpdate(sqlCommand)
                    End If                    'try
                Catch ex As Exception
                    ' komunikat o błędzie
                    Console.Error.WriteLine(("Erreur d'exploitation de la base de données (" + ex.Message + ")"))
                End Try

Wykonanie zapytania SQL może spowodować wyjątek, który jest tutaj obsługiwany.

  • Funkcja executeSelect podsumowuje wszystko, co zostało omówione w poprzednich przykładach.
  • Funkcja executeUpdate wykorzystuje metodę ExecuteNonQuery klasy OdbcCommand, która zwraca liczbę wierszy, na które ma wpływ polecenie.

7.3.5. IMPOTS

Wykorzystujemy obiekt impôt utworzony w poprzednim rozdziale:


' opcje
Option Strict On
Option Explicit On 

' przestrzenie nazw
Imports System

Public Class impôt
    ' dane niezbędne do obliczenia podatku
    ' pochodzą ze źródła zewnętrznego
    Private limites(), coeffR(), coeffN() As Decimal

    ' producent
    Public Sub New(ByVal LIMITES() As Decimal, ByVal COEFFR() As Decimal, ByVal COEFFN() As Decimal)
        ' sprawdza się, czy wszystkie trzy tabele mają ten sam rozmiar
        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
        ' w porządku
        Me.limites = LIMITES
        Me.coeffR = COEFFR
        Me.coeffN = COEFFN
    End Sub

    ' obliczenie podatku
    Public Function calculer(ByVal marié As Boolean, ByVal nbEnfants As Integer, ByVal salaire As Integer) As Long
        ' obliczanie liczby udziałów
        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
        ' obliczanie dochodu podlegającego opodatkowaniu i współczynnika rodzinnego
        Dim revenu As Decimal = 0.72D * salaire
        Dim QF As Decimal = revenu / nbParts
        ' obliczanie podatku
        limites((limites.Length - 1)) = QF + 1
        Dim i As Integer = 0
        While QF > limites(i)
            i += 1
        End While
        ' zwrot wyniku
        Return CLng(revenu * coeffR(i) - nbParts * coeffN(i))
    End Function
End Class

Dodajemy do niego nową metodę konstruktora, umożliwiającą zainicjowanie tablic limites, coeffR, coeffN na podstawie bazy danych ODBC:


Imports System.Data
Imports Microsoft.Data.Odbc
Imports System.Collections
...


    ' konstruktor 2
    Public Sub New(ByVal DSNimpots As String, ByVal Timpots As String, ByVal colLimites As String, ByVal colCoeffR As String, ByVal colCoeffN As String)
        ' inicjuje trzy tabele limitów: coeffR, coeffN na podstawie
        ' zawartości tabeli Timpots z bazy danych ODBC DSNimpots
        ' colLimites, colCoeffR, colCoeffN to trzy kolumny tej tabeli
        ' może wygenerować wyjątek
        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
        ' zapytanie SELECT
        Dim selectCommand As String = "select " + colLimites + "," + colCoeffR + "," + colCoeffN + " from " + Timpots
        ' tabele służące do pobierania danych
        Dim tLimites As New ArrayList
        Dim tCoeffR As New ArrayList
        Dim tCoeffN As New ArrayList

        ' próbuje się uzyskać dostęp do bazy danych
        impotsConn = New OdbcConnection(connectString)
        impotsConn.Open()
        ' tworzy się obiekt polecenia
        sqlCommand = New OdbcCommand(selectCommand, impotsConn)
        ' wykonuje się zapytanie
        Dim myReader As OdbcDataReader = sqlCommand.ExecuteReader()
        ' Analiza pobranej tabeli
        While myReader.Read()
            ' dane z bieżącego wiersza są umieszczane w tablicach
            tLimites.Add(myReader(colLimites))
            tCoeffR.Add(myReader(colCoeffR))
            tCoeffN.Add(myReader(colCoeffN))
        End While
        ' zwolnienie zasobów
        myReader.Close()
        impotsConn.Close()

        ' tabele dynamiczne są umieszczane w tabelach statycznych
        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

Program testowy działa w następujący sposób: jako argumenty otrzymuje parametry, które mają zostać przekazane do konstruktora klasy impôt. Po utworzeniu obiektu impôt dokonuje obliczeń podatku do zapłaty:


Option Explicit On 
Option Strict On

' przestrzenie nazw
Imports System
Imports Microsoft.VisualBasic

' strona testowa
Module testimpots
    Sub Main(ByVal arguments() As String)
        ' interaktywny program do obliczania podatku
        ' użytkownik wprowadza trzy dane za pomocą klawiatury: żonaty nbEnfants wynagrodzenie
        ' następnie program wyświetla podatek do zapłaty
        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"

        ' sprawdzenie parametrów programu
        If arguments.Length <> 5 Then
            ' komunikat o błędzie
            Console.Error.WriteLine(syntaxe1)
            ' koniec
            Environment.Exit(1)
        End If        'if
        ' pobieranie argumentów
        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)

        ' tworzenie obiektu podatku
        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

        ' pętla nieskończona
        While True
            ' na początku brak błędów
            Dim erreur As Boolean = False

            ' żądane są parametry obliczenia podatku
            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()

            ' co należy zrobić?
            If paramètres Is Nothing Or paramètres = "" Then
                Exit While
            End If

            ' sprawdzanie liczby argumentów w wprowadzonej linii
            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
                ' sprawdzanie poprawności parametrów
                ' żonaty
                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
                ' wynagrodzenie
                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
                ' parametry są poprawne – obliczamy podatek
                Console.Out.WriteLine(("impôt=" & objImpôt.calculer(marié = "o", nbEnfants, salaire).ToString + " F"))
            End If
        End While
    End Sub
End Module

Wykorzystywana baza to baza o nazwie MySQL, o nazwie 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 |
+---------+--------+---------+

Uruchomienie programu testowego daje następujące wyniki:

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 :