Acceso A Datos Con ADO
Acceso A Datos Con ADO
Acceso A Datos Con ADO
NET
En los siguientes temas vamos a tratar el acceso a datos desde VB.NET, haciendo uso del nuevo modelo
de acceso a datos incluido en la plataforma .NET Framework: ADO .NET. Mostraremos las tareas bsicas
para el acceso a datos desde aplicaciones basadas en formularios Windows, empleando la tecnologa
proporcionada por ADO .NET.
ADO .NET es la nueva versin del modelo de objetos ADO (ActiveX Data Objects), es decir, la estrategia
que ofrece Microsoft para el acceso a datos. ADO .NET ha sido ampliado para cubrir todas las
necesidades que ADO no ofreca, y est diseado para trabajar con conjuntos de datos desconectados, lo
que permite reducir el trfico de red. ADO .NET utiliza XML como formato universal de transmisin de
los datos.
ADO .NET posee una serie de objetos que son los mismos que aparecen en la versin anterior de ADO,
como pueden ser el objeto Connection o Command, e introduce nuevos objetos tales como el objeto
DataReader, DataSet o DataView.
ADO .NET se puede definir como:
Un conjunto de interfaces, clases, estructuras y enumeraciones que permiten el acceso a datos
desde la plataforma .NET de Microsoft
La evolucin lgica del API ADO tradicional de Microsoft
Permite un modo de acceso desconectado a los datos, los cuales pueden provenir de mltiples
fuentes de datos, de diferente arquitectura de almacenamiento
Soporta un completo modelo de programacin y adaptacin, basado en el estndar XML
Seguidamente vamos a realizar una descripcin genrica de la arquitectura de ADO .NET, y ms tarde
veremos como utilizarlo desde aplicaciones VB.NET
De los anteriores puntos podemos obtener muy buenas conclusiones en cuanto a las mejoras introducidas
en el nuevo modelo ADO .NET. Se puede resumir en un mejor mecanismo de comunicacin entre
procesos gracias a XML y una independencia del cliente con respecto al servidor, que posibilita el
funcionamiento autnomo de la aplicacin (mejor tolerancia a fallos, independencia del estado de la red).
Mantenimiento
En el ciclo de vida de una aplicacin los cambios poco sustanciales y modestos son permisibles. Pero
cuando es necesario abordar un cambio estructural o arquitectnico del sistema, la tarea se vuelve
demasiado compleja y a veces inviable. Esto es una gran desventaja de los sistemas actuales, pues muchas
veces se trata de una cuestin de actualizacin de los procesos de la propia empresa. Adems, cuanto ms
se aumenta el proceso de la operativa de la empresa, las necesidades de proceso crecen hasta desbordar
las mquinas. Es por ello que se separa la estructura de un programa en varias capas.
Una de esas capas es la de datos, que es fundamental desarrollar correctamente. Gracias a los DataSets, la
tarea de portar y aumentar los procesos de datos y de negocio ser mas sencillo: el intercambio de
informacin a travs de XML, hace que sea ms sencilla la tarea de estructurar en ms capas la
aplicacin, convirtindola en ms modular y fcil de mantener.
Programacin
Los programadores pueden acceder a un API de programacin estructurado, de fuerte tipificado y que
adems se concentra en la correcta forma de presentar los datos. Centra en la estructura del lenguaje lo
que un programador necesita para disear los programas sin dar muchos rodeos. El Cdigo fuente 557
muestra un ejemplo de cdigo sin tipificar:
....
IfCosteTotal>Table("Cliente")("Luis").Column("CreditoDisponible")Then
....
ADO .NET favorece la escalabilidad, puesto que su modelo de conexin Off-Line evita que se mantengan
los recursos reservados ms tiempo del considerado necesario. Esto permite que ms usuarios por unidad
de tiempo puedan acceder a la aplicacin sin problemas de tiempos.
Adems se pueden montar servicios en Cluster de alta disponibilidad que sern balanceados
automticamente por el sistema sin afectar a las conexiones ADO. Lo cual garantiza la ampliacin del
servicio sin representar un cambio de arquitectura de diseo.
El formato que emplea ADO .NET para almacenar su estado es XML. Puesto que ya es un estndar de la
industria, esta persistencia nos ofrece las siguientes cualidades:
La informacin puede estar accesible para cualquier componente del sistema que entienda
XML.
Es un formato de texto plano, no binario, que lo hace compatible con cualquier componente de
cualquier plataforma, y recuperable en cualquier circunstancia.
DataSet
El API de ADO .NET proporciona una superclase, DataSet, que encapsula lo que sera la base de datos a
un nivel lgico: tablas, vistas, relaciones, integridad entre todos ellos, etc., pero siempre con
independencia del tipo de fabricante que la dise. Aqu se tiene el mejor concepto de datos
desconectados: una copia en el cliente de la arquitectura de la base de datos, basada en un esquema XML
que la independiza del fabricante, proporcionando al desarrollador la libertad de trabajo independiente de
la plataforma. La Figura 339 muestra una representacin de este tipo de objeto.
Esta clase se compone a su vez, de clases de soporte, que representan cada una, los elementos
arquitecturales de la base de datos: tablas, columnas, filas, sus reglas de chequeo, sus relaciones, las vistas
asociadas a la tabla, etc.
ADO .NET y XML
XML se ha convertido en la piedra angular de la informtica distribuida de nuestros das. De ah que gran
parte de las motivaciones en cuanto a la redefinicin del API de ADO, se deban a la adaptacin de los
objetos a un modelo de procesos que se apoya en documentos XML, no en objetos especficos de cada
plataforma a partir de cursores. Esto permite que las clases de ADO .NET puedan implementar
mecanismos de conversin de datos entre plataformas, lectura de datos de cualquier origen, habilitar
mecanismos de persistencia en el mismo formato en el que se procesan., etc. En esta redefinicin,
Microsoft ha puesto como intermediario entre un cliente y sus datos, un adaptador que transforma cada
comando y cada dato en modelos de documentos XML. Tanto para consultas como para actualizaciones.
Esto es lo que posibilita la nueva filosofa de acceso a datos desconectados de ADO .NET: primero se
cargan en el cliente los documentos necesarios almacenndolos en DataSet, a partir de consultas a tablas,
vistas, procedimientos, etc.; se nos da la posibilidad de trabajar con documentos, sin necesidad de estar
continuamente consumiendo recursos de la red; y por ltimo, se procesarn los cambios producidos
envindolos a la base de datos, el adaptador tomar los cambios del documento, y los replicar al
servidor. En la Figura 340 se puede ver un esquema de la relacin entre ADO .NET y XML.
DataTable. Cada objeto DataTable de un DataSet posee la propiedad DefaultView, que devuelve la vista
de los datos por defecto de la tabla.
Otro objeto de ADO .NET es DataReader, que representa un cursor de slo lectura y que slo permite
desplazamiento hacia adelante (read-only/forward-only), cada vez existe un nico registro en memoria, el
objeto DataReader mantiene abierta la conexin con el origen de los datos hasta que es cerrado. Al igual
que ocurra con otros objetos de ADO .NET, de este objeto tenemos dos versiones, que como el lector
sospechar se trata de los objetos SqlDataReader y OleDbDataReader.
Espacios de nombres y clases en ADO .NET
En el presente apartado vamos a enumerar brevemente los principales elementos que forman parte del
API de ADO .NET.
Primero vamos a comentar los distintos espacios de nombres que constituyen la tecnologa ADO .NET:
System.Data. Clases genricas de datos de ADO .NET. Integra la gran mayora de clases que
habilitan el acceso a los datos de la arquitectura .NET.
System.Data.SqlClient. Clases del proveedor de datos de SQL Server. Permite el acceso a
proveedores SQL Server en su versin 7.0 y superior.
System.Data.OleDb. Clases del proveedor de datos de OleDB. Permite el acceso a proveedores
.NET que trabajan directamente contra controladores basados en los ActiveX de Microsoft.
System.Data.SqlTypes. Definicin de los tipos de datos de SQL Server. Proporciona la
encapsulacin en clases de todos los tipos de datos nativos de SQL Server y sus funciones de
manejo de errores, ajuste y conversin de tipos, etc.
System.Data.Common. Clases base, reutilizables de ADO .NET. Proporciona la coleccin de
clases necesarias para acceder a una fuente de datos (como por ejemplo una Base de Datos).
System.Data.Internal. Integra el conjunto de clases internas de las que se componen los
proveedores de datos. Dentro del espacio de nombres System.Data encontramos las siguientes
clases compartidas, que constituyen el eje central de ADO .NET.
DataSet. Almacn de datos por excelencia en ADO .NET. Representa una base de datos
desconectada del proveedor de datos. Almacena tablas y sus relaciones.
DataTable. Un contenedor de datos. Estructurado como un conjunto de filas (DataRow) y
columnas (DataColumn).
DataRow. Registro que almacena n valores. Representacin en ADO .NET de una fila/tupla de
una tabla de la base de datos.
DataColumn. Contiene la definicin de una columna. Metadatos y datos asociados a su
dominio.
DataRelation. Enlace entre dos o ms columnas iguales de dos o mas tablas.
Constraint. Reglas de validacin de las columnas de una tabla.
DataColumnMapping. Vnculo lgico existente entre una columna de un objeto del DataSet y
la columna fsica de la tabla de la base de datos.
DataTableMapping. Vnculo lgico existente entre una tabla del DataSet y la tabla fsica de la
base de datos.
Adems de estas clases, existe otro grupo de clases consistente en las clases especficas de un proveedor
de datos. Estas clases conforman los aspectos particulares de un fabricante de proveedores de datos .NET.
Tienen una sintaxis con el formato XXXClase, donde XXX es un prefijo que determina el tipo de
plataforma de conexin a datos. Se definen en dos espacios de nombre: System.Data.SqlClient y
System.Data.OleDb.
En la Tabla 32 se ofrece una descripcin de las clases que podemos encontrar en estos espacios de
nombre.
Tabla 31
Para aquellos conocedores de ADO en alguna de sus versiones anteriores, podemos hacer una analoga o
comparacin entre las antiguas clases de ADO y las nuevas de ADO .NET. En la Figura 341 se puede ver
esta aproximacin.
Hasta aqu hemos realizado una introduccin a la tecnologa ADO .NET, repasando su arquitectura y
comentando las clases principales. En lo que resta de tema vamos a utilizar las distintas clases que nos
ofrece ADO .NET desde VB.NET, para realizar tareas comunes de acceso a datos, como pueden ser
establecer una conexin, obtener un conjunto de registros, realizar operaciones con los datos, etc.
El primer paso obligado en un acceso a datos consiste en establecer una conexin con un almacn de
datos. Esto lo vamos a conseguir gracias a las clases Connection de ADO .NET, que nos permitirn
conectarnos a un origen de datos (ya sea una base de datos o no) , al igual que en ADO clsico
emplebamos el objeto Connection.
En ADO se poda ejecutar directamente una sentencia contra el almacn de datos, o bien abrir un conjunto
de registros (Recordset), pero en ADO .NET no vamos a realizar esta operacin con este tipo de objetos.
Debemos recordar que existen dos implementaciones para algunos de los objetos de ADO .NET, cada uno
especfico del origen de datos con el que nos vamos a conectar. Esto ocurre con el objeto Connection, que
tiene dos versiones, una como proveedor de datos de SQL Server, a travs de la clase
System.Data.SqlClient.SqlConnection, y otra como proveedor de datos OLEDB, a travs de la clase
Sysem.Data.OleDb.OleDbConnection.
Por norma general, del objeto Connection utilizaremos los mtodos Open( ) y Close( ), para abrir y cerrar
conexiones respectivamente, con el almacn de datos adecuado. Aunque tenemos el recolector de basura
que gestiona de forma automtica los recursos y objetos que no son utilizados, es recomendable cerrar las
conexiones de forma explcita utilizando el mtodo Close( ).
Las conexiones se abrirn de forma explcita utilizando el mtodo Open(), pero tambin se puede hacer de
forma implcita utilizando un objeto DataAdapter, esta posibilidad la veremos ms adelante. Cuando
ejecutamos el mtodo Open() sobre un objeto Connection (SqlConnection o OleDbConnection), se abrir
la conexin que se ha indicado en su propiedad ConnectionString, es decir, esta propiedad indicar la
cadena de conexin que se va a utilizar para establecer la conexin con el almacn de datos
correspondiente. El mtodo Open() no posee parmetros.
El constructor de la clase Connection (al decir clase Connection de forma genrica nos estamos refiriendo
en conjunto a las clases SqlConnection y OleDbConnection de ADO .NET) se encuentra sobrecargado, y
en una de sus versiones recibe como parmetro una cadena que ser la cadena de conexin que se aplique
a su propiedad ConnectionString. Si hacemos uso de la clase SqlConnection, en la cadena de conexin no
podremos especificar una DSN de ODBC, ya que la conexin se va a realizar en este caso directamente
con SQL Server. Y si utilizamos la clase OleDbConnection debemos especificar el proveedor OLEDB que
se va a utilizar para establecer la conexin, una excepcin es el proveedor OLEDB para ODBC
(MSDASQL), que no puede ser utilizado, ya que el proveedor OLEDB de .NET no soporta el proveedor
de ODBC, en este caso deberemos realizar la conexin utilizando el proveedor adecuado al almacn de
datos.
Los proveedores OLEDB que son compatibles con ADO .NET son:
SQLOLEDB: Microsoft OLE DB Provider for SQL Server.
MSDAORA: Microsoft OLE DB Provider for Oracle.
Microsoft.Jet.OLEDB.4.0: OLE DB Provider for Microsoft Jet.
La sintaxis utilizada para indicar la cadena de conexin, con las particularidades propias de cada
proveedor, veremos que es muy similar a la utilizada en ADO clsico. El Cdigo fuente 559 muestra un
ejemplo de conexin con un servidor SQL Server 2000, y su posterior desconexin, utilizando un objeto
SqlConnection. Debemos importar el espacio de nombres Data.SqlClient para poder utilizar el objeto.
Este cdigo lo podemos asociar a la pulsacin de un botn en un formulario.
ImportsSystem.Data.SqlClient
'....
Try
'crearelobjetodeconexin
DimoConexionAsNewSqlConnection()
'pasarlacadenadeconexin
oConexion.ConnectionString="server=(local);"&_
"database=Xnorthwind;uid=sa;pwd=;"
'abrirconexin
oConexion.Open()
MessageBox.Show("Conectado")
'cerrarconexin
oConexion.Close()
MessageBox.Show("Desconectado")
CatchoExcepAsSqlException
'siseproducealgnerror,
'locapturamosmedianteelobjeto
'deexcepcionesparticularpara
'elproveedordeSQLServer
MessageBox.Show("Erroralconectarcondatos"&_
ControlChars.CrLf&_
oExcep.Message&ControlChars.CrLf&_
oExcep.Server)
EndTry
este mtodo tiene sentido cuando estamos ejecutando una sentencia SQL del tipo Select
Count(*). Este mtodo devuelve un objeto de la clase genrica Object.
Prepare. Este mtodo construye una versin compilada del objeto Command dentro del
almacn de datos.
A continuacin mostraremos algunos ejemplos de uso de objetos Command.
El Cdigo fuente 561 ilustra la insercin de un registro utilizando un objeto SqlCommand. En primer
lugar utilizamos un constructor de la clase, que recibe como parmetro la sentencia a ejecutar y la
conexin. Como vamos a ejecutar una sentencia que no produce un conjunto de resultados, emplearemos
el mtodo ExecuteNonQuery( ). Observe tambin el lector en este ejemplo, que la conexin slo
permanece abierta en el momento de ejecutar el comando; esta es la tcnica recomendable que debemos
utilizar para todas las operaciones con datos: mantener abierta la conexin el menor tiempo posible.
'crearconexin
DimoConexionAsNewSqlConnection()
oConexion.ConnectionString="Server=(local);"&_
"Database=Gestion;uid=sa;pwd=;"
'crearsentenciaSQL
DimsSQLAsString
sSQL="INSERTINTOClientes(IDCliente,Nombre,FIngreso)"&_
"VALUES(10,'Alfredo','18/7/2002')"
'crearcomando
DimoComandoAsNewSqlCommand(sSQL,oConexion)
DimiResultadoAsInteger
oConexion.Open()'abrirconexin
iResultado=oComando.ExecuteNonQuery()'ejecutarcomando
oConexion.Close()'cerrarconexin
MessageBox.Show("Registrosaadidos:"&iResultado)
'crearcomando
DimoComandoAsNewSqlCommand(sSQL,oConexion)
'aadirparmetrosalcomando:
'parmetroprimercampo
oComando.Parameters.Add(NewSqlParameter("@CodCli",_
SqlDbType.Int))
oComando.Parameters("@CodCli").Value=25
'parmetrosegundocampo
oComando.Parameters.Add(NewSqlParameter("@Nombre","Raquel"))
'parmetrotercercampo
DimoParametroAsNewSqlParameter()
oParametro.ParameterName="@Fecha"
oParametro.SqlDbType=SqlDbType.DateTime
oParametro.Value="25/10/2002"
oComando.Parameters.Add(oParametro)
DimiResultadoAsInteger
oConexion.Open()'abrirconexin
iResultado=oComando.ExecuteNonQuery()'ejecutarcomando
oConexion.Close()'cerrarconexin
MessageBox.Show("Registrosaadidos:"&iResultado)
MessageBox.Show("Registrosmodificados:"&iResultado)
oConexion.Close()'cerrarconexin
MessageBox.Show("Nmeroderegistrosdeclientes:"&iResultado)
'crearcomando
DimoComandoAsNewSqlCommand("SELECT*FROMEmployees",_
oConexion)
'crearDataReader
DimoDataReaderAsSqlDataReader
oConexion.Open()
oDataReader=oComando.ExecuteReader()'obtenerDataReader
'recorrerfilas
WhileoDataReader.Read()
Me.lstEmpleados.Items.Add(oDataReader("LastName"))
EndWhile
oDataReader.Close()
oConexion.Close()
EndSub
EndWhile
oDataReader.Close()
oConexion.Close()
EndSub
Merge( ). Toma los contenidos de un DataSet y los mezcla con los de otro DataSet, de forma
que contendr los datos de ambos objetos DataSet. En lo que respecta a las propiedades de la
clase DataSet, podemos remarcar las siguientes.
CaseSensitive. Propiedad que indica si las comparaciones de texto dentro de las tablas
distinguen entre maysculas y minsculas. Por defecto tiene el valor False.
DataSetName. Establece o devuelve mediante una cadena de texto el nombre del objeto
DataSet.
HasErrors. Devuelve un valor lgico para indicar si existen errores dentro de las tablas del
DataSet.
Relations. Esta propiedad devuelve una coleccin de objetos DataRelation, que representan
todas las elaciones existentes entre las tablas del objeto DataSet.
Tables. Devuelve una coleccin de objetos DataTable, que representan a cada una de las tablas
existentes dentro del objeto DataSet.
En el ejemplo del Cdigo fuente 568 ofrecemos un sencillo ejemplo de creacin de un objeto DataSet que
llenaremos con un DataAdapter. Una vez listo el DataSet, recorreremos la tabla que contiene y
mostraremos valores de sus columnas en un ListBox.
'crearconexin
DimoConexionAsNewSqlConnection()
oConexion.ConnectionString="Server=(local);Database=Northwind;uid=sa;pwd=;"
'crearadaptador
DimoDataAdapterAsNewSqlDataAdapter("SELECT*FROMCustomersORDERBY
ContactName",oConexion)
'crearconjuntodedatos
DimoDataSetAsNewDataSet()
oConexion.Open()
'utilizareladaptadorparallenareldatasetconunatabla
oDataAdapter.Fill(oDataSet,"Customers")
oConexion.Close()
'unavezdesconectados,recorrerlatabladeldataset
DimoTablaAsDataTable
oTabla=oDataSet.Tables("Customers")
DimoFilaAsDataRow
ForEachoFilaInoTabla.Rows
'mostrarlosdatosmedianteunobjetofila
Me.lstCustomers.Items.Add(oFila.Item("CompanyName")&_
""&oFila.Item("ContactName")&""&_
oFila.Item("Country"))
Next
Para demostrar el uso de los objetos DataAdapter vamos a desarrollar un proyecto con el nombre
PruDataAdapter. En esta aplicacin vamos a utilizar el mismo objeto DataAdapter para realizar una
consulta contra una tabla e insertar nuevas filas en esa misma tabla.
En primer lugar disearemos el formulario del programa. Como novedad, introduciremos el control
DataGrid, que trataremos con ms profundidad en un prximo apartado. Baste decir por el momento, que
a travs del DataGrid visualizaremos una o varias tablas contenidas en un DataSet. La Figura 345 muestra
el aspecto de esta aplicacin en funcionamiento.
Respecto al cdigo del formulario, en primer lugar, vamos a declarar varios objetos de acceso a datos a
nivel de la clase para poder tenerlos disponibles en diversos mtodos. Veamos el Cdigo fuente 569.
ImportsSystem.Data.SqlClient
PublicClassForm1
InheritsSystem.Windows.Forms.Form
PrivateoConexionAsSqlConnection
PrivateoDataSetAsDataSet
PrivateoDataAdapterAsSqlDataAdapter
'....
'....
'yasignarlosaladaptador
DimoCmdInsercionAsNewSqlCommand("INSERTINTOAUTORES"&_
"(IDAutor,Autor)VALUES(@IDAutor,@Autor)",oConexion)
oDataAdapter.InsertCommand=oCmdInsercion
oDataAdapter.InsertCommand.Parameters.Add(NewSqlParameter("@IDAutor",
SqlDbType.Int))
oDataAdapter.InsertCommand.Parameters.Add(NewSqlParameter("@Autor",
SqlDbType.NVarChar))
DimoCmdConsultaAsNewSqlCommand("SELECT*FROMAUTORES",_
oConexion)
oDataAdapter.SelectCommand=oCmdConsulta
'crearconjuntodedatos
oDataSet=NewDataSet()
Me.CargarDatos()
EndSub
PrivateSubCargarDatos()
'vaciareldataset
oDataSet.Clear()
oConexion.Open()'abrirconexin
'utilizareladaptadorparallenareldatasetconunatabla
oDataAdapter.Fill(oDataSet,"Autores")
oConexion.Close()'cerrarconexin
'enlazardatasetcondatagrid;
'enDataSourceseasignaeldataset,
'enDataMemberelnombredelatabladel
'datasetquemostrareldatagrid
Me.grdDatos.DataSource=oDataSet
Me.grdDatos.DataMember="Autores"
EndSub
iResultado=oDataAdapter.InsertCommand.ExecuteNonQuery()
'cerrarconexin
oConexion.Close()
Me.CargarDatos()
MessageBox.Show("Registrosaadidos:"&iResultado)
EndSub
Como siguiente paso, escribiremos el manipulador del evento Load del formulario y un mtodo para
cargar los datos del registro actual en los controles del formulario, el Cdigo fuente 573 muestra esta
parte.
PrivateSubForm1_Load(ByValsenderAsObject,ByValeAsSystem.EventArgs)
Handles
MyBase.Load
'crearconexin
DimoConexionAsSqlConnection
oConexion=NewSqlConnection()
oConexion.ConnectionString="Server=(local);"&_
"Database=Gestion;uid=sa;pwd=;"
'crearadaptador
Me.oDataAdapter=NewSqlDataAdapter("SELECT*FROMClientes",_
oConexion)
'crearcommandbuilder
DimoCommBuildAsSqlCommandBuilder=NewSqlCommandBuilder(oDataAdapter)
'creardataset
Me.oDataSet=NewDataSet()
oConexion.Open()
'llenarconeladaptadoreldataset
Me.oDataAdapter.Fill(oDataSet,"Clientes")
oConexion.Close()
'establecerelindicadordelregistro
'amostrardelatabla
Me.iPosicFilaActual=0
'cargarcolumnasdelregistroen
'loscontrolesdelformulario
Me.CargarDatos()
EndSub
PrivateSubCargarDatos()
'obtenerunobjetoconlafilaactual
DimoDataRowAsDataRow
oDataRow=Me.oDataSet.Tables("Clientes").Rows(Me.iPosicFilaActual)
'cargarloscontrolesdelformulariocon
'losvaloresdeloscamposdelregistro
Me.txtIDCliente.Text=oDataRow("IDCliente")
Me.txtNombre.Text=oDataRow("Nombre")
Me.txtFIngreso.Text=oDataRow("FIngreso")
Me.txtCredito.Text=oDataRow("Credito")
'mostrarlaposicinactualdelregistro
'yelnmerototaldelregistros
Me.lblRegistro.Text="Registro:"&_
Me.iPosicFilaActual+1&"de"&_
Me.oDataSet.Tables("Clientes").Rows.Count
EndSub
PrivateSubbtnRetroceder_Click(ByValsenderAsSystem.Object,ByValeAs
System.EventArgs)HandlesbtnRetroceder.Click
'siestamosenelprimerregistro,
'nohacermovimiento
IfMe.iPosicFilaActual=0Then
MessageBox.Show("Primerregistro")
Else
'disminuirelmarcadorderegistro
'yactualizarloscontrolesconlos
'datosdelregistroactual
Me.iPosicFilaActual=1
Me.CargarDatos()
EndIf
EndSub
PrivateSubbtnPrimero_Click(ByValsenderAsSystem.Object,ByValeAs
System.EventArgs)HandlesbtnPrimero.Click
'establecerelmarcadorderegistroenelprimero
Me.iPosicFilaActual=0
Me.CargarDatos()
EndSub
PrivateSubbtnUltimo_Click(ByValsenderAsSystem.Object,ByValeAs
System.EventArgs)HandlesbtnUltimo.Click
'establecerelmarcadorderegistroenelprimero
'obteniendoelnmerodefilasquecontienelatablamenosuno
Me.iPosicFilaActual=(Me.oDataSet.Tables("Clientes").Rows.Count1)
Me.CargarDatos()
EndSub
'delatabladeldataset
Me.oDataSet.Tables("Clientes").Rows.Add(oDataRow)
EndSub
PrivateSubbtnModificar_Click(ByValsenderAsSystem.Object,ByValeAs
System.EventArgs)HandlesbtnModificar.Click
DimoDataRowAsDataRow
'obtenerelobjetofiladelatabladeldataset
'enelqueestamosposicionados
oDataRow=Me.oDataSet.Tables("Clientes").Rows(Me.iPosicFilaActual)
'modificarlascolumnasdelafila
'exceptolacorrespondientealidentificadorcliente
oDataRow("Nombre")=Me.txtNombre.Text
oDataRow("FIngreso")=Me.txtFIngreso.Text
oDataRow("Credito")=Me.txtCredito.Text
EndSub
PrivateSubbtnActualizar_Click(ByValsenderAsSystem.Object,ByValeAs
System.EventArgs)HandlesbtnActualizar.Click
'actualizarloscambiosrealizadoseneldataset
'contralabasededatosreal
Me.oDataAdapter.Update(Me.oDataSet,"Clientes")
EndSub
Me.oDataSet.Tables("Clientes").AcceptChanges()
'reposicionarenlaprimerafila
Me.btnPrimero.PerformClick()
EndSub
Pasando al cdigo de la clase del formulario, deberemos realizar las siguientes declaraciones a nivel de
clase, mostradas en el Cdigo fuente 577.
ImportsSystem.Data.SqlClient
PublicClassForm1
InheritsSystem.Windows.Forms.Form
PrivateoDataAdapterAsSqlDataAdapter
PrivateoDataSetAsDataSet
PrivateoBMBAsBindingManagerBase
'....
'....
'sedebeenlazar,eldataset,yelnombredetablacolumna;
'unavezcreadoelobjetoBinding,aadirloalacoleccin
'deenlacesdedatos,DataBindings,delcontrolquenecesitemos,
'conelmtodoAdd()dedichacoleccin
DimoBindAsBinding
oBind=NewBinding("Text",oDataSet,"Clientes.IDCliente")
Me.txtIDCliente.DataBindings.Add(oBind)
oBind=Nothing
oBind=NewBinding("Text",oDataSet,"Clientes.Nombre")
Me.txtNombre.DataBindings.Add(oBind)
oBind=Nothing
oBind=NewBinding("Text",oDataSet,"Clientes.FIngreso")
'AddHandleroBind.Format,AddressOfFormatoFecha
Me.txtFIngreso.DataBindings.Add(oBind)
oBind=Nothing
oBind=NewBinding("Text",oDataSet,"Clientes.Credito")
Me.txtCredito.DataBindings.Add(oBind)
oBind=Nothing
'obtenerdelcontextodeenlacedelformulario,
'elenlacedeundatasetyunatabladeterminadas
Me.oBMB=Me.BindingContext(oDataSet,"Clientes")
Me.VerContadorReg()
EndSub
PrivateSubVerContadorReg()
'mostrarinformacinsobreelnmerode
'registroactualyregistrostotales
'enlatabladeldataset
Me.lblRegistro.Text="Registro:"&_
Me.oBMB.Position+1&"de"&Me.oBMB.Count
EndSub
EndSub
PrivateSubbtnRetroceder_Click(ByValsenderAsSystem.Object,ByValeAs
System.EventArgs)HandlesbtnRetroceder.Click
Me.oBMB.Position=1
Me.VerContadorReg()
EndSub
PrivateSubbtnPrimero_Click(ByValsenderAsSystem.Object,ByValeAs
System.EventArgs)HandlesbtnPrimero.Click
Me.oBMB.Position=0
Me.VerContadorReg()
EndSub
PrivateSubbtnUltimo_Click(ByValsenderAsSystem.Object,ByValeAs
System.EventArgs)HandlesbtnUltimo.Click
Me.oBMB.Position=Me.oBMB.Count1
Me.VerContadorReg()
EndSub
Como detalle importante a observar en las operaciones de navegacin entre los registros, destacaremos el
hecho de que al mostrar el campo que contiene una fecha, dicho dato se muestra con toda la informacin
al completo, fecha y hora, sin ningn formato especfico.
Para conseguir en este caso, que la fecha se muestre con el formato que necesitemos, al crear el objeto
Binding para ese control, deberemos asignar a su evento Format un procedimiento manipulador, que
realice tal formateo y lo devuelva a travs del objeto ConvertEventArgs, que recibe ese evento. Veamos
estas operaciones en el Cdigo fuente 580.
PrivateSubForm1_Load(ByValsenderAsObject,ByValeAsSystem.EventArgs)
Handles
MyBase.Load
'....
oBind=NewBinding("Text",oDataSet,"Clientes.FIngreso")
AddHandleroBind.Format,AddressOfFormatoFecha
Me.txtFIngreso.DataBindings.Add(oBind)
oBind=Nothing
'....
EndSub
'manipuladordelEventoformatdelobjetoBinding
PrivateSubFormatoFecha(ByValsenderAsObject,ByValeAsConvertEventArgs)
DimdtFechaAsDateTime
dtFecha=e.Value
e.Value=dtFecha.ToString("ddMMMMyyyy")
EndSub
El proceso de edicin (insercin en este ejemplo), es muy similar al caso anterior. Aunque debemos tener
en cuenta que debido a las particularidades del Data Binding, no podemos borrar el contenido de los
TextBox, teclear datos e insertarlos, ya que eso realmente modificara el registro sobre el que estbamos
posicionados. Por ese motivo, en el botn Insertar, asignamos los valores directamente a las columnas del
objeto DataRow. Ver el Cdigo fuente 581.
PrivateSubbtnInsertar_Click(ByValsenderAsSystem.Object,ByValeAs
System.EventArgs)HandlesbtnInsertar.Click
DimoDataRowAsDataRow
GrupoEIDOS37.Conjuntosdedatosyenlace(DataBinding)
699
'crearunnuevoobjetofila
oDataRow=Me.oDataSet.Tables("Clientes").NewRow()
'aadirdatosalascolumnasdelafila
oDataRow("IDCliente")=100
oDataRow("Nombre")="Isabel"
oDataRow("FIngreso")="12/9/01"
oDataRow("Credito")=228
'aadirelobjetofilaalacoleccin
'defilasdelatabla
Me.oDataSet.Tables("Clientes").Rows.Add(oDataRow)
EndSub
PrivateSubbtnActualizar_Click(ByValsenderAsSystem.Object,ByValeAs
System.EventArgs)HandlesbtnActualizar.Click
Me.oDataAdapter.Update(Me.oDataSet,"Clientes")
EndSub
EndSub
PrivateSubbtnActualizar_Click(ByValsenderAsSystem.Object,ByValeAs
System.EventArgs)HandlesbtnActualizar.Click
Me.oDataAdapter.Update(oDataSet,"Grabaciones")
EndSub
Tras la ventana de presentacin, al pulsar el botn Siguiente, deberemos elegir la conexin que el
adaptador utilizar. Ver Figura 353.
A continuacin seleccionaremos el tipo de consulta, en este caso una sencilla sentencia SQL. Ver Figura
354.
Continuaremos con la escritura de la sentencia SQL que quedar incluida en el DataAdapter. Ver Figura
355.
Como paso final, se muestra un resumen de lo que este asistente ha generado en el DataAdapter. Figura
356.
Finalizada la creacin del adaptador de datos, seleccionaremos el men Datos + Generar conjunto de
datos del IDE, que nos mostrar una ventana en la que daremos el nombre del DataSet que utilizar el
formulario, y nos permitir elegir las tablas que contendr. Ver Figura 357.
Completado este ltimo paso, el DataGrid mostrar en tiempo de diseo, la disposicin de las columnas
de la tabla en su interior. Ver Figura 359.
En cuanto al cdigo que debemos escribir, en el evento Load, inicializaremos el DataSet, rellenndolo a
continuacin mediante el DataAdapter. Ver Cdigo fuente 583.
PrivateSubfrmGridAsist_Load(ByValsenderAsObject,ByValeAs
System.EventArgs)
HandlesMyBase.Load
Me.DsMusica1.Clear()
Me.SqlDataAdapter1.Fill(Me.DsMusica1)
EndSub
Me.grdDatos.Anchor=AnchorStyles.Bottom+AnchorStyles.Left+
AnchorStyles.Right+AnchorStyles.Top
Me.grdDatos.CaptionText="Ellistadodelasgrabaciones"
Me.grdDatos.CaptionBackColor=Color.Turquoise
Me.grdDatos.CaptionForeColor=Color.Black
'crearunobjetoparaestilosdeldatagrid
DimoEstiloGridAsNewDataGridTableStyle()
oEstiloGrid.MappingName="Grabaciones"
oEstiloGrid.BackColor=Color.LightGoldenrodYellow
oEstiloGrid.AlternatingBackColor=Color.Aquamarine
'crearobjetosdecolumnagridparacada
'columnadelatablaamostrareneldatagrid
DimoColGridAsDataGridTextBoxColumn
'configurarcadaobjetodecolumnagrid
oColGrid=NewDataGridTextBoxColumn()
oColGrid.TextBox.Enabled=False
oColGrid.Alignment=HorizontalAlignment.Center
oColGrid.HeaderText="Descripcingrabac."
'nombredelacolumnadeldatasetque
'semapeahaciaestacolumnadelgrid
oColGrid.MappingName="Titulo"
oColGrid.Width=300
'aadirlacolumnaalobjetoquecontiene
'losestilosdeldatagrid,enconcreto,
'alacoleccindeestilosdecolumna
oEstiloGrid.GridColumnStyles.Add(oColGrid)
oColGrid=Nothing
oColGrid=NewDataGridTextBoxColumn()
oColGrid.TextBox.Enabled=False
oColGrid.Alignment=HorizontalAlignment.Left
oColGrid.HeaderText="FechaCOMPRA"
oColGrid.MappingName="FCompra"
oColGrid.Width=110
oColGrid.Format="ddd,dMMMyyy"
oEstiloGrid.GridColumnStyles.Add(oColGrid)
oColGrid=Nothing
oColGrid=NewDataGridTextBoxColumn()
oColGrid.TextBox.Enabled=False
oColGrid.Alignment=HorizontalAlignment.Right
oColGrid.HeaderText="Valorpagado"
oColGrid.MappingName="Precio"
oColGrid.Width=85
oColGrid.Format="#,#"
oEstiloGrid.GridColumnStyles.Add(oColGrid)
oColGrid=Nothing
'unavezcreadastodaslascolumnasde
'estilosparaelgrid,aadirelobjeto
'quecontieneelestilopersonalizado
'alacoleccindeestilosdetablas
'deldatagrid
Me.grdDatos.TableStyles.Add(oEstiloGrid)
EndSub
DimoDAGrabacionesAsNewSqlDataAdapter("SELECT*FROMGrabaciones",
oConexion)
'creardataset
DimoDataSetAsNewDataSet()
oDAAutores.Fill(oDataSet,"Autores")
oDAGrabaciones.Fill(oDataSet,"Grabaciones")
'asignardatasetadatagrid
Me.grdDatos.DataSource=oDataSet
EndSub
DimdaCustomersAsNewSqlDataAdapter("SELECT*FROMCustomers",oConexion)
DimdaOrdersAsNewSqlDataAdapter("SELECT*FROMOrders",oConexion)
'instanciardataset
oDataSet=NewDataSet()
oConexion.Open()
'utilizarlosdataadaptersparallenareldatasetcontablas
daCustomers.Fill(oDataSet,"Customers")
daOrders.Fill(oDataSet,"Orders")
oConexion.Close()
'relacionarlasdostablasdeldatasetporcampocomn
oDataSet.Relations.Add("Customers_Orders",_
oDataSet.Tables("Customers").Columns("CustomerID"),_
oDataSet.Tables("Orders").Columns("CustomerID"))
'llenarelcomboboxconlosnombresdecliente
DimoDataRowAsDataRow
ForEachoDataRowInoDataSet.Tables("Customers").Rows
Me.cboCustomers.Items.Add(oDataRow("CustomerID")&_
""&oDataRow("CompanyName"))
Next
EndSub
'cadavezqueseseleccionaunvalorenelcombo
'seproduceesteevento
PrivateSubcboCustomers_SelectedIndexChanged(ByValsenderAsObject,ByVale
As
System.EventArgs)HandlescboCustomers.SelectedIndexChanged
'limpiarlosvaloresdellistbox
Me.lstOrders.Items.Clear()
DimdrFilaPadreAsDataRow
'obtenerlafiladelatablamaestra:Customers
drFilaPadre=oDataSet.Tables("Customers").Rows(Me.cboCustomers.SelectedIndex)
DimdrFilasHijas()AsDataRow
'obtenerlasfilashijasdelatablaOrders,
'graciasalarelacinCustomersOrders
drFilasHijas=drFilaPadre.GetChildRows("Customers_Orders")
DimdrFilaAsDataRow
'rellenarellistboxconvaloresdelasfilashijas
ForEachdrFilaIndrFilasHijas
Me.lstOrders.Items.Add(drFila("CustomerID")&_
""&drFila("OrderID")&_
""&drFila("OrderDate"))
Next
EndSub
En todo momento, desde la vista de las tablas hijas, podemos volver a la vista de la tabla padre, haciendo
clic en el icono con forma de flecha situado en el ttulo del DataGrid.
oDataAdapter=Nothing
EndSub
Como hemos comentado anteriormente, a partir de un DataTable podemos obtener varios filtros mediante
distintos objetos DataView, sin que ello suponga una penalizacin en el consumo de recursos. Para
demostrar este punto, la opcin Vistas + Combinada, crea una vista basada en un filtro combinado, y una
vista normal, ambas empleando la misma tabla base. Veamos el Cdigo fuente 593.
PrivateSubmnuCombinada_Click(ByValsenderAsSystem.Object,ByValeAs
System.EventArgs)HandlesmnuCombinada.Click
'tomarlatablaCustomersdeldatasetyaplicar...
'...filtrocombinadopordoscamposydepositarenundatagrid
DimoDataViewAsNewDataView()
oDataView.Table=oDataSet.Tables("Customers")
oDataView.RowFilter="ContactTitleLIKE'%Manager%'ANDCountryIN
('Spain','USA')"
Me.grdDatos.CaptionText="FiltrocombinadoporcamposContactTitleyCountry"
Me.grdDatos.DataSource=oDataView
'...filtroporuncampoydepositarenotrodatagrid
DimoDVAsNewDataView()
oDV.Table=oDataSet.Tables("Customers")
oDV.RowFilter="ContactNameLIKE'%an%'"
Me.grdDatosBIS.CaptionText="FiltroporcampoContactName"
Me.grdDatosBIS.DataSource=oDV
EndSub
En la Figura 371 vemos el resultado de una bsqueda, mostrado en uno de los DataGrid del formulario.
Si necesitamos ordenar por mltiples columnas de la tabla, slo tenemos que asignar a Sort una cadena
con la lista de columnas requeridas. Ver Cdigo fuente 596.
oDataView.Sort="Country,PostalCode"
El esquema de un DataSet consiste en toda la informacin contenida por este objeto, acerca de los
nombres de tablas, columnas, relaciones, etc.; es decir, se trata de metainformacin sobre los datos que
contiene el DataSet. Podemos obtener estos metadatos del DataSet recorriendo la coleccin que nos
interese en cada caso: Tables, Columns, etc. El Cdigo fuente 598 muestra como tras crear un DataSet,
recorremos sus tablas, y dentro de estas, sus columnas, mostrando la informacin obtenida en un ListBox.
PrivateSubbtnEsquema_Click(ByValsenderAsSystem.Object,ByValeAs
System.EventArgs)HandlesbtnEsquema.Click
'crearconexin
DimoConexionAsNewSqlConnection()
oConexion.ConnectionString="Server=(local);"&_
"Database=Northwind;uid=sa;pwd=;"
'creardataset
DimoDataSetAsNewDataSet()
'crearadaptadoresdedatosparalastablas
'yaadircadatablaaldatasetconeladaptador
DimoDataAdapterAsSqlDataAdapter
oDataAdapter=NewSqlDataAdapter("SELECT*FROMCustomers",oConexion)
oDataAdapter.Fill(oDataSet,"Customers")
oDataAdapter=Nothing
oDataAdapter=NewSqlDataAdapter("SELECT*FROMOrders",oConexion)
oDataAdapter.Fill(oDataSet,"Orders")
oDataAdapter=Nothing
oDataAdapter=NewSqlDataAdapter("SELECT*FROMProducts",oConexion)
oDataAdapter.Fill(oDataSet,"Products")
oDataAdapter=Nothing
oDataAdapter=NewSqlDataAdapter("SELECT*FROMTerritories",oConexion)
oDataAdapter.Fill(oDataSet,"Territories")
oDataAdapter=Nothing
'crearunobjetotablaycolumnaparamostrar
'lainformacindelesquemaqueeldatasetcontiene
DimoDataTableAsDataTable
DimoDataColumnAsDataColumn
Me.lstEsquema.Items.Add("EstructuradelDataSet")
'recorrerlacoleccindetablasdelDataSet
ForEachoDataTableInoDataSet.Tables
Me.lstEsquema.Items.Add("Tabla:"&oDataTable.TableName)
'recorrerlacoleccindecolumnasdelatabla
ForEachoDataColumnInoDataTable.Columns
Me.lstEsquema.Items.Add("Campo:"&_
oDataColumn.ColumnName&""&_
"Tipo:"&oDataColumn.DataType.Name)
Next
Next
EndSub