1.8 añadir una datagridview y bindingnavigator...

39
Al usar los métodos DataSet.WriteXml y DataSet.WriteXmlSchema para persistir DataSets en archivos locales, se ve que el esquema Customers DataSet, que difiere enormemente de la versión en tiempo de diseño, ocupa 9,31 KBytes y el documento XML ocupa 37,3 KBytes. Más adelante en este libro, se incluye código para guardar el esquema del juego de datos Northwind Customers. El esquema guardado no se puede abrir en ventana principal del proyecto. 1.8 Añadir una DataGridView y BindingNavigator Controls Al abrir el Form1 y el panel Orígenes de datos cambia el aspecto de los nodos DataSource. Por defecto, el icono de la tabla de datos Customers representa un DataGridView. Arrastrando el nodo de la tabla Customers desde el panel Orígenes de datos hasta el Form1 por defecto del proyecto, se autogeneran cuatro componentes en la bandeja que hay bajo el diseñador y se añaden los controles DataGridView y DataNavigator a un for- mulario que ha crecido considerablemente, tal como muestra la siguiente figura. Aquí están las descripciones de las cuatro componentes de la bandeja que muestra la figura anterior: NorthwindDataSet es la referencia del formulario a la fuente de datos para el for- mulario NorthwindDataSource.xsd. 29 Pasar de ADO a ADO.NET

Upload: truongthuan

Post on 15-Oct-2018

226 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

Al usar los métodos DataSet.WriteXml y DataSet.WriteXmlSchema para persistir DataSetsen archivos locales, se ve que el esquema Customers DataSet, que difiere enormemente dela versión en tiempo de diseño, ocupa 9,31 KBytes y el documento XML ocupa 37,3 KBytes.Más adelante en este libro, se incluye código para guardar el esquema del juego de datosNorthwind Customers. El esquema guardado no se puede abrir en ventana principal delproyecto.

1.8 Añadir una DataGridView y BindingNavigatorControls

Al abrir el Form1 y el panel Orígenes de datos cambia el aspecto de los nodos DataSource.Por defecto, el icono de la tabla de datos Customers representa un DataGridView.Arrastrando el nodo de la tabla Customers desde el panel Orígenes de datos hasta elForm1 por defecto del proyecto, se autogeneran cuatro componentes en la bandeja quehay bajo el diseñador y se añaden los controles DataGridView y DataNavigator a un for-mulario que ha crecido considerablemente, tal como muestra la siguiente figura.

Aquí están las descripciones de las cuatro componentes de la bandeja que muestra lafigura anterior:

NorthwindDataSet es la referencia del formulario a la fuente de datos para el for-mulario NorthwindDataSource.xsd.

29

Pasar de ADO a ADO.NET

VisualBasic2005_01.qxp 02/08/2007 16:11 PÆgina 29

Page 2: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

CustomersBindingSource es un objeto BindingSource basado en formulario, el cualunifica la unión y la navegación de datos de control y fiilas de datos para la tablade datos Customers, al proporcionar acceso directo al objeto BindingManager. Parafacilitar a los programadores de VB6 el cambio a ADO.NET 2.0, las BindingSourcestienen propiedades y métodos que simulan a los objetos ADODB.Recordset.Ejemplo de ellos son las propiedades AllowEdit, AllowAddNew, y AllowRemove(delete) y los correspondientes métodos AddNew, CancelNew, EndNew, Edit, Can-celEdit, y EndEdit. Los conocidos métodos MoveFirst, MoveLast, MoveNext, y Move-Previous se ocupan de la navegación por las filas. Hacer posible la navegación sig-nifica vincular un DataGridView o añadir otros controles para manipular laBindingSource.CustomersTableAdapter es el envoltorio del formulario para cualquier objeto Sql-DataAdapter que llene la tabla de datos NorthwindDataSet´s Customers invocando elmétodo CustomersTableAdapter.Fill. Los métodos Update, Insert, y Delete envíancambios en el juego de datos al servidor de la base de datos. La propiedad Custo-mersTableAdapter.Adapter permite acceder al SqlDataAdapter subyacente.CustomersBindingNavigator es un control habitual de ToolStrip que simula el botónVCR y otros de un ADODB.DataControl. Vincular el CustomersBindingNavigator conla CustomersBindingSource permite invocar con los botones los métodos Move...,AddNew, y Cancel.... Por defecto, los BindingNavigators suelen estar en la partesuperior del formulario. Al ejecutar el formulario se puede arrastrar el Binding-Navigator a una posición más cómoda, en la parte inferior del formulario, o tam-bién se le puede dar el valor Bottom a la propiedad Dock del DataNavigator en eldiseñador de proyecto.

DataComponents, DataConnectors, y DataNavigators son componentes y controles nuevosde ADO.NET 2.0 que substituyen los DataConnections y DataAdapters basados en formu-lario de ADO.NET 1.x. Las fuentes de datos de VS 2005 crean automáticamente relacionesentre los juegos de datos de diferentes tablas, que requieren una intervención manual pre-via. Los DataConnectors simplifican el código para navegar por las tablas de datos. El archi-vo DataSet.vb contiene clases, interfaces y tratadores de eventos para las componentes dedatos.

El último paso en el proceso de autogeneración del formulario de datos 2005 es añadir el métodoCustomersComponent.Fill al evento Form1_Load; y código para salvar los cambios del DataSet no seañade automáticamente al evento bindingNavigatorSaveItem_Click, debido a la complejidad delcódigo cuando el juego de datos contiene tablas múltiples. Salvar cambios múltiples entablas madre y derivadas requiere secuencias para inserciones, actualizaciones y borra-dos, a fin de mantener la integridad referencial.

Private Sub Form1_Load(ByVal sender As System.Object,

¨ ByVal e As System.EventArgs) Handles MyBase.Load

'TODO: This line of code loads data into the 'NorthwindDataSet.Customers' table.

'You can move, or remove it, as needed.

Me.CustomersTableAdapter.Fill(Me.NorthwindDataSet.Customers)

End Sub

30

Bases de datos con Visual Basic

VisualBasic2005_01.qxp 02/08/2007 16:11 PÆgina 30

Page 3: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

Private Sub bindingNavigatorSaveItem_Click(ByVal sender As System.Object,

ByVal e As System.EventArgs) Handles bindingNavigatorSaveItem.Click

Me.CustomersBindingSource.EndEdit()

Me.CustomersTableAdapter.Update(Me.NorthwindDataSet.Customers)

End Sub

La siguiente figura muestra el formulario final después de reducir el tamaño, ampliarel control de DataGridView para llenar el espacio disponible y pulsar <F5> para crear,depurar y ejecutar el proyecto.

La CustomersDataGridView está vinculada a la tabla Customers y se puede editar pordefecto. Los cambios que se hagan en la DataGridView no se validan en la tabla hastaque no se pulsa el botón Save Data.

Para facilitar la edición, el ancho de columna se puede adaptar automáticamente al con-tenido definiendo para la propiedad AutoSizeColumnsMode de DataGridView el valorAllCells o DisplayedCells, que añade una barra de desplazamiento horizontal al control.

1.9 Persistir y reabrir el juego de datosEl manejador de eventos del proyecto frmDataGridView_Load incluye el siguiente códi-go para salvar el documento de datos XML NorthwindDataSet y el esquema solo. Sepuede añadir código parecido después de la última invocación DataComponent.Fill oDataAdapter.Fill de cualquier proyecto para persistir su juego de datos.

Private Sub Form1_Load(ByVal sender As System.Object,

¨ ByVal e As System.EventArgs) Handles MyBase.Load

'TODO: This line of code loads data into the 'NorthwindDataSet.Customers' table.

'You can move, or remove it, as needed.

Me.CustomersTableAdapter.Fill(Me.NorthwindDataSet.Customers)

Dim strPath As String = Application.StartupPath

With Me.NorthwindDataSet

.WriteXml(strPath + "CustsNoSchema.xml", XmlWriteMode.IgnoreSchema)

31

Pasar de ADO a ADO.NET

VisualBasic2005_01.qxp 02/08/2007 16:11 PÆgina 31

Page 4: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

.WriteXml(strPath + "CustsWithSchema.xml", XmlWriteMode.WriteSchema)

.WriteXmlSchema(strPath + "CustsSchema.xsd")

End With

End Sub

Persistiendo el DataSet como documento XML, sin el esquema incrustado, permite darsoporte a los usuarios sin conexión, cargando de nuevo el DataSet del archivo. La sen-tencia siguiente se puede substituir por Me.CustomersTableAdapter.Fill(Me.North-windDataSet.Customers) cuando el usuario está desconectado:

Me.NorthwindDataSet.ReadXml(strPath + CustsNoSchema.xml , XmlReadMode.Auto)

El escenario en el mundo real para persistir y cargar de nuevo un juego de datos es muchomás complejo que lo que hemos visto aquí. En capítulos posteriores se describe cómo salvary cargar de nuevo los cambios pendientes del DataSet que no se han pasado a las tablasbase. El argumento XmlReadMode.Auto aparece por defecto, así que incluirlo es opcional.

1.10 Cambiar de un DataViewGrid a un Details Form

La combinación por defecto de los controles DataViewGrid y DataNavigator acelera lacreación de un formulario utilizable. De todos modos, un DataNavigator es mucho másútil para crear un formulario de detalles que muestre en pantalla los valores de colum-na en cuadros de texto u otros controles vinculados, como selectores de datos DateTimey cuadros de verificación para valores booleanos.

La ventana Data Sources facilita el cambio de la DataGridView a un formulario de deta-lle. Borre el control DataGridView, muestre la ventana Orígenes de datos, abra la lista des-plegable para la tabla de datos, y seleccione Detalles como se muestra en la siguientefigura.

Arrastre el icono DataTable hasta el formulario para añadir automáticamente unacolumna de etiquetas con controles asociados de vinculación de datos (cuadros de textoen este ejemplo) al formualrio. La siguiente figura, que es una versión modificada delproyecto GeneratedDataGridView, muestra las etiquetas y los cuadros de texto reordena-

32

Bases de datos con Visual Basic

VisualBasic2005_01.qxp 02/08/2007 16:11 PÆgina 32

Page 5: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

dos para reducir la altura del formulario.

1.11 Añadir un control de vínculo de datos relacionado

Al panel Orígenes de datos se le puede añadir una tabla relacionada y después un con-trol, como DataGridView, que se puede vincular al BindingAdapter relacionado. Paraañadir un control relacionado OrdersDataGridView a una copia del proyecto Genera-tedDetailView.sln, Debe realiar los siguientes pasos:

1. Copie y pege la carpeta GeneratedDetailView y renombre la nueva carpeta comoOrdersDetailView. No renombre el proyecto.

2. Pulse <F5> para crear y compilar el proyecto. Corrija cualquier error de nombreque detecte el depurador.

3. Abra la ventana Orígenes de datos y pulse el botón del ayudante Configurar Datasetcon el asistente para abrir la página Elija los objetos de la base de datos.

4. Expandir el árbol Tablas y seleccione la casilla de verificación de la tabla Orders.Pulse el botón Finalizar. De ese modo se añade en panel Orígenes de datos un nodorelacional Orders a la tabla Customers y un nodo individual Orders (ver siguientefigura).

5. Con DataGridView seleccionado en la lista desplegable, arrastre el nodo Orders rela-cionado por debajo de los cuadros de texto vinculados del formulario para autoge-nerar un control OrdersDataGridView.

6. Ajuste el tamaño y la posición de los controles y defina para la propiedad Or-dersDataGridView.AutoSizeRowsMode el valor DisplayedCells. Opcionalmente sepuede modificar la propiedad Text del formulario para reflejar el cambio en eldiseño.

33

Pasar de ADO a ADO.NET

VisualBasic2005_01.qxp 02/08/2007 16:11 PÆgina 33

Page 6: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

7. Pulse <F5> para crear y ejecutar el proyecto. El formulario aparecerá tal comomuestra la siguiente figura.

Arrastrando el nodo relacionado de la tabla Orders hasta el formulario se añade unOrdersTableAdapter y OrdersBindingSource a la bandeja, y el control OrdersDataGridViewal formulario. El valor de la propiedad OrdersDataGridView del control DataSource esOrdersBindingSource.

34

Bases de datos con Visual Basic

VisualBasic2005_01.qxp 02/08/2007 16:11 PÆgina 34

Page 7: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

La propiedad OrdersBindingSource tiene el valor CustomersBindingSource y el valor de lapropiedad DataMember es FK_Orders_Customers, el cual es la relación de clave foráneaen el campo CustomerID entre las tablas de Customers y Orders. Para verificar las pro-piedades de FK_Orders_Customers debe abrir el NorthwindDataSet.xsd en la ventanaprincipal, pulsar con el botón secundario la línea de relación entre las tablas Customersy Orders, y seleccionar Editar relación para abrir el cuadro de diálogo Relación (ver figu-ra siguiente).

Las relaciones que se definen añadiendo tablas relacionadas a la ventana Orígenes de datosno refuerzan la integridad referencial por defecto. Hay que cambiar el valor por defecto dela propiedad Sólo relación a uno de los otros valores para mantener la integridad referen-cial. También se puede especificar Cascade u otras opciones para las reglas actualización,eliminación, y aceptación o rechazo.

35

Pasar de ADO a ADO.NET

VisualBasic2005_01.qxp 02/08/2007 16:11 PÆgina 35

Page 8: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

37

CAPÍTULO 2

Las novedades de ADO.NET 2.0En este capítulo trataremos los nuevos objetos de ADO.NET 2.0 y los métodos, propie-dades y eventos utilizados con ellos. De la misma forma que el capítulo anterior, estecapítulo empieza con unas descripciones de los nuevos objetos en tiempo de ejecución,como DbProviderFactory y SqlBulkCopy, con los correspondientes ejemplos de códigopara crear y manejar los nuevos objetos. El capítulo continúa com más ejemplos avan-zados de las componentes y controles de ADO.NET 2.0 para los formularios deWindows, que se pueden agragar con la ayuda de diseñadores: DataTables, BindingSour-ces, BindingNavigators y DataGridViews.

Todos los ejemplos de código SQLServer de este capítulo se pueden ejecutar con SQL-Server 2000, SQLServer 2005 o SQLServer 2005 Express Edition (SQLX) y nencesitan losprivilegios del administrador del sistema.

Si trabajamos con SQLX, deberemos cambiar la cadena de conexión de cada proyecto de local-host a .\SQLEXPRESS.

2.1 Los objetos de formularioEste libro define un objeto en tiempo de ejecución como un tipo de objeto no visual,relacionado con los datos que se genera sin la ayuda de los múltiples asistentes. Losobjetos en tiempo de ejecución de ADO.NET 2.0 se crean escribiendo código VB.NET2005 sin la ayuda de los ayudantes de tiempo-diseño de VS 2005 ni código autogenera-do. Microsoft ha dedicado una parte importante del esfuerzo invertido en el desarrollode VS 2005 y ADO.NET 2.0 en simplificar con arrastrar y colocar la creación de formu-larios básicos Windows y Web de vinculación de datos.

Otro aspecto que se ha cuidado ha sido dar soporte a las nuevas características delSQLServer 2005 con los objetos System.Data y System.Xml. Por eso, ADO.NET 2.0 sóloincluye algunos objetos y características nuevas y actualizadas que son compatibles conlas fuentes de datos de SQLServer 2000. Más adelante, en este mismo libro, se trataránlas propiedades de ADO.NET 2.0 y VB.NET 2005 específicas para SQLServer 2005.

A continuación indicamos los objetos en tiempo de ejecución y actualizados, métodosy características de lenguaje, más importantes para los proyectos de formularioWindows:

VisualBasic2005_02.qxp 02/08/2007 16:14 PÆgina 37

Page 9: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

El objeto DbProviderFactory permite escribir código común para proveedores dedatos y servidores de bases de datos alternativos.El objeto SqlBulkCopy permite insertar con gran eficiencia datos de SQLServer defuentes relacionales y XML.El método SqlConnection.RetrieveStatistics proporciona información detallada sobrela conexión abierta con el SQLServer.La ejecución asincrónica de SqlCommand permite entrelazar consultas o actualiza-ciones múltiples de larga ejecución.Los nuevos objetos actualizados DataTable soportan las características comunes delos DataSet, como son los métodos ReadXml y WriteXml, retornan valores de los ser-vicios Web e interfaces remotas y streaming.A las tablas de datos se les puede asignar espacios-nombre y prefijos para los nom-bre de espacio.Los tipos Null permiten definir objetos fuertemente tipificados, con miembros a losque se puede asignar el valor DbNull.

En las secciones siguientes se explica cómo utilizar las características del precedenteADO.NET 2.0 con ejemplos de código derivado de los proyectos-ejemplo de formula-rios Windows.

2.1.1 Utilizar DbProviderFactories para crear proyectos con bases de datos agnósticas

La nueva clase System.Data.Common.DbProviderFactories proporciona a los desarrolla-dores de bases de datos la oportunidad de enfrentarse a la creación de aplicacionesagnósticas frente a las fuentes de datos. Crear aplicaciones de entradas de datos no-tri-viales que puedan interactuar sin fisuras con todos los administradores de bases dedatos relacionales, para los que existen proveedores de datos controlados, no es preci-samente algo simple. Las diferencias menores en la sintaxis SQL, tipos de datos, dialec-tos de procedimientos almacenados, tratamiento de error, y otras características pro-pias de una base de datos, requerirán sin duda un esfuerzo. Si actualmente utiliza elproveedor de datos controlados .NET Framework OleDb, o ADODB con proveedoresOLE DB para asegurar la interoperabilidad de las bases de datos, seguramente encon-trará que Microsoft y el tercero en cuestión, ADO.NET, ofrecen mejor rendimiento y,como resultado, mayor escalabilidad. Por otra parte, el nivel de ampliación y mejoraque .NET garantiza a los proveedores de datos, hace difícil escribir código que sea total-mente transparente al proveedor.

Los grupos de terceros de proveedores controlados .NET pueden reducir la interoperabilidad concostes de licencia añadidos. Por ejemplo, DataDirect Technologies ofrece proveedores de datoscontrolados para IBM DB2 y DB2 UDB; Oracle 8i, 9i, y 10g; SQLServer 7 y 2000; SybaseAdaptive Server 11.5 y 11.9; y Sybase Adaptive Server Enterprise 12.0 y 12.5. Todos los prove-edores DataDirect buscan salidas para minimizar las diferencias de sintaxis SQL y comunicar-se con servidores a través de los protocolos de los vendedores de bases de datos.

38

Bases de datos con Visual Basic

VisualBasic2005_02.qxp 02/08/2007 16:14 PÆgina 38

Page 10: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

Crear un objeto DataReader de la clase DbProviderFactories es un proceso en siete pasos:1. Crear un objeto DbProviderFactory pasando el nombre completo de la clase del pro-

veedor de datos, como System.Data.SqlClient, al argumento de una sentenciaDimFactoryNameAsDbProviderFactory = DbProviderFactories.GetFactory(strProvider).

2. Crear un objeto IdbConnection invocando el método DimConnectionNameAsIDbCon-nection = FactoryName.CreateConnection().

3. Definir el valor de la propiedad ConnectionName.Connection.String.4. Crear un objeto IdbCommand invocando el método DimCommandNameAsIDbCom-

mand = ConnectionName.CreateCommand().5. Definir para las propiedades CommandName.CommandType (opcional) y Command-

Name.CommandText los valores adecuados para el proveedor.6. Llamar al método ConnectionName.Open().7. Crear un objeto IdataReader invocando el método DimReaderNameAsIDataReader =

CommandName.ExecuteReader.

El objeto IDataReader tiene los miembros que los DataReaders específicos del proveedorpara ADO.NET 1.x y 2.0, más el nuevo método GetSchemaTable que se describe en el si-guiente apartado.

El proyecto de ejemplo DbFactoryTest.sln muestra datos en pantalla de una de las trestablas Northwind creando y atravesando los objetos IDataReader de SqlClient, OleDb, uOdbc, que se especifiquen seleccionando la opción apropiada. El formulario incluyetambién un control DataGridView con el que se muestra en pantalla el esquema de tablaDataTable (del que trata el siguiente apartado) tal como muestra la siguiente figura.

El siguiente listado contiene el código para las declaraciones de variables y el botón deopción del manejador de eventos de OleDb DbProviderFactory:

39

Las novedades de ADO.NET 2.0

VisualBasic2005_02.qxp 02/08/2007 16:14 PÆgina 39

Page 11: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

'OleDb provider settings - Products tablePrivate strOleDbProvider As String = "System.Data.OleDb"Private strOleDbConn As String = "Provider=SQLOLEDB;Data Source=.\SQLEXPRESS;" + _

"Initial Catalog=Northwind;Integrated Security=SSPI"Private strOleDbTable As String = "Products"

Private Sub optOleDb_CheckedChanged(ByVal sender As System.Object, _ByVal e As System.EventArgs) Handles optOleDb.CheckedChanged

If optOleDb.Checked = True ThenPopulateList(strOleDbProvider, strOleDbConn, strOleDbTable)Me.Text = "DbFactory Test Form - OleDb"

End IfEnd Sub

El tratador de eventos optOleDB_CheckedChanged pasa los valores requeridos delparámetro OleDb al procedimiento PopulateList, ampliado con el código siguiente:

Private Sub PopulateList(ByVal strProvider As String, _ByVal strConn As String, ByVal strTable As String)

Dim cnFactory As IDbConnection = NothingDim drData As IDataReader = NothingTry

Dim dpFactory As DbProviderFactory = _DbProviderFactories.GetFactory(strProvider)

cnFactory = dpFactory.CreateConnection()cnFactory.ConnectionString = strConnDim cmFactory As IDbCommand = cnFactory.CreateCommandcmFactory.CommandType = CommandType.TextcmFactory.CommandText = "SELECT * FROM " + strTablecnFactory.Open()drData = cmFactory.ExecuteReader(CommandBehavior.KeyInfo)lstData.Items.Clear()Dim dtSchema As DataTableWith drData

While drData.ReadlstData.Items.Add(.GetValue(0).ToString + _" - " + .GetValue(1).ToString)

End WhiledtSchema = drData.GetSchemaTable()With dgvSchema

If dtSchema.Columns.Count > 1 Then.RowHeadersVisible = False.DataSource = dtSchema.AutoGenerateColumns = TrueApplication.DoEvents()If .Columns.Count > 0 Then

.Columns(0).Frozen = True

.Columns("BaseSchemaName").Width = 110If .Columns.Count = 24 Then

.Columns(23).Width = 200End If

End If

40

Bases de datos con Visual Basic

VisualBasic2005_02.qxp 02/08/2007 16:14 PÆgina 40

Page 12: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

End IfEnd With

End WithIf dgvSchema.Columns.Count > 0 Then

Dim intCtr As IntegerDim strDataCols As String = ""For intCtr = 0 To dgvSchema.Rows(0).Cells.Count - 1

strDataCols += dgvSchema.Columns(intCtr).Name + vbTab + _dgvSchema.Rows(0).Cells(intCtr).Value.ToString + vbCrLf

Next intCtrintCtr = 0

End IfCatch exc As Exception

MsgBox(exc.Message + exc.StackTrace)Finally

If Not drData Is Nothing ThendrData.Close()

End IfIf Not cnFactory Is Nothing Then

cnFactory.Close()End If

End TryEnd Sub

Hay que especificar CommandBehavior.KeyInfo como el argumento ExecuteReader para devol-ver las claves primarias correctas y las propiedades de campo relacionadas.

Si sus proyectos deben incluir independencia respecto al proveedor de datos y está dis-puesto a escribir más para especificar las diferencias, sutiles o no, entre las diferentesmejoras de los proveedores de datos, pruebe con DbProviderFactories. Sin embargo,tenga en cuenta que el código independiente de proveedores tiene que usar tipos dedatos originales .NET, antes que los tipos de datos específicos de cada proveedor paralos diferentes add-in de SQLServer, Oracle, y otros servidores soportados por terceros.

DbProviderFactories mejora la vinculación de la base de datos, lo que deja en clara desventa-ja a muchas propiedades del modelo de programación de ADO.NET. El SQL específico de unvendedor y la sintaxis de ejecución de los procedimientos almacenados hacen que escribircódigo transparente al vendedor con los proveedores de datos ADO.NET 2.0 sea difícil, si noimposible.

2.1.2 Restablecer los esquemas de las tablas baseLos DataReaders de ADO.NET 1.x y 2.0 y los DataTableReaders de ADO.NET 2.0, tienenun método GetSchemaTable que devuelve los correspondientes esquemas del objeto enun objeto DataTable. Para dar información sobre el tipo de datos utilizado en los proyec-tos, que substituye el código para los controles de vinculación que muestran y actuali-

41

Las novedades de ADO.NET 2.0

VisualBasic2005_02.qxp 02/08/2007 16:14 PÆgina 41

Page 13: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

zan las tablas base, se utilizan los valores de propiedad del esquema DataTable. Losesquemas DataTable dan valores ColumnLength para definir la propiedad MaxLength encuadros de texto y valores IsReadOnly que se pueden aplicar a la propiedad ReadOnlyde los controles de entrada de datos normal. Estos DataTable también devuelven infor-mación clave primaria como son los índices de columna y detalles de autoincremen-tación.

El System.Data.ObjectSpaces.ObjectDataReader, que se incluía en las primeras versiones alphay Community Technical Preview de VS 2005, daban miembros similares a los de otrosDataReaders, incluido el método GetSchemaTable. En Mayo del 2004, Microsoft anunció queObjectSpaces se lanzaría como componente dentro de las mejoras del sistema de archivosWinFS.

Para crear un esquema DataTable en un DataReader y poblar una DataGridView paramostrar las propiedades de columna se ha de utilizar código parecido al siguiente:

Dim dtSchema As DataTable

With drData

While drData.Read

lstData.Items.Add(.GetValue(0).ToString + _

" - " + .GetValue(1).ToString)

End While

dtSchema = drData.GetSchemaTable()

With dgvSchema

If dtSchema.Columns.Count > 1 Then

.RowHeadersVisible = False

.DataSource = dtSchema

.AutoGenerateColumns = True

Application.DoEvents()

If .Columns.Count > 0 Then

.Columns(0).Frozen = True

.Columns("BaseSchemaName").Width = 110

If .Columns.Count = 24 Then

.Columns(23).Width = 200

End If

End If

End If

End With

End With

El esquema DataTable contiene una fila por cada columna de tabla base y 27 campos depropiedades de columna SqlDataReader. OleDbDataReaders y OdbcDataReaders devuel-ven 18 propiedades; los DataTableReaders tienen 25 campos de propiedades. Como elobjeto DataTableReader es nuevo en ADO.NET 2.0, en la tabla siguiente se comparan elíndice de campos del esquema DataTable y los nombres de propiedades de las tres cla-ses de DataReaders.

42

Bases de datos con Visual Basic

VisualBasic2005_02.qxp 02/08/2007 16:14 PÆgina 42

Page 14: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

Index SqlDataReader OleDb y Odbc DataTableReaderDataReaders

0 ColumnName ColumnName ColumnName

1 ColumnOrdinal ColumnOrdinal ColumnOrdinal

2 ColumnSize ColumnSize ColumnSize

3 NumericPrecision NumericPrecision NumericPrecision

4 NumericScale NumericScale NumericScale

5 IsUnique DataType DataType

6 IsKey ProviderType ProviderType

7 BaseServerName IsLong IsLong

8 BaseCatalogName AllowDBNull AllowDBNull

9 BaseColumnName IsReadOnly IsReadOnly

10 BaseSchemaName IsRowVersion IsRowVersion

11 BaseTableName IsUnique IsUnique

12 DataType IsKey IsKey

13 AllowDBNull IsAutoIncrement IsAutoIncrement

14 ProviderType BaseSchemaName BaseCatalogName

15 IsAliased BaseCatalogName BaseSchemaName

16 IsExpression BaseTableName BaseTableName

17 IsIdentity BaseColumnName BaseColumnName

18 IsAutoIncrement AutoIncrementSeed

19 IsRowVersion AutoIncrementStep

20 IsHidden DefaultValue

21 IsLong Expression

22 IsReadOnly ColumnMapping

23 ProviderSpecificDataType BaseTableNamespace

24 DataTypeName BaseColumnNamespace

25 XmlSchema CollectionDatabase

26 XmlSchema CollectionOwningSchema

27 XmlSchema CollectionName

Las propiedades que se muestran en negrita son miembros de la nueva clase de ADO.NET 2.0System.Data.Common .SchemaTableColumn y son necesarias. El resto son miembros opciona-les de la clase SystemData.Common.SchemaOptionalTableColumn. Los campos XmlSchema-Collection aparecen sólo en las tablas del SQLServer 2005 y especifican el esquema, si existe, delos campos para los tipos de datos xml.

Los desarrolladores de bases de datos pueden traducir la mayor parte de las propieda-des incluidas en la tabla-lista anterior. Por eso la tabla siguiente sólo ofrece la lista delas propiedades cuyo significado no es obvio o que devuelven valores inesperados.

43

Las novedades de ADO.NET 2.0

VisualBasic2005_02.qxp 02/08/2007 16:14 PÆgina 43

Page 15: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

Nombre de la propiedad Descripción

ColumnSize Devuelve –1 si el dato no está disponible, de lo contrario, el tamaño de la columna en bytes.

DataType El tipo de datos original de .NET que corresponde al tipo de datode la columna, como en System.Int32 o System.String.

ProviderType El valor íntegro de una enumeración de tipo de datos especifícos del proveedor.

IsLong True indica un tipo de datos text o ntext de SQL, o un image,y un campo de objeto OLE o Jet Memo.

ProviderSpecificDataType Uno de los tipos Sql, como SqlString o SqlInt32 (sólo SqlClient)

Expression La expresión calculada para una columna de una DataTable (sóloDataTable)

ColumnMapping Un valor String que especifica la columna de la tabla de destino o1 si la columna no está mapeada (sólo DataTable)

BaseTableNamespace El nombre de espacio XML asignado a la tabla, heredado del nombre de espacio del DataSet si está vacío (sólo DataTable)

BaseColumnNamespace El nombre de espacio XML asignado a la tabla, heredado del nombre de espacio del DataSet si está vacío (sólo DataTable)

XmlSchema Collection El nombre de la base de datos del servidor SQL Server 2005 que Database contiene el conjunto de esquemas para una columna del tipo

xml (null si la columna xml no tiene esquema)

XmlSchema CollectionOwning Esquema relacional del SQL Server 2005 que contiene el conjun-Schema to de XmlSchema (null si la columna xml no tiene esquema)

XmlSchema CollectionName Nombre del conjunto de esquemas para una columna del tipo xml (null si la columna xml no tiene esquema)

Más adelante en este capítulo, se describe cómo cargar y persistir DataTables desdebases de datos y archivos XML, y mostrar en pantalla la información del esquema delos objetos DataTable.

2.2 Comprobar las instancias de servidor SQL disponibles y los proveedores de datos ADO.NET 2.0

El método System.Data.Common.SqlDataSourceEnumerator.Instance.GetDataSources de-vuelve una DataTable que tiene una fila para cada instancia de servidor SQL 2000 y 2005accesibles. Las columnas muestran las propiedades ServerName, InstanceName,IsClustered, y Version.

Al invocar el método DbProviderFactories.GetFactoryClasses(), éste devuelve una tablasimilar con una fila para cada proveedor Microsoft de datos controlados .NET instala-

44

Bases de datos con Visual Basic

VisualBasic2005_02.qxp 02/08/2007 16:14 PÆgina 44

Page 16: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

dos en el sistema, con columnas para las propiedades del proveedor Name, Description,InvariantName, y AssemblyQualifiedName y el número de SupportedClasses. Los provee-dores de datos a terceros, como Oracle ODP.NET con Oracle 10g (Oracle.DataAccess.dll),no aparecen en la tabla.

El archivo machine.config contiene un elemento para cada uno de los cuatro espacios de nom-bre de proveedores de datos ADO.NET 2.0, y una sección system.data que añade estos provee-dores a DbProviderFactories. El método GetFactoryClasses lee el archivo machine.config paraproporcionar la lista de proveedores instalados.

El siguiente código, del proyecto de ejemplo DataEnums.sln, puebla dos controles Data-GridView con una instancia de SQLServer y un proveedor instalado de datos .NET deMicrosoft:

Private Sub frmDataEnums_Load(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles MyBase.Load

Dim dtServers As DataTable = SqlDataSourceEnumerator.Instance.GetDataSources

With dgvServers

.DataSource = dtServers

.AutoGenerateColumns = True

.RowHeadersVisible = False

.BorderStyle = BorderStyle.None

End With

Dim dtProviders As DataTable = DbProviderFactories.GetFactoryClasses()

With dgvProviders

.DataSource = dtProviders

.AutoGenerateColumns = True

.RowHeadersVisible = False

.RowTemplate.Height = 22

.BorderStyle = BorderStyle.None

End With

End Sub

Al ejecutar el proyecto DataEnums, éste enumera las instancias de SQLServer y los pro-veedores de datos instalados. La Figura siguiente muestra una instancia por defecto de unSQLServer 2000 (OAKLEAF-W2K3), y una instancia MSDE con nombre (OAKLEAF-W2K3\SHAREPOINT), una instancia de SQLServer 2005 (OAKLEAF-MS18), y una ins-tancia SQLExpress (SQLX) con nombre (OAKLEAF-MS18\SQLEXPRESS), así como pro-veedores de datos accesibles o instalados en el ordenador de desarrollo utilizado paraescribir este libro.

45

Las novedades de ADO.NET 2.0

VisualBasic2005_02.qxp 02/08/2007 16:14 PÆgina 45

Page 17: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

2.2.1 Entradas Batch en tablas de servidor SQL con el objeto SqlBulkCopy

La utilidad BCP del SQLServer y la sentencia BULK INSERT son los métodos tradicio-nales para añadir filas muy rápidamente a las tablas del SQL Server. ADO.NET 2.0 ofre-ce una opción alternativa: programar el nuevo objeto SqlBulkCopy. La fuente más habi-tual para las filas son los DataReader en tablas relacionales. Otra alternativa es insertarfilas desde documentos tabulares XML creando un juego de datos en tiempo de ejecu-ción runtime con una o más tablas de datos para copiar.

Copiar documentos XML a las tablas del servidor SQL (un proceso llamado shredding) esmucho más sencillo con SqlBulkCopy que con la propiedad para cargar de SQLXML3.0. Cargarrequiere un esquema XML anotado para mapear elementos o atributos y añadirlos a las colum-nas de las tablas base. SqlBulkCopy tiene una colección de ColumnMappings que permite defi-nir la relación entre las columnas de la tabla de datos fuente y las de la tabla base destino.

Para insertar filas de un DataReader en una tabla base destino ya existente, hay que:

1. Crear una conexión y un comando para los datos fuente. Se puede usar cualquierproveedor .NET para conectarse a la fuente de datos y crear el DataReader.

2. Aplicar el método Command.ExecuteReader para crear el DataReader.3. Crear un objeto nuevo SqlBulkCopy que tendrá como argumentos el string de cone-

xión y la enumeración apropiada en SqlBulkCopyOptions.4. Definir el valor de la propiedad SqlBulkCopy.DestinationTableName.5. Añadir miembros ColumnMapping a la colección ColumnMappings si el esquema de

la tabla destino difiere de la tabla o la petición fuente.6. Definir otros valores opcionales para la propiedad SqlBulkCopy, como BatchSize y

BulkCopyTimeout.

46

Bases de datos con Visual Basic

VisualBasic2005_02.qxp 02/08/2007 16:14 PÆgina 46

Page 18: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

7. Si la operación de copia implica un número muy alto de registros o ejecuciones conuna conexión de red muy lenta, añadir un tratador para el evento SqlBulkCopy.Sql-RowsCopied a fin de mostrar en pantalla el número o el porcentaje de registroscopiados.

8. Invocar el método SqlBulkCopy.WriteToServer para ejecutar la operación de copia.9. Aplicar el método SqlBulkCopy.Close() y, si ya ha terminado, cierre la conexión. En

caso contrario, use de nuevo el objeto SqlBulkCopy para realizar cualquier otra ope-ración.

La tabla siguiente describe los miembros de la enumeración SqlBulkCopyOptions.

Nombre del miembro Descripción

CheckConstraints Aplica un chequeo restringido durante el proceso de copia.

Default No utiliza opciones (por defecto) para la operación de copiar.

FireTriggers Permite a detonadores INSERT dispararse durante el proceso de copia.

KeepIdentity Utiliza valores de identificación de la tabla fuente en lugar de generar nuevos valores de identidad basados en los valores de integridad e incremento de la tabla destino.

KeepNulls Conserva los valores null de la tabla fuente a pesar de los valores por defecto de las tablas destino.

TableLock Aplica un candado a toda la tabla durante el proceso de copia, en lugar del candado por defeccto aplicado por filas.

UseInternalTransaction Hace que cada batch de la copia bulk se ejecute dentro de una tran-sacción.

KeepIdentity es el miembro más importante de la enumeración SqlBulkCopyOptions para tablasque usan una columna de identificación como clave primaria. Si no se especifica esta opción, lasclaves de la tabla destino podrían ser distintas de los valores en la tabla fuente. También es con-veniente añadir la opción UseInternalTransaction para prevenir copias parciales si ocurrieraalguna excepción durante el proceso.

El ejemplo más sencillo de una operación SqlBulkCopy crea copias de tablas en la mismabase de datos. El siguiente código del proyecto BulkCopySameSchema.sln copia las tablasde productos Northwind (Northwind Products) como ProductsCopy:

Private Sub btnCopyProds_Click(ByVal sender As System.Object, ByVal e As

System.EventArgs) Handles btnCopyProds.Click

Dim sdrProds As SqlDataReader = Nothing

Dim sbcProds As SqlBulkCopy = Nothing

Try

Dim lngTime As Long = Now.Ticks

btnCopyProds.Enabled = False

cnnNwind.Open()

cmdProds.CommandText = "DELETE FROM ProductsCopy"

Dim intRecs As Integer = cmdProds.ExecuteNonQuery

47

Las novedades de ADO.NET 2.0

VisualBasic2005_02.qxp 02/08/2007 16:14 PÆgina 47

Page 19: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

cmdProds.CommandText = "SELECT * FROM Products"sdrProds = cmdProds.ExecuteReader()If chkKeepIdentity.Checked Then

sbcProds = New SqlBulkCopy(strConn, _SqlBulkCopyOptions.UseInternalTransaction Or _SqlBulkCopyOptions.KeepIdentity)

ElsesbcProds = New SqlBulkCopy(strConn, _

SqlBulkCopyOptions.UseInternalTransaction)Dim blnUseCm As Boolean = TrueIf blnUseCm Then

sbcProds.ColumnMappings.Clear()Dim intCol As IntegerFor intCol = 1 To 9

sbcProds.ColumnMappings.Add(intCol, intCol)Next intCol

End IfEnd IfAddHandler sbcProds.SqlRowsCopied, New

SqlRowsCopiedEventHandler(AddressOf ProdRowAdded)With sbcProds

.DestinationTableName = "ProductsCopy"

.BatchSize = CInt(nudBatchSize.Value)

.BulkCopyTimeout = 30

.NotifyAfter = 1

.WriteToServer(sdrProds)

.Close()End WithsdrProds.Close()lngTime = Now.Ticks - lngTimetxtTime.Text = Format(lngTime / 10000000, "0.000")FillProdsList(True)

Catch excCopy As ExceptionMsgBox(excCopy.Message + excCopy.StackTrace, , "Products Bulk Copy

Exception")Finally

If Not sbcProds Is Nothing ThensbcProds.Close()

End IfIf Not sdrProds Is Nothing Then

sdrProds.Close()End IfIf Not cnnNwind Is Nothing Then

cnnNwind.Close()End IfbtnCopyProds.Enabled = True

End TryEnd Sub

48

Bases de datos con Visual Basic

VisualBasic2005_02.qxp 02/08/2007 16:14 PÆgina 48

Page 20: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

La propiedad SqlBulkCopy.NotifyAfter determina el número de filas añadidas antes dedispararse el evento SqlRowsCopied. A continuación vemos el código para un tratadorde eventos SqlRowsCopied que muestra el progreso del proceso de copia de las tablas deproductos en un cuadro de texto:

Sub ProdRowAdded(ByVal oSource As Object, ByVal oArgs As SqlRowsCopiedEventArgs)

txtProdRows.Text = oArgs.RowsCopied.ToString

Application.DoEvents()

End Sub

Mostrar el progreso de la copia reduce sustancialmente la velocidad de la copia. En las aplica-ciones finales que deben proporcionar interacción al usuario, el valor de la propiedadNotifyAfter debe ser como mínimo el 10 por ciento del número total de registros añadidos.

La siguiente figura muestra el formulario del proyecto BulkCopySameSchema.sln des-pués de copiar las dos tablas. Los scripts Transact-SQL recrean la tabla en el manejadorde eventos frmBulkCopy_Load. Los cuadros de lista muestran la clave primera de latabla fuente y los valores de segunda columna cuando se carga el formulario, y losvalores de la tabla destino después de la copia. El deslizador Batch Size determina elnúmero de filas por intervalo; 0 (el valor por defecto) intenta enviar todas las filas alservidor en un solo intervalo. Definiendo 1 para el tamaño del intervalo y copiando denuevo las tablas se puede comparar el rendimiento de la copia frente a las operacionesfila por fila.

Cachear datos y código provoca una diferencia considerable entre el tiempo de ejecución de lacopia bulk inicial y las siguientes. Por lo tanto, habría que comparar los tiempos de ejecucióncon batchs de diferentes tamaños después de una o dos pruebas con un tamaño de batch defini-do en 0.

Deseleccionando el cuadro de verificación Keep Source Identity, la opción KeepIdentity seelimina del constructor SqlBulkCopy de la tabla de productos. En este caso, los valoresde clave primarios se incrementan en 77 por cada operación de copia. En el apartado

49

Las novedades de ADO.NET 2.0

VisualBasic2005_02.qxp 02/08/2007 16:15 PÆgina 49

Page 21: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

siguiente se describe el tratador de eventos para el botón Show Connection Statistics(Mostrar estadísticas de conexión).

2.2.2 Obtener las estadísticas de conexión del servidor SQL

El nuevo método SqlConnection.RetrieveStatistics averigua la instancia del servidor SQLcon los datos de la conexión actual y devuelve un objeto IDictionary que contiene los 18pares nombre/valor que muestra la siguiente figura.

Esta propiedad se ha de permitir explícitamente ejecutando una instrucción SqlConnec-tion.EnableStatistics=True antes de invocar el método RetrieveStatistic. El método mássencillo para para tratar los valores nombre/valor es encrustar el objeto IDictionary enun tipo HashTable y, después reiterar la tabla Hash en un bucle ForEach...Next. El códi-go siguiente del proyecto BulkCopySameSchema.sln muestra en pantalla las estadísticasen un cuadro de texto de un sencillo formulario frmConnStats:

Private Sub btnShowStats_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles btnShowStats.Click

Try

htStats = CType(cnnNwind.RetrieveStatistics(), Hashtable)

Dim txtStats As Control = frmConnStats.Controls.Item("txtStats")

txtStats.Text = ""

Dim oStat As Object

Dim strStat As String

For Each oStat In htStats.Keys

strStat = oStat.ToString

If InStr(strStat, "Time") > 0 Then

txtStats.Text += strStat + " = " + _

50

Bases de datos con Visual Basic

VisualBasic2005_02.qxp 02/08/2007 16:15 PÆgina 50

Page 22: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

Microsoft.VisualBasic.Format(CLng(htStats(strStat)) /1000, _

"#,##0.000") + " secs" + vbCrLf

Else

txtStats.Text += strStat + " = " + htStats(strStat).ToString

+ vbCrLf

End If

Next

frmConnStats.Show()

frmConnStats.Controls.Item("btnClose").Focus()

Catch excStats As Exception

MsgBox(excStats.Message + excStats.StackTrace, , _

"Exception Displaying Connection Statistics")

End Try

End Sub

El código anterior y el formulario frmConnStats se pueden añadir a cualquier proyectoque utilice una SqlConnection. Invoque el método SqlConnection.ResetStatistics para ini-cializar los datos, excepto ConnectionTime.

Recuperar las estadísticas de conexión requiere establecer de nuevo una conexión con el ser-vidor, por lo tanto es mejor reservar el uso de esta función para diagnosticar problemas deconexión.

2.3 Ejecutar comandos SQL de forma asincrónicaADO.NET 2.0 añade los métodos BeginExecuteReader, BeginExecuteXmlReader, y Begin-ExecuteNonQuery (junto con los correspondientes métodos End) para las clases Sql-Command. Estos métodos permiten ejecutar código mientras se espera a que un coman-do complete su ejecución. Para ejecutar un comando SqlCommand hay que añadirAsync=True a la cadena de comando que se pasó al constructor de la SqlConnection. Enlos apartados siguientes se describe, con el correspondiente código de ejemplo, para lostres modelos de ejecución de comandos SqlCommand asíncronos que soporta la interfazIasyncResul. La siguiente figura ilustra las bases de datos, conexiones y comandos quese utilizan con los tres modelos. Obtendrá resultados más interesantes del proyecto deejemplo AsyncDataOperations.sln si dispone de dos o tres instancias del servidor SQLServer 2000 o 2005 con la base de datos de ejemplo de Northwind para cada instancia(figura en la página siguiente).

El proveedor de memoria compartida por defecto del SQLServer 2000 no soporta comando así-cronos, por lo que hay que utilizar localhost, y no (local), como valor para el servidor o la fuen-te de datos de la cadena de conexión en cualquier instancia local del SQLServer 2000.

51

Las novedades de ADO.NET 2.0

VisualBasic2005_02.qxp 02/08/2007 16:15 PÆgina 51

Page 23: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

2.3.1 El modelo PollingEl modelo Polling es el más sencillo de los tres. La siguiente figura ilustra el flujo delprograma para tres conexiones asíncronas (figura en la página siguiente).

El código siguiente abre un comando asíncrono en la base de datos Northwind, en unainstancia del servidor local SQL y utiliza un bucle While que consulta constantementepara que se complete el método BeginExecuteReader:

Private Sub PollingAsyncCommand()Try

Dim strConn As String = "Data Source=localhost;" + _"Initial Catalog=Northwind;Integrated Security=SSPI;Async=True"

Dim cnnCusts As SqlConnection = New SqlConnection(strConn)cnnCusts = New SqlConnection(strCusts)Dim cmdCusts As SqlCommand = cnnCusts.CreateCommandWith cmdCusts

.CommandType = CommandType.Text

.CommandTimeout = 60

.CommandText = "SELECT * FROM Customers"End With

Dim asrCustsReader As IAsyncResult = _cmdCusts.BeginExecuteReader(CommandBehavior.CloseConnection)

While Not asrCustsReader.IsCompleted'Do something while waiting

52

Bases de datos con Visual Basic

VisualBasic2005_02.qxp 02/08/2007 16:15 PÆgina 52

Page 24: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

End WhileDim sdrCusts As SqlDataReader = cmdCusts.EndExecuteReader(asrCustsReader)'Do something with the datasdrCusts.Close()sdrCusts.Dispose()

Catch excAsync As ExceptionMsgBox(excAsync.Message + excAsync.StackTrace, , "Async Operation

Exception")End Try

End Sub

La ejecución asíncrona con polling es muy práctica para las operaciones sencillas den-tro del bucle While, como mostrar una barra de progresión cuyo valor vienen definidopor las pulsaciones e un contador. También se puede incluir código que permita alusuario cancelar un comando antes del tiempo indicado por su propiedad Com-mandTimeout. Al salir del bucle, la ejecución del mismo queda bloqueada hasta que sehayan completado todos los comandos o haya expirado su tiempo de ejecuión. El códi-go se va ejecutando en el hilo del formulario, por lo que los comandos múltiples se eje-

53

Las novedades de ADO.NET 2.0

VisualBasic2005_02.qxp 02/08/2007 16:15 PÆgina 53

Page 25: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

cutan secuencialmente en conexiones separadas. Si las operaciones múltiples Data-Reader.Read son complejas, se pueden ejecutar en un hilo dedicado al nuevo objetoBackgroundWorker. Esto permite invocar el siguiente método BeginExecuteReader inme-diatamente después de que la propiead IAsyncResult.IsComplete cambie a True.

2.3.2 El módelo CallbackEl módelo asíncrono callback es más flexible que el polling porque utiliza un manejadorde callback que ejecuta su propio hilo, extraído de la consulta. El modelo callback permi-te entrelazar comandos con bases de datos múltiples que se ejecutan en lo mismos ser-vidores o en servidores distintos. En ese caso, hay que especificar el tratador callback ypasarle el comando como objeto al segundo parámetro del método sobrecargado Be-ginExecuteReader. Al pasar el comando se tiene acceso al método EndExecuteReader conla propiedad IAsyncResult.AsyncState en el tratador callback. La siguiente figura mues-tra el flujo del programa en el modo callback. Las líneas punteadas indican la ejecucióndirecta de los métodos Read, sin tener que esperar a que estén disponibles dotos los jue-gos de filas.

54

Bases de datos con Visual Basic

VisualBasic2005_02.qxp 02/08/2007 16:15 PÆgina 54

Page 26: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

A continuación mostramos un ejemplo de código para un comando asíncrono sencilloSqlCommand que usa el método callback:

Private Sub CallbackAsyncCommand()

Try

Dim strConn As String = "Data Source=localhost;" + _

"Initial Catalog=Northwind;Integrated Security=SSPI;Async=True"

Dim cnnCusts As SqlConnection = New SqlConnection(strConn)

cnnCusts = New SqlConnection(strCusts)

Dim cmdCusts As SqlCommand = cnnCusts.CreateCommand

With cmdCusts

.CommandType = CommandType.Text

.CommandTimeout = 60

.CommandText = "SELECT * FROM Customers"

End With

cnnCusts.Open()

Dim objCmdCusts As Object = CType(cmdCusts, Object)

Dim asrCustsReader As IAsyncResult = _

cmdCusts.BeginExecuteReader(New AsyncCallback(AddressOf

CustomersHandler), _

objCmdCusts, CommandBehavior.CloseConnection)

Catch excAsync As Exception

MsgBox(excAsync.Message + excAsync.StackTrace, , "Async Operation

Exception")

End Try

End Sub

Y aquí está el código del tratador callback para el procedimiento anterior:Private Sub CustomersHandler(ByVal iarResult As IAsyncResult)

Try

Dim sdrData As SqlDataReader = CType(iarResult.AsyncState,

SqlCommand).EndExecuteReader(iarResult)

With sdrData

Dim intCtr As Integer

While .Read

For intCtr = 0 To .FieldCount - 1

objData = .GetValue(intCtr)

Next intCtr

End While

.Close()

.Dispose()

End With

Dim blnIsPool As Boolean = Thread.CurrentThread.IsThreadPoolThread

CustomersDone(Thread.CurrentThread.ManagedThreadId, blnIsPool)

Catch excHandler As Exception

MsgBox(excHandler.Message + excHandler.StackTrace, , "Customers

Handler Exception")

End Try

End Sub

55

Las novedades de ADO.NET 2.0

VisualBasic2005_02.qxp 02/08/2007 16:15 PÆgina 55

Page 27: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

La mayor parte de los ejemplos de cliente de este libro conectan con finales back en la mismamáquina que el cliente; eso significa que la ejecución sincrónica de los DataReaders se comple-ta rápidamente o bien arroja inmediatamente una excepción. La ejecución asincrónica resultaespecialmente eficaz en los proyectos con DataReaders múltiples que conectan individualemen-te a bases de datos remotas, especialmente si una o más conexiónes se ejecutan en un WAN.

El proyecto de ejemplo AsyncDataOperations.sln simula una aplicación de producciónque conecta a bases de datos trabajadas en red múltiple estableciendo conexiones indi-viduales SqlConnections con tablas Northwind de clientes, pedidos y detalles de pedidos(Customers, Orders, y Order Details). Si tiene acceso a tres instancias de servidor SQLpuede modificar las cadenas de conexión cambiando los nombres del segundo y el ter-cer servidor (OAKLEAF-W2K3 y OAKLEAF-MS2K3) por RemoteServerName, y seleccio-nar el cuadro de texto Use Multiple Instances para mostrar la secuencia de invocacionesde los métodos Connection.Open, BeginExecuteReader, y EndExecuteReader. La siguientefigura muestra dos instancias del formulario AsyncDataOperations.

Una clase timer VB.NET, escrita por Alastair Dallas, proporciona la resolución requerida paraobtener datos de sincronización con sentido. Los números entre paréntesis de las entradas delcuadro de lista son los valores System.Threading.Thread .CurrentThread.ManagedThreadId delas instancias del formulario y los tres manejadores de callback El sufijo P indica que los hilosdel manejador son del pool de hilos. La sincronización de datos es para una segunda ejecución(cacheada).

El código de ejemplo ejecuta objetos Customer desde el host local y objeto Orders yOrder Details desde los servidores de red. La tabla Orders Details tiene unas 500.000 filas,por lo que leer toda la tabla lleva unos 2 segundos. La velocidad de ejecución en unaLAN de bajo tráfico es normalmente suficiente para devolver los datos en la secuenciaBeginExecuteReadercalling, como muestra la figura anterior (izquierda). Todas las opera-ciones de restablecimiento de datos se ejecutan en un solo hilo (13). Para simular unaconexión WAN con la tabla Orders, el código en OrdersHandler provoca un retraso deunos segundos mediante múltiples operaciones en cada fila DataReader en un bucle ani-

56

Bases de datos con Visual Basic

VisualBasic2005_02.qxp 02/08/2007 16:15 PÆgina 56

Page 28: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

dado. En este caso, DataReader de Orders completa la ejecución antes que el DataReaderde Customers, el cual termina la ejecución antes que el DataReader de Order Details, talcomo muestra la figura anterior (derecha). En este caso, la restauración de Order Detailsse ejecuta en un hilo (14P), y Customers y Orders en otro distinto (13P).

El uso del modelo callback en las aplicaciones de formulario Windows es un tema controverti-do. Miembros del equipo de datos de VS 2005 de Microsoft recomiendan no utilizar este mode-lo con los proyectos de formulario de Windows. Los objetos de ADO.NET no son seguros en loshilos, y los problemas con hilos son difíciles de depurar.

2.3.3 El modelo WaitAllUna alternativa al método callback es utilizar un array WaitHandle y asignarlo a un ele-mento en cada llamada de método BeginExecuteReader. Un WaitHandle.WaitAll(wh-Array) detiene la ejecución del código hasta que todos los DataReaders están listos parasus llamadas EndExecuteReader. Este comportamiento hace al modelo WaitAll especial-mente adecuado para clientes que procesan juegos de filas relacionados, ya que no senecesita el bucle de sincronización que se mostró anteriormente. La siguiente figuramuestra el diagrama de flujo en el modelo WaitAll.

57

Las novedades de ADO.NET 2.0

VisualBasic2005_02.qxp 02/08/2007 16:15 PÆgina 57

Page 29: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

La manera más sencilla de ver los resultados del método WaitAll en un entorno de for-mulario Windows es crear una versión multi-hilo o multi-threaded apartment (MTA) de unprocedimiento habitual Sub Main. Por defecto, los procedimientos de VB.NET utilizan elmodelo de hilo único (single-threaded apartment, STA) requerido para los formularios basa-dos en Win32. Llamando WaitAll con múltiples WaitHandles, arroja una excepción dentrode los procedimiento STA, por lo que hay que añadir el prefijo <MTAThreadAttribute()> ala sentencia SharedSubMain. El siguiente listado es una adaptación del código del mode-lo callback para implementar el array multi-elemento WaitHandle:

<MTAThreadAttribute()> _

Shared Sub Main()

Dim blnIsMultiServer As Boolean

Try

cnnCusts = New SqlConnection(strCusts)

Dim cmdCusts As SqlCommand = cnnCusts.CreateCommand

With cmdCusts

.CommandType = CommandType.Text

.CommandTimeout = 10

.CommandText = "SELECT * FROM Customers"

End With

If blnIsMultiServer Then

cnnOrders = New SqlConnection(strOrders)

Else

cnnOrders = New SqlConnection(strCusts)

End If

Dim cmdOrders As SqlCommand = cnnOrders.CreateCommand

With cmdOrders

.CommandType = CommandType.Text

.CommandTimeout = 10

.CommandText = "SELECT * FROM Orders"

End With

If blnIsMultiServer Then

cnnDetails = New SqlConnection(strDetails)

Else

cnnDetails = New SqlConnection(strCusts)

End If

Dim cmdDetails As SqlCommand = cnnDetails.CreateCommand

With cmdDetails

.CommandType = CommandType.Text

.CommandTimeout = 10

.CommandText = "SELECT * FROM [Order Details]"

End With

Dim timHiRes As New clsTimer

timHiRes.Start()

58

Bases de datos con Visual Basic

VisualBasic2005_02.qxp 02/08/2007 16:15 PÆgina 58

Page 30: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

Dim awhHandle(2) As WaitHandle

cnnCusts.Open()

astrListItems(0) = Format(timHiRes.ElapsedTime, "0.000") + " - 1

Opened Customers connection"

Dim asrCustomersReader As IAsyncResult

asrCustomersReader =

cmdCusts.BeginExecuteReader(CommandBehavior.CloseConnection)

awhHandle(0) = asrCustomersReader.AsyncWaitHandle

astrListItems(1) = Format(timHiRes.ElapsedTime, "0.000") + " - 2

BeginExecuteReader: Customers"

cnnOrders.Open()

astrListItems(2) = Format(timHiRes.ElapsedTime, "0.000") + " - 3

Opened Orders connection"

Dim asrOrdersReader As IAsyncResult

asrOrdersReader =

cmdOrders.BeginExecuteReader(CommandBehavior.CloseConnection)

awhHandle(1) = asrOrdersReader.AsyncWaitHandle

astrListItems(3) = Format(timHiRes.ElapsedTime, "0.000") + " - 4

BeginExecuteReader: Orders"

cnnDetails.Open()

astrListItems(4) = Format(timHiRes.ElapsedTime, "0.000") + " - 5

Opened Details connection"

Dim asrDetailsReader As IAsyncResult

asrDetailsReader =

cmdDetails.BeginExecuteReader(CommandBehavior.CloseConnection)

awhHandle(2) = asrDetailsReader.AsyncWaitHandle

astrListItems(5) = Format(timHiRes.ElapsedTime, "0.000") + " - 6

BeginExecuteReader: Order Details"

WaitHandle.WaitAll(awhHandle)

Dim sdrCustomers As SqlDataReader =

cmdCusts.EndExecuteReader(asrCustomersReader)

sdrCustomers.Close()

sdrCustomers.Dispose()

Dim sdrOrders As SqlDataReader =

cmdOrders.EndExecuteReader(asrOrdersReader)

sdrOrders.Close()

sdrOrders.Dispose()

Dim sdrDetails As SqlDataReader =

cmdDetails.EndExecuteReader(asrDetailsReader)

sdrDetails.Close()

59

Las novedades de ADO.NET 2.0

VisualBasic2005_02.qxp 02/08/2007 16:15 PÆgina 59

Page 31: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

sdrDetails.Dispose()

astrListItems(6) = Format(timHiRes.ElapsedTime, "0.000") + " - 7

EndExecuteReader: All readers"

frmAsync.ShowDialog()

Catch excAsync As Exception

MsgBox(excAsync.Message + excAsync.StackTrace, , "Async Operation

Exception")

End Try

End Sub

El primer paso es crear un array WaitHandle con el mismo número de elementos quecomandos asíncronos. Al igual que con el modelo callback, se abren las conexiones, seejecutan las instrucciones SqlCommand.BeginExecuteReader, y se añaden los correspon-dientes objetos SqlDataReader.AsyncWaitHandle al array WaitHandle sin importar el or-den. La ejecución se detiene al llegar a la instrucción WaitHandle.WaitAll(awhHandle)hasta que se completan todos los DataReaders. Al retomarse la ejecución, los juegos defilas se procesan en el orden deseado (en este caso padre, hijo, nieto).

El código Shared Sub Main del proyecto de ejemplo AsyncDataOperations.sln se puedeejecutar abriendo la ventana de propiedades del proyecto, seleccionando la página deAplicación, marcando el cuadro de verificación Habilitar marco de trabajo de la aplicacióny pulsando <Ctrl> + <S> para guardar los cambios. La siguiente figura muestra el for-mulario con el valor True para blnIsMultiServerflag.

2.3.4 Crear tablas de datos independientesLas tablas de datos de ADO.NET 1.x son miembros, normalmente, de los objetosDataSet. ADO.NET 2.0 permite crear tablas de datos ligeras, independientes, que com-parten muchos métodos DataSet, como ReadXml, ReadXmlSchema, WriteXml, y Write-XmlSchema. Las tablas de datos también soportan interfaces DataReader con el métodoLoad(DataReader) y el objeto DataTableReader. También se puede asignar un prefijo nom-bre de espacio a la tabla de datos. Los apartados anteriores de este capítulo introducí-

60

Bases de datos con Visual Basic

VisualBasic2005_02.qxp 02/08/2007 16:15 PÆgina 60

Page 32: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

an las tablas de datos y los controles DataGridView poblados por los métodos GetSche-maTable, GetDataSources, y GetFactoryClasses.

El proyecto StandaloneDataTables.sln ilustra las siguientes características de las tablas dedatos:

Crear una DataTable con un SqlDataReader, ejecutar un DataTableReader, y vincularla DataTable a una DataGridView editable.Persistir el contenido de una DataTable en archivos XML sólo para datos y esque-ma, en formato DataSet, y con ediciones de DataTable en formato “diffGram”.Definir los valores de Namespace y, opcionalmente, de la propiedad Prefix.Utilizar el método ReadXml para cargar una tabla de datos desde el archivo guar-dado DataSet.xmlMostrar en pantalla el esquema DataTable con el método DataTable.GetSchemaTable

La siguiente figura muestra el formulario del proyecto para la la tabla de datos inde-pendiente StandaloneDataTables.sln después de una mínima edición de la columna Con-tactName de la primera fila. Los botones Show... abren documentos XML guardados enInternet Explorer. La parrilla inferior muestra el esquema DataTable del SqlDataReader obien, después de pulsar el botón Reload from XMLFiles, el esquema de la tabla de datosprimaria.

El procedimiento siguiente carga una base de datos del archivo Northwind Customers,añade un nombre de espacio y un prefijo adicionales, designa la columna clave prima-ria (si falta), crea un esquema DataTable, itera la tabla de datos primaria con unDataTableReader, y activa el procedimiento LoadDataGridView para mostrar en pantalla

61

Las novedades de ADO.NET 2.0

VisualBasic2005_02.qxp 02/08/2007 16:15 PÆgina 61

Page 33: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

los contenidos y el esquema de la tabla:

Private Sub LoadFromDatabase(ByVal blnWithNamespace As Boolean)

Dim strConn As String = "Server=.\SQLEXPRESS;Integrated

Security=True;Database=Northwind"

Dim cnnNwind As SqlConnection = New SqlConnection(strConn)

Try

Dim cmdCusts As SqlCommand = cnnNwind.CreateCommand

With cmdCusts

.CommandType = CommandType.Text

.CommandText = "SELECT * FROM Customers"

End With

cnnNwind.Open()

Dim drCusts As SqlDataReader =

cmdCusts.ExecuteReader(CommandBehavior.KeyInfo)

dtCusts = New DataTable

dtSchema = drCusts.GetSchemaTable

With dtCusts

.TableName = "Customers"

If blnWithNamespace Then

'.Prefix = "custs"

.Namespace = "http://www.oakleaf.ws/schemas/northwind/custo

mers"

End If

.Load(drCusts)

.AcceptChanges()

If .PrimaryKey.Length = 0 Then

Dim acolKeys(1) As DataColumn

acolKeys(0) = .Columns(0)

.PrimaryKey = acolKeys

End If

If Not .DataSet Is Nothing Then

Dim strName As String = .DataSet.DataSetName

MsgBox(strName)

End If

End With

drCusts.Close()

Dim dtrCusts As New DataTableReader(dtCusts)

intRows = 0

While dtrCusts.Read

intRows += 1

End While

dtrCusts.Close()

62

Bases de datos con Visual Basic

VisualBasic2005_02.qxp 02/08/2007 16:15 PÆgina 62

Page 34: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

LoadDataGridViews()

Catch excDT As Exception

MsgBox(excDT.Message + excDT.StackTrace, , "DataTable Load

Exception")

Finally

cnnNwind.Close()

End Try

End Sub

Eliminar el argumento CommandBehavior.KeyInfo del método ExecuteReader method paraañadir la clave primaria con código. Las instrucciones del test prueban que las tablas de datosno generan juegos de datos de manera oculta.

Las tablas de datos que se cargan desde los DataReaders son actualizables, y se puedenpersistir como archivos de documentos XML en formatos DataSet sólo-datos, sólo-esquema o diffGram. El procedimiento SaveXmlFiles genera documentos XML de datosy esquema y mantiene el contenido de la tabla de datos en formato DataSet. El proce-dimiento guarda como archivo diffGram todas las ediciones que se hagan enDataGridView.

Private Sub SaveXmlFiles(ByVal blnShowMessage As Boolean)

DeleteXmlFiles()

With dtCusts

.WriteXml(strPath + "Data.xml",

System.Data.XmlWriteMode.IgnoreSchema)

.WriteXml(strPath + "DataSet.xml",

System.Data.XmlWriteMode.WriteSchema)

.WriteXmlSchema(strPath + "Schema.xsd")

End With

btnShowData.Enabled = True

btnShowDataSet.Enabled = True

btnShowSchema.Enabled = True

Dim dtChanges As New DataTable

dtChanges = dtCusts.GetChanges

Dim strMsg As String

If dtChanges Is Nothing Then

strMsg = "Data and schema for " + intRows.ToString + " rows written

to '" _

+ strPath + "' folder."

btnShowDiffGram.Enabled = False

Else

dtChanges.WriteXml(strPath + "Diffgram.xml",

System.Data.XmlWriteMode.DiffGram)

strMsg = "Data for " + intRows.ToString + " rows, schema, and chan

ges diffgram written to '" + _

strPath + "' folder and changes accepted."

dtCusts.AcceptChanges()

btnShowDiffGram.Enabled = True

63

Las novedades de ADO.NET 2.0

VisualBasic2005_02.qxp 02/08/2007 16:15 PÆgina 63

Page 35: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

End If

If blnShowMessage Then

MsgBox(strMsg, , "XML Files Saved")

End If

btnReadXML.Enabled = True

End Sub

El manejador de eventos btnReadXML_Click carga la tabla de datos desde el archivoguardado DataSet.xml, aplica las ediciones previas guardadas como archivo diffGram ymuestra en pantalla el esquema DataTable.

Si se añade un nombre de espacio a la tabla de datos cuando se importan valores de la tabla base,se provocará un fallo en la validación del esquema al guardar el archivo de datos XML.

Private Sub btnReadXML_Click(ByVal sender As System.Object, ByVal e As

System.EventArgs) Handles btnReadXML.Click

btnShowDiffGram.Enabled = False

Try

dtCusts = New DataTable

With dtCusts

.ReadXml(strPath + "DataSet.xml")

If File.Exists(strPath + "Diffgram.xml") Then

.ReadXml(strPath + "Diffgram.xml")

End If

.AcceptChanges()

End With

Dim dtrCusts As New DataTableReader(dtCusts)

dtSchema = dtrCusts.GetSchemaTable

intRows = 0

While dtrCusts.Read

intRows += 1

End While

dtrCusts.Close()

LoadDataGridViews()

Catch excXML As Exception

MsgBox(excXML.Message + excXML.StackTrace, , "DataTable ReadXml

Exception")

End Try

End Sub

Las tablas de datos DataTablestienen colecciones de ChildRelations y ParentRelations que per-miten añadir código para definir las relaciones entre los diferentes objetos de las tablas de datosmúltiples. En la mayoría de los casos, sin embargo, crear un juego de datos tipificado es lo mejorcuando se trabaja con proyectos que tienen más de una tabla de datos relacional (o relacionada).

64

Bases de datos con Visual Basic

VisualBasic2005_02.qxp 02/08/2007 16:15 PÆgina 64

Page 36: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

2.4 Utilizar tipos Nullable que soporten valores DBNull

.NET Framework 2.0 añade tipos genéricos a VB.NET 2005 cuando añade el parámetro(OfType) a las declaraciones de variable. Las variables de tipo nullable son una extensiónde tipos genéricos que permiten valores Integer, Int16, Decimal, Date, DateTime, y simi-lares y soportan valores nulos. Asignando Nothing a un valor, éste devuelve el valor pordefecto para el tipo (0 para los tipos numéricos y 01/01/0001 12:00:00 AM para lasfechas).

Para hacer posibles los valores nulos hay que remplazar los identificadores del tipo devalores con Nullable (OfType). Las referencias del tipo String ya soportan de por sí valo-res nulos, por lo que añadir Nullable (OfString) no resulta apropiado. La aplicación másútil de las variables de tipo nullable la tenemos en las rúbricas de método, donde lostipos de valores nullable hacen innecesaria la sobrecarga. Por ejemplo, si se inserta unafila nueva en la tabla de pedidos Northwind (Northwind Orders) desde un juego dedatos tipificado, normalmente se necesitarán las dos rúbricas de método Insert, comomostramos a continuación, y las dos funciones de sobrecarga correspondientes:

Function Insert(ByVal CustomerID As String,

ByVal EmployeeID As Integer, _

ByVal OrderDate As Date, _

ByVal RequiredDate As Date, _

ByVal ShippedDate As Date, _

ByVal ShipVia As Integer,

ByVal Freight As Decimal,

ByVal ShipName As String, _

ByVal ShipAddress As String,

ByVal ShipCity As String, _

ByVal ShipRegion As String,

ByVal ShipPostalCode As String, _

ByVal ShipCountry As String) As Integer

Function Insert(ByVal CustomerID As Object,

ByVal EmployeeID As Object, _

ByVal OrderDate As Object,

ByVal RequiredDate As Object, _

ByVal ShippedDate As Object, _

ByVal ShipVia As Object, _

ByVal Freight As Object, _

ByVal ShipName As Object, _

ByVal ShipAddress As Object, _

ByVal ShipCity As Object, _

ByVal ShipRegion As Object, _

ByVal ShipPostalCode As Object, _

ByVal ShipCountry As Object) As Integer

65

Las novedades de ADO.NET 2.0

VisualBasic2005_02.qxp 02/08/2007 16:15 PÆgina 65

Page 37: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

La primera rúbrica de método es válida si están presentes todos los valores. Si algunode los tipos de valores dados a la función es nulo, se necesita la segunda rúbrica, notipificada. En ese caso el código podría proporcionar un valor String en lugar de Integero Decimal, un error que el compilador no detectaría. Añadiendo Nullable(OfType) a lostipos de valores, tal como mostramos aquí, permite manejar valores nulos con una solafunción:

Function Insert(ByVal CustomerID As String, _

ByVal EmployeeID As Nullable(Of Integer), _

ByVal OrderDate As Nullable(Of Date), _

ByVal RequiredDate As Nullable(Of Date), _

ByVal ShippedDate As Nullable(Of Date), _

ByVal ShipVia As Nullable(Of Integer), _

ByVal Freight As Nullable(Of Decimal), _

ByVal ShipName As String, _

ByVal ShipAddress As String, _

ByVal ShipCity As String, _

ByVal ShipRegion As String, _

ByVal ShipPostalCode As String, _

ByVal ShipCountry As String) As Integer

Si queremos definir valores para los parámetros INSERT o UPDATE asociados contipos nullable, deberemos comprobar que hay un valor con la propiedad HasValue y, siel valor de HasValue es True, dárselo a la propiedad Value, tal como se muestra en elsiguiente fragmento de comando INSERT (al que se han tenido que añadir parámetros):...

Me.InsertCommandParameters(0).Value = CustomerID

If EmployeeID.HasValue Then

Me.InsertCommandParameters(1).Value = EmployeeID.Value

Else

Me.InsertCommandParameters(1).Value = DBNull.Convert

End If

If OrderDate.HasValue Then

Me.InsertCommandParameters(2).Value = OrderDate.Value

Else

Me.InsertCommandParameters(2).Value = DBNull.Convert

End If

If RequiredDate.HasValue Then

Me.InsertCommandParameters(3).Value = RequiredDate.Value

Else

Me.InsertCommandParameters(3).Value = DBNull.Convert

End If

If ShippedDate.HasValue Then

Me.InsertCommandParameters(4).Value = ShippedDate.Value

Else

Me.InsertCommandParameters(4).Value = DBNull.Convert

End If

66

Bases de datos con Visual Basic

VisualBasic2005_02.qxp 02/08/2007 16:15 PÆgina 66

Page 38: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

.Nullable(OfType) también se puede aplicar a los miembros de clase Public o Private. Acontinuación vemos un ejemplo de un sencillo objeto de negocios con propiedadesPublic que hacen un mapa de los campos de la tabla Orders. Las reglas de negocios y lasrestricciones de llave foránea determinan qué campos son de tipo nullable(RequiredDate, ShippedDate, Freight, ShipRegion, y ShipPostalCode en este ejemplo).ShipRegion y ShipPostalCode son tipos de referencia, nullable por definición.

Public Class Orders

Public OrderID As Integer

Public CustomerID As String

Public EmployeeID As Integer

Public OrderDate As Date

Public RequiredDate As Nullable(Of Date)

Public ShippedDate As Nullable(Of Date)

Public ShipVia As Integer

Public Freight As Nullable(Of Decimal)

...

Public ShipCountry As String

End Class

A continuación, una versión abreviada de la clase precedente, que utiliza miembros pri-vados con accesssors Get y Set:

Public Class Orders

Private m_OrderID As Integer

Public Property OrderID() As Integer

Get

Return m_OrderID

End Get

Set(ByVal value As Integer)

m_OrderID = value

End Set

End Property

...

Private m_RequiredDate As Nullable(Of Date)

Public Property RequiredDate() As Nullable(Of Date)

Get

Return m_RequiredDate

End Get

Set(ByVal value As Nullable(Of Date))

m_RequiredDate = value

End Set

End Property

Private m_ShippedDate As Nullable(Of Date)

67

Las novedades de ADO.NET 2.0

VisualBasic2005_02.qxp 02/08/2007 16:15 PÆgina 67

Page 39: 1.8 Añadir una DataGridView y BindingNavigator Controlsgaleon.com/melissacalleja1/basesdatos2.pdf · ... y DataNavigators son componentes y controles nuevos de ADO.NET 2.0 que

Public Property ShippedDate() As Nullable(Of Date)

Get

Return m_ShippedDate

End Get

Set(ByVal value As Nullable(Of Date))

m_ShippedDate = value

End Set

End Property

...

Private m_Freight As Nullable(Of Decimal)

Public Property Freight() As Nullable(Of Decimal)

Get

Return m_Freight

End Get

Set(ByVal value As Nullable(Of Decimal))

m_Freight = value

End Set

End Property

...

Private m_ShipCountry As String

Public Property ShipCountry() As String

Get

Return m_ShipCountry

End Get

Set(ByVal value As String)

m_ShipCountry = value

End Set

End Property

End Class

Especificar miembros de clase nullable y utilizar las propiedades HasValue y Value esequivalente a utilizar los tests IfReferenceTypeIsNothingThen...o IfValueType=Nothing-Then... para los valores de propiedad asignados. Los dos tests del proyecto de ejemploNullableTypes.sln con objetos poblados desde un SqlDataReader de la tabla de pedidosOrders.

2.5 Utilizar objetos persistentes de formulario Windows de ADO.NET 2.0

Este libro define los objetos persistent como elementos que son visibles (están en lasuperficie) en los formularios Windows o en la bandeja de diseño de los formularios ycuyos valores se pueden definir en el modo diseño. Los objetos persistent se añadendesde la categoría Datos del Cuadro de herramientas o con herramientas de generación de

68

Bases de datos con Visual Basic

VisualBasic2005_02.qxp 02/08/2007 16:15 PÆgina 68