7. Acceso a las bases de datos
7.1. Generalidades
Existen numerosas bases de datos para las plataformas Windows. Para acceder a ellas, las aplicaciones pasan a través de programas denominados controladores (drivers).
![]() |
En el esquema anterior, el controlador presenta dos interfaces:
- la interfaz I1, que se presenta a la aplicación
- la interfaz I2 hacia la base de datos
Para evitar que una aplicación escrita para una base de datos B1 tenga que reescribirse si se migra a una base de datos B2 diferente, se ha llevado a cabo un esfuerzo de normalización en la interfaz I1. Si se utilizan bases de datos que emplean controladores «normalizados», la base B1 se suministrará con un controlador P1, la base B2 con un controlador P2, y la interfaz I1 de estos dos controladores será idéntica. Por lo tanto, no será necesario reescribir la aplicación. De este modo, por ejemplo, se podrá migrar una base de datos ACCESS a una base de datos MySQL sin cambiar la aplicación.
Existen dos tipos de controladores estandarizados:
- los controladores ODBC (Open DataBase Connectivity)
- los controladores OLE y DB (Object Linking and Embedding DataBase)
Los controladores ODBC permiten el acceso a bases de datos. Las fuentes de datos para los controladores OLE y DB son más variadas: bases de datos, sistemas de mensajería, directorios... No hay límite. Cualquier fuente de datos puede ser objeto de un controlador OLE DB si así lo decide un desarrollador. El interés es, evidentemente, grande: se dispone de un acceso uniforme a una gran variedad de datos.
La plataforma .NET se suministra con dos tipos de clases de acceso a datos:
- las clases SQL y Server.NET
- las clases OLE y Db.NET
Las primeras clases permiten el acceso directo al servidor SGBD SQL de Microsoft sin controlador intermedio. Las segundas permiten el acceso a las fuentes de datos OLE DB.

La plataforma .NET se suministra (mayo de 2002) con tres controladores OLE DB para, respectivamente: SQL Server, Oracle y Microsoft Jet (Access). Si se desea trabajar con una base de datos que tenga un controlador ODBC pero no un controlador OLE ni DB, no es posible. Por lo tanto, no se puede trabajar con SGBD MySQL, que (en mayo de 2002) no proporciona los controladores OLE DB. Sin embargo, existe una serie de clases que permiten el acceso a las fuentes de datos ODBC, las clases odbc.net. No se incluyen de serie con el SDK y hay que descargarlas de la página web de Microsoft. En los ejemplos que siguen, utilizaremos principalmente estas clases ODBC, ya que la mayoría de las bases de datos en Windows se suministran con un controlador de este tipo. A continuación se muestra, por ejemplo, una lista de los controladores ODBC instalados en un equipo con Windows 2000 (Menú Inicio/Configuración/Panel de control/Herramientas administrativas):

Seleccionamos el icono Fuente de datos ODBC:

7.2. Los dos modos de funcionamiento de una fuente de datos
La plataforma .NET permite utilizar una fuente de datos de dos maneras diferentes:
- modo conectado
- modo sin conexión
En el modo conectado, la aplicación
- abre una conexión con la fuente de datos
- trabaja con la fuente de datos en modo lectura/escritura
- cierra la conexión
En modo desconectado, la aplicación
- abre una conexión con la fuente de datos
- obtiene una copia en memoria de todos o parte de los datos de la fuente
- cierra la conexión
- trabaja con la copia en memoria de los datos en modo lectura/escritura
- cuando el trabajo ha finalizado, abre una conexión, envía los datos modificados a la fuente de datos para que los incorpore y cierra la conexión
En ambos casos, es la operación de explotación y actualización de los datos lo que lleva tiempo. Imaginemos que estas actualizaciones las realiza un usuario que introduce datos; esta operación puede llevar decenas de minutos. Durante todo ese tiempo, en modo conectado, se mantiene la conexión con la base de datos y los cambios se reflejan inmediatamente. En modo desconectado, no hay conexión con la base de datos durante la actualización de los datos. Los cambios se realizan únicamente en la copia en memoria. Se reflejan en la fuente de datos de una sola vez cuando todo ha finalizado.
¿Cuáles son las ventajas y desventajas de ambos métodos?
- Una conexión consume muchos recursos del sistema. Si hay muchas conexiones simultáneas, el modo desconectado permite reducir su duración al mínimo. Este es el caso de las aplicaciones web con miles de usuarios.
- La desventaja del modo desconectado es la delicada gestión de las actualizaciones simultáneas. El usuario U1 obtiene datos en el momento T1 y comienza a modificarlos. En el momento T2, el usuario U2 también accede a la fuente de datos y obtiene los mismos datos. Mientras tanto, el usuario U1 ha modificado algunos datos, pero aún no los ha transmitido a la fuente de datos. Por lo tanto, U2 trabaja con datos, algunos de los cuales son erróneos. Las clases .NET ofrecen soluciones para gestionar este problema, pero no es fácil de resolver.
- En modo conectado, la actualización simultánea de datos por parte de varios usuarios no suele suponer ningún problema. Al mantenerse la conexión con la base de datos, es la propia base de datos la que gestiona estas actualizaciones simultáneas. Así, Oracle bloquea una línea de la base de datos en cuanto un usuario la modifica. Permanecerá bloqueada, y por tanto inaccesible para los demás usuarios, hasta que quien la haya modificado valide (commit) su modificación o la anule (rollback).
- Si los datos deben circular por la red, hay que elegir el modo desconectado. Este permite obtener una instantánea de los datos en un objeto denominado conjunto de datos (dataset), que representa una base de datos en sí mismo. Este objeto puede circular por la red entre máquinas.
En primer lugar, estudiaremos el modo conectado.
7.3. Acceso a los datos en modo conectado
7.3.1. Las bases de datos del ejemplo
Consideramos una base de datos ACCESS llamada articles.mdb y que solo tiene una tabla llamada ARTICLES con la siguiente estructura:
nombre | tipo |
código | código del artículo de 4 caracteres |
nombre | su nombre (cadena de caracteres) |
precio | su precio (real) |
stock_actuel | su stock actual (entero) |
stock_minimum | el stock mínimo (entero) por debajo del cual hay que reponer el artículo |
Su contenido inicial es el siguiente:

Utilizaremos esta base tanto a través de un piloto ODBC comoun controlador OLE DB para mostrar la similitud de ambos enfoques y porque disponemos de estos dos tipos de controladores para ACCESS.
También utilizaremos una base de datos MySQL DBARTICLES que contiene la misma tabla única ARTICLES, el mismo contenido y a la que se accede a través de un controlador ODBC, con el fin de demostrar que la aplicación escrita para explotar la base ACCESS no tiene que modificarse para utilizar la base MySQL. Se puede acceder a la base DBARTICLES mediante un usuario llamado admarticles con la contraseña mdparticles. La siguiente captura de pantalla muestra el contenido de la base 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
Para definir la base ACCESS como fuente de datos ODBC, siga estos pasos:
- active el administrador de fuentes de datos ODBC tal y como se ha mostrado anteriormente y seleccione la pestaña User DSN (DSN = Nombre de la fuente de datos)

- añada una fuente con el botón Add, indique que se puede acceder a esta fuente a través de un controlador Access y haga Terminer:

- Asigne el nombre articles-access a la fuente de datos, introduzca una descripción libre y utilice el botón Sélectionner para designar el archivo .mdb de la base de datos. Finalice con OK.

La nueva fuente de datos aparecerá entonces en la lista de fuentes de datos del usuario:

Para definir la base de datos MySQL DBARTICLES como fuente de datos ODBC, proceda de la siguiente manera:
- active el administrador de fuentes de datos ODBC tal y como se ha mostrado anteriormente y seleccione la pestaña Usuario DSN. Añada una nueva fuente de datos con Add y seleccione el controlador ODBC de MySQL.

- Ejecute Terminer. Aparecerá entonces una página de configuración de la fuente MySQL:
54321

- en (1) se le da un nombre a nuestra fuente de datos ODBC
- en (2) indicamos la máquina en la que se encuentra el servidor MySQL. Aquí introducimos localhost para indicar que se encuentra en la misma máquina que nuestra aplicación. Si el servidor MySQL estuviera en una máquina M remota, introduciríamos allí su nombre y nuestra aplicación funcionaría entonces con una base de datos remota sin necesidad de modificaciones.
- En (3) se introduce el nombre de la base de datos. Aquí se llama DBARTICLES.
- En (4) se introduce el nombre de usuario admarticles y en (5) la contraseña mdparticles.
7.3.2. Uso de un controlador ODBC
En una aplicación que utilice una base de datos en modo conectado, normalmente se seguirán los siguientes pasos:
- Conexión a la base de datos
- Envío de consultas SQL a la base de datos
- Recepción y procesamiento de los resultados de estas consultas
- Cierre de la conexión
Los pasos 2 y 3 se repiten, y el cierre de la conexión solo tiene lugar al finalizar la explotación de la base de datos. Se trata de un esquema relativamente clásico al que quizá esté acostumbrado si ha utilizado una base de datos de forma interactiva. Estos pasos son los mismos tanto si la base de datos se utiliza a través de un controlador ODBC como de un controlador OLE DB. A continuación presentamos un ejemplo con las clases .NET de gestión de fuentes de datos ODBC. El programa se llama liste y admite como parámetro el nombre DSN de una fuente de datos ODBC que tiene una tabla ARTICLES. A continuación, muestra el contenido de esta tabla:
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
En los resultados anteriores, vemos que el programa ha listado tanto el contenido de la base ACCESS como el de la base MySQL. Analicemos ahora el código de este programa:
' opciones
Option Explicit On
Option Strict On
' espacios de nombres
Imports System
Imports System.Data
Imports Microsoft.Data.Odbc
Imports Microsoft.VisualBasic
Module db1
Sub main(ByVal args As String())
' aplicación de consola
' muestra el contenido de una tabla ARTICLES de una base de datos DSN
' cuyo nombre se pasa como parámetro
Const syntaxe As String = "syntaxe : pg dsnArticles"
Const tabArticles As String = "articles" ' la table des articles
' verificación de los parámetros
' ¿hay 1 parámetro?
If args.Length <> 1 Then
' mensaje de error
Console.Error.WriteLine(syntaxe)
' fin
Environment.Exit(1)
End If
' se recupera el parámetro
Dim dsnArticles As String = args(0) ' la base DSN
' Preparación de la conexión a la base de datos
Dim articlesConn As OdbcConnection = Nothing ' la connexion
Dim myReader As OdbcDataReader = Nothing ' le lecteur de données
' se intenta acceder a la base de datos
Try
' cadena de conexión a la base
Dim connectString As String = "DSN=" + dsnArticles + ";"
articlesConn = New OdbcConnection(connectString)
articlesConn.Open()
' ejecución de un comando SQL
Dim sqlText As String = "select * from " + tabArticles
Dim myOdbcCommand As New OdbcCommand(sqlText)
myOdbcCommand.Connection = articlesConn
myReader = myOdbcCommand.ExecuteReader()
' Análisis de la tabla recuperada
' Visualización de las columnas
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))
' Visualización de los datos
While myReader.Read()
' procesamiento de la línea actual
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
' Cierre del lector
myReader.Close()
' Cierre de la conexión
articlesConn.Close()
End Try
End Sub
End Module
Las clases de gestión de fuentes ODBC se encuentran en el espacio de nombres Microsoft.Data.Odbc, por lo que debemos importarlo. Por otra parte, hay una serie de clases en el espacio de nombres System.Data.
Imports System.Data
Imports Microsoft.Data.Odbc
Los espacios de nombres utilizados por el programa se encuentran en diferentes ensamblados. Se compila el programa con el siguiente 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 de conexión
Una conexión ODBC utiliza la clase OdbcConnection. El constructor de esta clase admite como parámetro lo que se denomina una cadena de conexión. Se trata de una cadena de caracteres que define todos los parámetros necesarios para que se pueda establecer la conexión con la base de datos. Estos parámetros pueden ser muy numerosos y, por lo tanto, la cadena puede resultar compleja. La cadena tiene el formato "param1=valeur1;param2=valeur2;...;paramj=valeurj;". A continuación se muestran algunos parámetros paramj posibles:
nombre de un usuario que va a acceder a la base de datos | |
contraseña de este usuario | |
nombre de la base de datos DSN, si lo tiene | |
nombre de la base de datos a la que se accede | |
Si se define una fuente de datos como ODBC mediante el administrador de fuentes de datos ODBC, estos parámetros ya se han especificado y guardado. Basta entonces con pasar el parámetro DSN, que proporciona el nombre DSN de la fuente de datos. Esto es lo que se hace aquí:
' preparación de la conexión a la base de datos
Dim articlesConn As OdbcConnection = Nothing ' la connexion
Dim myReader As OdbcDataReader = Nothing ' le lecteur de données
Try
' se intenta acceder a la base de datos
' cadena de conexión a la base de datos
Dim connectString As String = "DSN=" + dsnArticles + ";"
articlesConn = New OdbcConnection(connectString)
articlesConn.Open()
Una vez creado el objeto OdbcConnection, se abre la conexión con el método Open. Esta apertura puede fallar, como cualquier otra operación en la base de datos. Por eso, todo el código de acceso a la base de datos se encuentra en un try-catch. Una vez establecida la conexión, se pueden enviar consultas SQL a la base de datos.
7.3.2.2. Enviar consultas SQL
Para enviar consultas SQL, necesitamos un objeto Command, en este caso más concretamente un objeto OdbcCommand, ya que utilizamos una fuente de datos ODBC. La clase OdbcCommand tiene varios constructores:
- OdbcCommand(): crea un objeto Command vacío. Para utilizarlo, habrá que especificar posteriormente diversas propiedades:
- CommandText: el texto de la consulta SQL que se va a ejecutar
- Conexión: el objeto OdbcConnection que representa la conexión a la base de datos en la que se realizará la consulta
- CommandType: el tipo de la consulta SQL. Hay tres valores posibles
- CommandType.Text: la propiedad CommandText contiene el texto de una consulta SQL (valor por defecto)
- CommandType.StoredProcedure: la propiedad CommandText contiene el nombre de un procedimiento almacenado en la base de datos
- CommandType.TableDirect: la propiedad CommandText contiene el nombre de una tabla T. Equivalente a select * from T. Solo existe para los controladores OLE DB.
- OdbcCommand(string sqlText): el parámetro sqlText se asignará a la propiedad CommandText. Este es el texto de la consulta SQL que se va a ejecutar. La conexión deberá especificarse en la propiedad Connection.
- OdbcCommand(cadena sqlText, OdbcConnection conexión): el parámetro sqlText se asignará a la propiedad CommandText y el parámetro connexion a la propiedad Connection.
Para emitir la consulta SQL, se dispone de dos métodos:
- OdbcdataReader ExecuteReader(): envía la consulta SELECT de CommandText a la conexión Connection y crea un objeto OdbcDataReader que permiteacceder a todas las filas de la tabla de resultados de select
- int ExecuteNOnQuery(): envía la consulta de actualización (INSERT, UPDATE, DELETE) de CommandText a la conexión Connection y devuelve el número de líneas afectadas por esta actualización.
En nuestro ejemplo, tras abrir la conexión a la base de datos, emitimos una consulta SQL SELECT para obtener el contenido de la tabla ARTICLES:
' ejecución de un comando SQL
Dim sqlText As String = "select * from " + tabArticles
Dim myOdbcCommand As New OdbcCommand(sqlText)
myOdbcCommand.Connection = articlesConn
myReader = myOdbcCommand.ExecuteReader()
Una consulta de búsqueda suele ser del tipo:
Solo las palabras clave de la primera línea son obligatorias; las demás son opcionales. Existen otras palabras clave que no se muestran aquí.
- Se realiza una unión con todas las tablas que se encuentran detrás de la palabra clave from
- Solo se conservan las columnas que se encuentran detrás de la palabra clave «select»
- Solo se conservan las filas que cumplen la condición de la palabra clave «where»
- Las filas resultantes, ordenadas según la expresión de la palabra clave «order by», forman el resultado de la consulta.
El resultado de un select es una tabla. Si tomamos la tabla ARTICLES anterior y queremos los nombres de los artículos cuyo stock actual está por debajo del umbral mínimo, escribiremos:
Si los queremos ordenados alfabéticamente por nombre, escribiremos:
7.3.2.3. Explotación del resultado de una consulta SELECT
El resultado de una consulta SELECT en modo no conectado es un objeto DataReader, en este caso un objeto OdbcDataReader. Este objeto permite obtener secuencialmente todas las líneas del resultado y disponer de información sobre las columnas de dichos resultados. Veamos algunas propiedades y métodos de esta clase:
el número de columnas de la tabla | |
Item(i) representa la columna n.º i de la línea actual del resultado | |
el valor de la columna n.º i de la línea actual, representado como tipo XXX (Int16, Int32, Int64, Double, String, Boolean, ...) | |
nombre de la columna n.º i | |
cierra el objeto OdbcdataReader y libera los recursos asociados | |
avanza una línea en la tabla de resultados. Devuelve falso si esto no es posible. La nueva línea se convierte en la línea actual del lector. |
La explotación del resultado de un select suele ser una explotación secuencial análoga a la de los archivos de texto: solo se puede avanzar en la tabla, no retroceder:
While myReader.Read()
' se tiene una línea; se procesa
....
' línea siguiente
end while
Estas explicaciones bastan para comprender el siguiente código de nuestro ejemplo:
' Explotación de la tabla recuperada
' Visualización de las columnas
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))
' Visualización de los datos
While myReader.Read()
' explotación de la línea actual
ligne = ""
For i = 0 To myReader.FieldCount - 1
ligne += myReader(i).ToString + " "
Next i
Console.WriteLine(ligne)
End While
La única dificultad está en la instrucción en la que se concatenan los valores de las diferentes columnas de la línea actual:
For i = 0 To myReader.FieldCount - 1
ligne += myReader(i).ToString + " "
Next i
La notación línea+=myReader(i).ToString se traduce como línea+=myReader.Item(i).ToString() donde Item(i) es el valor de la columna i de la línea actual.
7.3.2.4. Liberación de recursos
Las clases OdbcReader y OdbcConnection disponen ambas de un método Close() que libera los recursos asociados a los objetos así cerrados.
7.3.3. Uso de un controlador OLE DB
Retomamos el mismo ejemplo, esta vez con una base de datos a la que se accede mediante un controlador OLE DB. La plataforma .NET proporciona un controlador de este tipo para las bases de datos ACCESS. Por lo tanto, vamos a utilizar la misma base de datos articles.mdb que anteriormente. Lo que queremos demostrar aquí es que, aunque cambien las clases, los conceptos siguen siendo los mismos:
- la conexión está representada por un objeto OleDbConnection
- se emite una consulta SQL gracias a un objeto OleDbCommand
- si esta consulta es una cláusula SELECT, se obtendrá a cambio un objeto OleDbDataReader para acceder a las filas de la tabla de resultados
Estas clases se encuentran en el espacio de nombres System.Data.OleDb. El programa anterior se puede transformar fácilmente para gestionar una base OLE DB:
- se sustituye en todas partes OdbcXX por OleDbXX
- se modifica la cadena de conexión. Para una base de datos ACCESS sin nombre de usuario ni contraseña, la cadena de conexión es Provider=Microsoft.JET.OLEDB.4.0;Data Source=[fichier.mdb]. La parte configurable de esta cadena es el nombre del archivo ACCESS que se va a utilizar. Modificaremos nuestro programa para que acepte como parámetro el nombre de este archivo.
- El espacio de nombres que hay que importar es ahora System.Data.OleDb.
Nuestro programa queda así:
' opciones
Option Explicit On
Option Strict On
' espacios de nombres
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)
' aplicación de consola
' muestra el contenido de una tabla ARRTICLES de una base de datos DSN
' cuyo nombre se pasa como parámetro
Const syntaxe As String = "syntaxe : pg base_access_articles"
Const tabArticles As String = "articles" ' la table des articles
' verificación de los parámetros
' ¿hay 1 parámetro?
If args.Length <> 1 Then
' mensaje de error
Console.Error.WriteLine(syntaxe)
' fin
Environment.Exit(1)
End If
' se recupera el parámetro
Dim dbArticles As String = args(0) ' la base de données
' Preparación de la conexión a la base de datos
Dim articlesConn As OleDbConnection = Nothing ' la connexion
Dim myReader As OleDbDataReader = Nothing ' le lecteur de données
' se intenta acceder a la base de datos
Try
' cadena de conexión a la base
Dim connectString As String = "Provider=Microsoft.JET.OLEDB.4.0;Data Source=" + dbArticles + ";"
articlesConn = New OleDbConnection(connectString)
articlesConn.Open()
' ejecución de un comando SQL
Dim sqlText As String = "select * from " + tabArticles
Dim myOleDbCommand As New OleDbCommand(sqlText)
myOleDbCommand.Connection = articlesConn
myReader = myOleDbCommand.ExecuteReader()
' Análisis de la tabla recuperada
' Visualización de las columnas
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))
' Visualización de los datos
While myReader.Read()
' procesamiento de la línea actual
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
' Cierre del lector
myReader.Close()
' Cierre de la conexión
articlesConn.Close()
End Try
' fin
Environment.Exit(0)
End Sub
End Module
Resultados obtenidos:
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. Actualización de una tabla
Los ejemplos anteriores se limitaban a enumerar el contenido de una tabla. Modificamos nuestro programa de gestión de la base de datos de artículos para que pueda modificarla. El programa se llama sql. Se le pasa como parámetro el nombre DSN de la base de datos de artículos que se va a gestionar. El usuario introduce directamente los comandos SQL en el teclado y el programa los ejecuta, tal y como muestran los siguientes resultados obtenidos en la base de datos de artículos 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
El programa es el siguiente:
' opciones
Option Explicit On
Option Strict On
' espacios de nombres
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)
' aplicación de consola
' ejecuta consultas SQL introducidas mediante el teclado en una
' tabla ARTICLES de una base de datos DSN cuyo nombre se pasa como parámetro
Const syntaxe As String = "syntaxe : pg dsnArticles"
' verificación de los parámetros
' ¿hay 2 parámetros?
If args.Length <> 1 Then
' mensaje de error
Console.Error.WriteLine(syntaxe)
' fin
Environment.Exit(1)
End If 'if
' se recupera el parámetro
Dim dsnArticles As String = args(0)
' cadena de conexión a la base de datos
Dim connectString As String = "DSN=" + dsnArticles + ";"
' preparación de la conexión a la base de datos
Dim articlesConn As OdbcConnection = Nothing
Dim sqlCommand As OdbcCommand = Nothing
Try
' se intenta acceder a la base de datos
articlesConn = New OdbcConnection(connectString)
articlesConn.Open()
' se crea un objeto de comando
sqlCommand = New OdbcCommand("", articlesConn)
'try
Catch ex As Exception
' mensaje de error
Console.Error.WriteLine(("Erreur d'exploitation de la base de données (" + ex.Message + ")"))
' liberación de recursos
Try
articlesConn.Close()
Catch
End Try
Environment.Exit(2)
End Try 'catch
' se crea un diccionario de comandos SQL aceptados
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
' lectura y ejecución de los comandos SQL introducidos mediante el teclado
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+")
' bucle de introducción y ejecución de los comandos SQL introducidos mediante el teclado
While True
' sin errores al inicio
Dim erreur As Boolean = False
' solicitud de la consulta
Console.Out.Write(ControlChars.Lf + "Requête SQL (fin pour arrêter) : ")
requête = Console.In.ReadLine().Trim().ToLower()
' ¿Terminado?
If requête = "fin" Then
Exit While
End If
' se descompone la consulta en campos
champs = modèle.Split(requête)
' ¿consulta válida?
If champs.Length = 0 Or Not dicoCommandes.ContainsKey(champs(0)) Then
' mensaje de error
Console.Error.WriteLine("Requête invalide. Utilisez select, insert, update, delete")
' siguiente consulta
erreur = True
End If
If Not erreur Then
' preparación del objeto Command para ejecutar la consulta
sqlCommand.CommandText = requête
' ejecución de la consulta
Try
If champs(0) = "select" Then
executeSelect(sqlCommand)
Else
executeUpdate(sqlCommand)
End If
Catch ex As Exception
' mensaje de error
Console.Error.WriteLine(("Erreur d'exploitation de la base de données (" + ex.Message + ")"))
End Try
End If
End While
' liberación de recursos
Try
articlesConn.Close()
Catch
End Try
Environment.Exit(0)
End Sub
' ejecución de una consulta de actualización
Sub executeUpdate(ByVal sqlCommand As OdbcCommand)
' ejecuta sqlCommand, consulta de actualización
Dim nbLignes As Integer = sqlCommand.ExecuteNonQuery()
' visualización
Console.Out.WriteLine(("Il y a eu " & nbLignes & " ligne(s) modifiée(s)"))
End Sub
' ejecución de una consulta Select
Sub executeSelect(ByVal sqlCommand As OdbcCommand)
' ejecuta sqlCommand, consulta Select
Dim myReader As OdbcDataReader = sqlCommand.ExecuteReader()
' Explotación de la tabla recuperada
' visualización de las columnas
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))
' Visualización de datos
While myReader.Read()
' procesamiento de la línea actual
ligne = ""
For i = 0 To myReader.FieldCount - 1
ligne += myReader(i).ToString + " "
Next i
' Visualización
Console.WriteLine(ligne)
End While
' Liberación de recursos
myReader.Close()
End Sub
End Module
Aquí solo comentamos las novedades con respecto al programa anterior:
- Creamos un diccionario de comandos SQL aceptados:
' se crea un diccionario de comandos SQL aceptados
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
lo que nos permite comprobar fácilmente si la primera palabra (campo [0]) de la consulta introducida es uno de los cuatro comandos aceptados:
' ¿Consulta válida?
If champs.Length = 0 Or Not dicoCommandes.ContainsKey(champs(0)) Then
' mensaje de error
Console.Error.WriteLine("Requête invalide. Utilisez select, insert, update, delete")
' siguiente consulta
erreur = True
End If 'if
- Anteriormente, la consulta se había descompuesto en campos utilizando el método Split de la clase RegEx:
Dim modèle As New Regex("\s+")
....
' se descompone la consulta en campos
champs = modèle.Split(requête)
Las palabras que componen la consulta pueden estar separadas por cualquier número de espacios.
- La ejecución de una consulta select no utiliza el mismo método que el de una consulta de actualización (insert, update, delete). Por lo tanto, hay que realizar una prueba y ejecutar una función diferente para cada uno de estos dos casos:
' Preparación del objeto Command para ejecutar la consulta
sqlCommand.CommandText = requête
' ejecución de la consulta
Try
If champs(0) = "select" Then
executeSelect(sqlCommand)
Else
executeUpdate(sqlCommand)
End If 'try
Catch ex As Exception
' mensaje de error
Console.Error.WriteLine(("Erreur d'exploitation de la base de données (" + ex.Message + ")"))
End Try
La ejecución de una consulta SQL puede generar una excepción que se gestiona aquí.
- La función executeSelect recoge todo lo visto en los ejemplos anteriores.
- La función executeUpdate utiliza el método ExecuteNonQuery de la clase OdbcCommand, que devuelve el número de líneas afectadas por el comando.
7.3.5. IMPOTS
Retomamos el objeto impôt creado en un capítulo anterior:
' opciones
Option Strict On
Option Explicit On
' espacios de nombres
Imports System
Public Class impôt
' datos necesarios para el cálculo del impuesto
' proceden de una fuente externa
Private limites(), coeffR(), coeffN() As Decimal
' creador
Public Sub New(ByVal LIMITES() As Decimal, ByVal COEFFR() As Decimal, ByVal COEFFN() As Decimal)
' se comprueba que las 3 tablas tengan el mismo tamaño
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
' Todo correcto
Me.limites = LIMITES
Me.coeffR = COEFFR
Me.coeffN = COEFFN
End Sub
' cálculo del impuesto
Public Function calculer(ByVal marié As Boolean, ByVal nbEnfants As Integer, ByVal salaire As Integer) As Long
' cálculo del número de participaciones
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
' cálculo de la renta imponible y del coeficiente familiar
Dim revenu As Decimal = 0.72D * salaire
Dim QF As Decimal = revenu / nbParts
' cálculo del impuesto
limites((limites.Length - 1)) = QF + 1
Dim i As Integer = 0
While QF > limites(i)
i += 1
End While
' Devolución del resultado
Return CLng(revenu * coeffR(i) - nbParts * coeffN(i))
End Function
End Class
Le añadimos un nuevo constructor que permite inicializar las tablas limites, coeffR y coeffN a partir de una base de datos ODBC:
Imports System.Data
Imports Microsoft.Data.Odbc
Imports System.Collections
...
' constructor 2
Public Sub New(ByVal DSNimpots As String, ByVal Timpots As String, ByVal colLimites As String, ByVal colCoeffR As String, ByVal colCoeffN As String)
' inicializa las tres tablas de límites, coeffR, coeffN a partir
' del contenido de la tabla Timpots de la base ODBC DSNimpots
' colLimites, colCoeffR, colCoeffN son las tres columnas de esta tabla
' puede lanzar una excepción
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 consulta SELECT
Dim selectCommand As String = "select " + colLimites + "," + colCoeffR + "," + colCoeffN + " from " + Timpots
' tablas para recuperar los datos
Dim tLimites As New ArrayList
Dim tCoeffR As New ArrayList
Dim tCoeffN As New ArrayList
' se intenta acceder a la base de datos
impotsConn = New OdbcConnection(connectString)
impotsConn.Open()
' se crea un objeto de comando
sqlCommand = New OdbcCommand(selectCommand, impotsConn)
' se ejecuta la consulta
Dim myReader As OdbcDataReader = sqlCommand.ExecuteReader()
' Explotación de la tabla recuperada
While myReader.Read()
' los datos de la línea actual se colocan en las tablas
tLimites.Add(myReader(colLimites))
tCoeffR.Add(myReader(colCoeffR))
tCoeffN.Add(myReader(colCoeffN))
End While
' Liberación de recursos
myReader.Close()
impotsConn.Close()
' las tablas dinámicas se colocan en tablas estáticas
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
El programa de prueba es el siguiente: recibe como argumentos los parámetros que se deben transmitir al constructor de la clase impôt. Tras construir un objeto impôt, realiza los cálculos del impuesto a pagar:
Option Explicit On
Option Strict On
' Espacios de nombres
Imports System
Imports Microsoft.VisualBasic
' página de prueba
Module testimpots
Sub Main(ByVal arguments() As String)
' programa interactivo de cálculo de impuestos
' el usuario introduce tres datos con el teclado: casado nbEnfants salario
' el programa muestra entonces el impuesto a pagar
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"
' verificación de los parámetros del programa
If arguments.Length <> 5 Then
' mensaje de error
Console.Error.WriteLine(syntaxe1)
' fin
Environment.Exit(1)
End If 'if
' se recuperan los argumentos
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)
' creación de un objeto de impuesto
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
' bucle infinito
While True
' al principio no hay errores
Dim erreur As Boolean = False
' se solicitan los parámetros para el cálculo del impuesto
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()
' ¿Hay que hacer algo?
If paramètres Is Nothing Or paramètres = "" Then
Exit While
End If
' Comprobación del número de argumentos en la línea introducida
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
' verificación de la validez de los parámetros
' casado
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
' salario
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
' los parámetros son correctos: se calcula el impuesto
Console.Out.WriteLine(("impôt=" & objImpôt.calculer(marié = "o", nbEnfants, salaire).ToString + " F"))
End If
End While
End Sub
End Module
La base utilizada es una base MySQL con el nombre 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 |
+---------+--------+---------+
La ejecución del programa de prueba da los siguientes resultados:
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 :
