Está en la página 1de 6

Acceso a datos con ADO.

NET
Publicado por: Guillermo 'guille' Som - Martes, Marzo 8, 2005 · 37 comentarios

Figura1: Selección de datos filtrada

Figura2: Un formulario para editar los datos

1- Conectar con el origen de datos mediante un objeto Connection

El objeto Connection debe ser el adecuado al origen de datos, por tanto si nos decidimos
por acceder a una base de datos SQL Server, usaremos un objeto del tipo
SqlConnection y si el origen de datos es una base Access, usaremos uno del tipo
OleDbConnection.
Lo habitual es que el objeto Connection lo instanciemos usando la cadena de conexión
adecuada, en esa cadena de conexión indicaremos, (como mínimo), el origen de datos y
el usuario o password que debemos usar para acceder a la base de datos.

Veamos dos ejemplos para acceder tanto a la base de datos pubs que se incluye como
ejemplo al instalar Visual Studio .NET, como a una versión de esta misma base, pero
usando el formato de Access.

Conexión a la base de datos pubs de SQL Server:

Dim connectionString As String


Dim sqlCnn As System.Data.SqlClient.SqlConnection
connectionString = “data source=(local)NETSDK;” & _
”initial catalog=pubs;” & _
”integrated security=SSPI;persist security info=False;”
sqlCnn = New SqlConnection(connectionString)

Conexión a la base de datos pubs.mdb de Access:

Dim connectionString As String


Dim odbCnn As System.Data.OleDb.OleDbConnection
connectionString = “Provider=Microsoft.Jet.OLEDB.4.0;Data Source=pubs.mdb”
odbCnn = New OleDbConnection(connectionString)

Nota:
En estos dos ejemplos, suponemos que la base de datos no está protegida con
contraseña y, en el caso de SQL Server, que el usuario actual de Windows tiene los
permisos adecuados para acceder a esa base de datos.
En el código incluido en el ZIP con el código completo se tienen en cuenta las bases de
datos protegidas por contraseña, tanto para SQL Server como para Access.

2- Indicar los comandos de selección, actualización, etc.

El siguiente paso que debemos dar es crear los comandos necesarios para seleccionar
(comando select) los datos que queremos obtener de la base de datos, además de los
comandos necesarios para actualizar (comando update), eliminar (comando delete) e
insertar nuevos datos (comando insert).
Al primero de ellos, el comando select, ya estamos acostumbrados, es el típico comando
Select de SQL. El comando típico para la base pubs y la tabla authors sería:
SELECT * FROM authors
Este comando de selección lo podemos indicar en el constructor de la clase DataAdapter
correspondiente, aunque también podemos indicarlo creando un objeto Command y
asignándolo a la propiedad SelectCommand de la clase DataAdapter, por supuesto tanto
la clase DataAdapter como el objeto Command debe ser del tipo adecuado dependiendo
del origen de datos: SqlCommand u OleDbCommand para acceder a una base de
datos de SQL Server o una de Access respectivamente.
En los siguientes ejemplos vemos cómo hacerlo en ambos casos:

Dim sqlda As New SqlDataAdapter(“SELECT * FROM authors”, sqlCnn)

Dim odbda As New OleDbDataAdapter(“SELECT * FROM authors”, odbCnn)

Una vez que tenemos creado el objeto DataAdapter, si vamos a modificar los datos
obtenidos debemos crear y asignar los comandos de inserción, actualización y
eliminación, aunque estos comando solamente tenemos que asignarlos si vamos a
realizar esos tipos de acciones, es decir, si no vamos a insertar nuevos datos no es
necesario asignar el comando insert, de la misma manera, si no vamos a eliminar datos,
¿para que indicar el comando delete? Lo que si debemos tener muy presente, es que si
no indicamos esos comandos y después realizamos cualquiera de esas tres acciones, a la
hora de actualizar los datos en la base de datos, se producirá un error de que no se ha
indicado el comando adecuado.
Estos tres comandos los podemos indicar de dos formas distintas:
1- Definiéndolos de forma manual.
2- Usando un objeto del tipo CommandBuilder.

En el primer caso, tendremos que indicar el comando SQL adecuado para cada una de
las tres acciones, el problema es que en la mayoría de los casos, solamente “los
expertos” en acceso a datos serán capaces de crear o definir esos comandos, al menos de
forma manual, ya que siempre podemos echar mano del propio Visual Studio .NET y
dejar que sea el propio IDE el que nos “muestre” el camino y nos “enseñe” a crear esos
comandos. Para hacer esto simplemente tenemos que arrastrar desde el Explorador de
Servidores la tabla a la que queremos acceder, haciendo esto, (ya sea con una tabla de
SQL Server como una de Access), tendremos no solo el objeto DataAdapter con cada
uno de los comandos (incluido el de selección), sino que también tendremos un objeto
del tipo Connection con la cadena de conexión necesaria para acceder a la base de datos.

Pero si nuestra intención es poder acceder a cualquier base de datos y, por tanto a
cualquier tabla de la base de datos indicada, la forma más fácil de hacerlo es usando un
objeto del tipo SqlCommandBuilder (para SQL Server) o bien OleDbCommandBuilder
(para bases de Access). La forma de usar esta clase es bien sencilla y simplemente
tendremos que hacer algo así:

Para SQL Server:

Dim cb As New SqlCommandBuilder(sqlda)

Para Access:

Dim cb As New OleDbCommandBuilder(odbda)

También hay que aclarar, que la clase CommandBuilder no crea el código más optimo,
pero… es siempre una forma de hacer las cosas, si no óptimas, al menos rápidas.

3- Obtener los datos desde la fuente de datos y asignarlos a una tabla

Una vez que tenemos los objetos Connection y DataAdapter creados, ya estamos
preparados para traer los datos desde la base de datos y asignarlos a un objeto DataSet o
DataTable, para que posteriormente podamos manipularlos en modo desconectado.
Esta operación es bien sencilla y lo único que necesitamos es tener instanciado un
objeto DataSet o DataTable, dependiendo de que datos vayamos a obtener, en este
ejemplo solo vamos a acceder a una tabla, por tanto lo mejor es usar un objeto del tipo
DataTable, dicho objeto lo tenemos que indicar como parámetro del método Fill del
objeto DataAdapter.

Para SQL Server:

dt = New DataTable
sqlda.Fill(dt)

Para Access:
dt = New DataTable
odbda.Fill(dt)

Llegados a este punto, debemos tener en cuenta ciertas circunstancias, por ejemplo, que
para crear nuevos elementos de la tabla, estos utilicen un valor autoincremental o que
los nombres de los campos contengan espacios u otros caracteres no válidos. En el
primer caso tendremos que asignarle a la propiedad MissingSchemaAction del objeto
DataAdapter un valor MissingSchemaAction.AddWithKey, en el segundo debemos
especificar los caracteres que queremos usar para “encerrar” el nombre conflictivo, lo
habitual es usar los corchetes cuadrados ([ y ]), esto lo haremos asignando el carácter de
apertura a la propiedad QuotePrefix y el de cierre a la propiedad QuoteSuffix, ambas
son propiedades del objeto CommandBuilder.

4- Manejar los datos en la memoria

Al usar el método Fill del objeto DataAdapter lo que hacemos es traer desde la base de
datos, no solo los datos, sino también la estructura de la tabla, o al menos la parte de la
tabla que hemos indicado en la cadena Select, es decir, si usamos SELECT * FROM
tabla, nos traeremos todos los datos y todas las columnas de la tabla, pero si solamente
queremos manejar parte del contenido de esa tabla, podemos “filtrar” los datos, por
ejemplo, si solamente nos interesan las columnas au_id, au_lname y au_fname de la
tabla authors que el valor de state sea CA, podemos usar esta cadena de selección:
SELECT au_id, au_lname, au_fname FROM authors WHERE state = ‘CA’
Como es obvio, la cantidad de datos sería menor y no tendríamos acceso a todas las
columnas, solo a las tres indicadas entre SELECT y FROM.
En la figura 1 podemos ver un ejemplo de esto último.

Una vez que tenemos los datos en memoria, podemos manipularlos de la forma que
mejor nos parezca, pudiendo añadir, eliminar o modificar, si es que eso es lo que
queremos hacer.
La forma de realizar estas acciones ya dependerá de nuestras preferencias y todo lo que
debemos saber es que, al menos en el ejemplo que estamos usando, toda esa
información reside en un objeto del tipo DataTable, este objeto tiene las columnas y
filas de datos que hemos traído de la tabla.
Para aclarar conceptos, veamos cómo asignar esa información a un control de tipo
ListView, al que previamente le hemos indicado que muestre los datos con vista del tipo
Document.
Este código, tal como podemos comprobar, es totalmente independiente de que los
datos los hayamos obtenido de una base de datos de SQL Server o de Access, ya que
realmente están “dentro” del objeto DataTable, el cual como ya hemos comentado
anteriormente es independiente de la fuente de datos, por tanto el siguiente código nos
servirá para los dos casos que estamos tratando:

With lvDatos
.Columns.Clear()
For i As Integer = 0 To dt.Columns.Count – 1
Dim dc As DataColumn = dt.Columns(i)
Dim lvc As New ColumnHeader

lvc.Text = dc.ColumnName
lvc.TextAlign = HorizontalAlignment.Left
lvc.Width = dc.ColumnName.Length * 16
.Columns.Add(lvc)
Next
.Items.Clear()
For j As Integer = 0 To dt.Rows.Count – 1
Dim lvi As ListViewItem
For i As Integer = 0 To dt.Columns.Count – 1
Dim dc As DataColumn = dt.Columns(i)
If i = 0 Then
lvi = New ListViewItem(dt.Rows(j).Item(i).ToString)
Else
lvi.SubItems.Add(dt.Rows(j).Item(i).ToString)
End If
Next
.Items.Add(lvi)
Next
End With

En la primera parte recorremos cada una de las columnas de la tabla para poder crear las
cabeceras del ListView, para ello recorremos cada uno de los elementos de la
propiedad/colección Columns del objeto DataTable, creamos un nuevo objeto del tipo
ColumnHeader y le asignamos el nombre de la columna.
En la segunda parte, recorremos todas las filas de la tabla y de cada fila obtenemos el
valor o contenido de cada columna, para asignarlo a un elemento del ListView.

Para tener los conceptos más claros, vamos a permitir que el usuario de esta aplicación
de ejemplo pueda modificar cada una de las filas, las cuales mostramos en el control
LisView, por tanto debemos tener alguna forma de “ligar” cada fila del control
ListView con cada una de las filas de la tabla, dicha relación la vamos a hacer
manualmente, es decir, cada elemento del ListView tendrá una referencia a cada una de
las filas, el mejor sitio para guardar esa relación es la propiedad Tag de cada elemento,
por tanto, vamos a modificar el código anterior para que cada vez que creemos un
nuevo elemento del ListView le indiquemos a que fila de la tabla se corresponde, por
tanto haremos esta asignación justo después de la línea lvi = New
ListViewItem(dt.Rows(j).Item(i).ToString):

‘ asignamos la fila obtenida


‘ para después poder saber a que datos corresponde
lvi.Tag = dt.Rows(j)

De esta forma podemos saber a que fila de la tabla corresponde cada elemento del
ListView, y por tanto, podemos hacer algo como esto:

Dim fila As DataRow = CType(lvDatos.SelectedItems(0).Tag, DataRow)

Después esta fila la podemos usar para modificar los datos que contiene, por ejemplo
usando un formulario que nos permita dicha modificación, tal como podemos ver en la
figura 2.
5- Actualizar los datos que han cambiado en la base de datos

La última acción que debemos realizar es hacer efectivos los cambios que hayamos
realizado en el objeto DataTable que mantenemos en la memoria.
En este caso debemos volver hacer uso del objeto DataAdapter y, por supuesto, del
objeto DataTable. El método Update del adaptador se encargará de consultar cada uno
de los cambios (eliminación, inserción o actualización) que se hayan producido en la
tabla y usará los comandos adecuados para hacer efectivos dichos cambios en la base de
datos a la que está “ligado”.
Por tanto, si los datos han cambiado haremos esta llamada:

da.Update(dt)

Esto hará que la base de datos se sincronice con los datos que tenemos en memoria,
pero además debemos indicarle a la tabla que también haga efectivos esos cambios de
forma interna, ya que cada vez que llamamos al método Update del DataAdapter, éste
realiza una comprobación fila por fila en busca de cambios y una vez que hemos
actualizado los datos tenemos que asegurarnos de que el estado de cada fila del
DataTable esté como está en la base de datos, ya que si en memoria hemos eliminado
una fila, al llamar al método Update esa fila se eliminará de la base de datos, pero si no
“actualizamos” los datos en memoria, esa fila borrada aún permanecerá en memoria,
con el “estado” de borrado, pero estará y si llamamos nuevamente al método Update, ya
no habrá ninguna fila que eliminar de la base de datos, con lo que se producirá, en el
mejor de los casos, un error.
Por tanto debemos decirle a la tabla que acepte los cambios que se han realizado, esto lo
conseguimos mediante este código:

dt.AcceptChanges()

Es decir, llamamos al método AcceptChanges del objeto DataTable