Sei sulla pagina 1di 39

Al usar los mtodos DataSet.WriteXml y DataSet.

WriteXmlSchema para persistir DataSets


en archivos locales, se ve que el esquema Customers DataSet, que difiere enormemente de
la versin en tiempo de diseo, ocupa 9,31 KBytes y el documento XML ocupa 37,3 KBytes.
Ms adelante en este libro, se incluye cdigo para guardar el esquema del juego de datos
Northwind Customers. El esquema guardado no se puede abrir en ventana principal del
proyecto.
1.8 Aadir una DataGridView y BindingNavigator
Controls
Al abrir el Form1 y el panel Orgenes 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 Orgenes de datos hasta el
Form1 por defecto del proyecto, se autogeneran cuatro componentes en la bandeja que
hay bajo el diseador y se aaden los controles DataGridView y DataNavigator a un for-
mulario que ha crecido considerablemente, tal como muestra la siguiente figura.
Aqu estn 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
VisualBasic2005_01.qxp 02/08/2007 16:11 Pgina 29
CustomersBindingSource es un objeto BindingSource basado en formulario, el cual
unifica la unin y la navegacin de datos de control y fiilas de datos para la tabla
de datos Customers, al proporcionar acceso directo al objeto BindingManager. Para
facilitar a los programadores de VB6 el cambio a ADO.NET 2.0, las BindingSources
tienen propiedades y mtodos que simulan a los objetos ADODB.Recordset.
Ejemplo de ellos son las propiedades AllowEdit, AllowAddNew, y AllowRemove
(delete) y los correspondientes mtodos AddNew, CancelNew, EndNew, Edit, Can-
celEdit, y EndEdit. Los conocidos mtodos MoveFirst, MoveLast, MoveNext, y Move-
Previous se ocupan de la navegacin por las filas. Hacer posible la navegacin sig-
nifica vincular un DataGridView o aadir otros controles para manipular la
BindingSource.
CustomersTableAdapter es el envoltorio del formulario para cualquier objeto Sql-
DataAdapter que llene la tabla de datos NorthwindDataSets Customers invocando el
mtodo CustomersTableAdapter.Fill. Los mtodos Update, Insert, y Delete envan
cambios 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 botn
VCR y otros de un ADODB.DataControl. Vincular el CustomersBindingNavigator con
la CustomersBindingSource permite invocar con los botones los mtodos Move...,
AddNew, y Cancel.... Por defecto, los BindingNavigators suelen estar en la parte
superior del formulario. Al ejecutar el formulario se puede arrastrar el Binding-
Navigator a una posicin ms cmoda, en la parte inferior del formulario, o tam-
bin se le puede dar el valor Bottom a la propiedad Dock del DataNavigator en el
diseador de proyecto.
DataComponents, DataConnectors, y DataNavigators son componentes y controles nuevos
de 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 automticamente relaciones
entre los juegos de datos de diferentes tablas, que requieren una intervencin manual pre-
via. Los DataConnectors simplifican el cdigo para navegar por las tablas de datos. El archi-
vo DataSet.vb contiene clases, interfaces y tratadores de eventos para las componentes de
datos.
El ltimo paso en el proceso de autogeneracin del formulario de datos 2005 es aadir el mtodo
CustomersComponent.Fill al evento Form1_Load; y cdigo para salvar los cambios del DataSet no se
aade automticamente al evento bindingNavigatorSaveItem_Click, debido a la complejidad del
cdigo cuando el juego de datos contiene tablas mltiples. Salvar cambios mltiples en
tablas 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 Pgina 30
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 despus de reducir el tamao, ampliar
el 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 por
defecto. Los cambios que se hagan en la DataGridView no se validan en la tabla hasta
que no se pulsa el botn Save Data.
Para facilitar la edicin, el ancho de columna se puede adaptar automticamente al con-
tenido definiendo para la propiedad AutoSizeColumnsMode de DataGridView el valor
AllCells o DisplayedCells, que aade una barra de desplazamiento horizontal al control.
1.9 Persistir y reabrir el juego de datos
El manejador de eventos del proyecto frmDataGridView_Load incluye el siguiente cdi-
go para salvar el documento de datos XML NorthwindDataSet y el esquema solo. Se
puede aadir cdigo parecido despus de la ltima invocacin DataComponent.Fill o
DataAdapter.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 Pgina 31
.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 dar
soporte a los usuarios sin conexin, 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 mucho
ms complejo que lo que hemos visto aqu. En captulos posteriores se describe cmo salvar
y cargar de nuevo los cambios pendientes del DataSet que no se han pasado a las tablas
base. El argumento XmlReadMode.Auto aparece por defecto, as que incluirlo es opcional.
1.10 Cambiar de un DataViewGrid a un Details
Form
La combinacin por defecto de los controles DataViewGrid y DataNavigator acelera la
creacin de un formulario utilizable. De todos modos, un DataNavigator es mucho ms
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 DateTime
y cuadros de verificacin 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 Orgenes de datos, abra la lista des-
plegable para la tabla de datos, y seleccione Detalles como se muestra en la siguiente
figura.
Arrastre el icono DataTable hasta el formulario para aadir automticamente una
columna de etiquetas con controles asociados de vinculacin de datos (cuadros de texto
en este ejemplo) al formualrio. La siguiente figura, que es una versin modificada del
proyecto 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 Pgina 32
dos para reducir la altura del formulario.
1.11 Aadir un control de vnculo de datos
relacionado
Al panel Orgenes de datos se le puede aadir una tabla relacionada y despus un con-
trol, como DataGridView, que se puede vincular al BindingAdapter relacionado. Para
aadir 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 como
OrdersDetailView. No renombre el proyecto.
2. Pulse <F5> para crear y compilar el proyecto. Corrija cualquier error de nombre
que detecte el depurador.
3. Abra la ventana Orgenes de datos y pulse el botn del ayudante Configurar Dataset
con el asistente para abrir la pgina Elija los objetos de la base de datos.
4. Expandir el rbol Tablas y seleccione la casilla de verificacin de la tabla Orders.
Pulse el botn Finalizar. De ese modo se aade en panel Orgenes de datos un nodo
relacional Orders a la tabla Customers y un nodo individual Orders (ver siguiente
figura).
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 tamao y la posicin de los controles y defina para la propiedad Or-
dersDataGridView.AutoSizeRowsMode el valor DisplayedCells. Opcionalmente se
puede modificar la propiedad Text del formulario para reflejar el cambio en el
diseo.
33
Pasar de ADO a ADO.NET
VisualBasic2005_01.qxp 02/08/2007 16:11 Pgina 33
7. Pulse <F5> para crear y ejecutar el proyecto. El formulario aparecer tal como
muestra la siguiente figura.
Arrastrando el nodo relacionado de la tabla Orders hasta el formulario se aade un
OrdersTableAdapter y OrdersBindingSource a la bandeja, y el control OrdersDataGridView
al formulario. El valor de la propiedad OrdersDataGridView del control DataSource es
OrdersBindingSource.
34
Bases de datos con Visual Basic
VisualBasic2005_01.qxp 02/08/2007 16:11 Pgina 34
La propiedad OrdersBindingSource tiene el valor CustomersBindingSource y el valor de la
propiedad DataMember es FK_Orders_Customers, el cual es la relacin de clave fornea
en 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 ventana
principal, pulsar con el botn secundario la lnea de relacin entre las tablas Customers
y Orders, y seleccionar Editar relacin para abrir el cuadro de dilogo Relacin (ver figu-
ra siguiente).
Las relaciones que se definen aadiendo tablas relacionadas a la ventana Orgenes de datos
no refuerzan la integridad referencial por defecto. Hay que cambiar el valor por defecto de
la propiedad Slo relacin a uno de los otros valores para mantener la integridad referen-
cial. Tambin se puede especificar Cascade u otras opciones para las reglas actualizacin,
eliminacin, y aceptacin o rechazo.
35
Pasar de ADO a ADO.NET
VisualBasic2005_01.qxp 02/08/2007 16:11 Pgina 35
37
CAPTULO 2
Las novedades de ADO.NET 2.0
En este captulo trataremos los nuevos objetos de ADO.NET 2.0 y los mtodos, propie-
dades y eventos utilizados con ellos. De la misma forma que el captulo anterior, este
captulo empieza con unas descripciones de los nuevos objetos en tiempo de ejecucin,
como DbProviderFactory y SqlBulkCopy, con los correspondientes ejemplos de cdigo
para crear y manejar los nuevos objetos. El captulo contina com ms ejemplos avan-
zados de las componentes y controles de ADO.NET 2.0 para los formularios de
Windows, que se pueden agragar con la ayuda de diseadores: DataTables, BindingSour-
ces, BindingNavigators y DataGridViews.
Todos los ejemplos de cdigo SQLServer de este captulo se pueden ejecutar con SQL-
Server 2000, SQLServer 2005 o SQLServer 2005 Express Edition (SQLX) y nencesitan los
privilegios del administrador del sistema.
Si trabajamos con SQLX, deberemos cambiar la cadena de conexin de cada proyecto de local-
host a .\SQLEXPRESS.
2.1 Los objetos de formulario
Este libro define un objeto en tiempo de ejecucin como un tipo de objeto no visual,
relacionado con los datos que se genera sin la ayuda de los mltiples asistentes. Los
objetos en tiempo de ejecucin de ADO.NET 2.0 se crean escribiendo cdigo VB.NET
2005 sin la ayuda de los ayudantes de tiempo-diseo de VS 2005 ni cdigo autogenera-
do. Microsoft ha dedicado una parte importante del esfuerzo invertido en el desarrollo
de VS 2005 y ADO.NET 2.0 en simplificar con arrastrar y colocar la creacin de formu-
larios bsicos Windows y Web de vinculacin de datos.
Otro aspecto que se ha cuidado ha sido dar soporte a las nuevas caractersticas del
SQLServer 2005 con los objetos System.Data y System.Xml. Por eso, ADO.NET 2.0 slo
incluye algunos objetos y caractersticas nuevas y actualizadas que son compatibles con
las fuentes de datos de SQLServer 2000. Ms adelante, en este mismo libro, se tratarn
las propiedades de ADO.NET 2.0 y VB.NET 2005 especficas para SQLServer 2005.
A continuacin indicamos los objetos en tiempo de ejecucin y actualizados, mtodos
y caractersticas de lenguaje, ms importantes para los proyectos de formulario
Windows:
VisualBasic2005_02.qxp 02/08/2007 16:14 Pgina 37
El objeto DbProviderFactory permite escribir cdigo comn para proveedores de
datos y servidores de bases de datos alternativos.
El objeto SqlBulkCopy permite insertar con gran eficiencia datos de SQLServer de
fuentes relacionales y XML.
El mtodo SqlConnection.RetrieveStatistics proporciona informacin detallada sobre
la conexin abierta con el SQLServer.
La ejecucin asincrnica de SqlCommand permite entrelazar consultas o actualiza-
ciones mltiples de larga ejecucin.
Los nuevos objetos actualizados DataTable soportan las caractersticas comunes de
los DataSet, como son los mtodos ReadXml y WriteXml, retornan valores de los ser-
vicios Web e interfaces remotas y streaming.
Alas 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 los
que se puede asignar el valor DbNull.
En las secciones siguientes se explica cmo utilizar las caractersticas del precedente
ADO.NET 2.0 con ejemplos de cdigo derivado de los proyectos-ejemplo de formula-
rios Windows.
2.1.1 Utilizar DbProviderFactories para crear proyectos
con bases de datos agnsticas
La nueva clase System.Data.Common.DbProviderFactories proporciona a los desarrolla-
dores de bases de datos la oportunidad de enfrentarse a la creacin de aplicaciones
agnsticas 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 de
datos 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 caractersticas pro-
pias de una base de datos, requerirn sin duda un esfuerzo. Si actualmente utiliza el
proveedor de datos controlados .NET Framework OleDb, o ADODB con proveedores
OLE DB para asegurar la interoperabilidad de las bases de datos, seguramente encon-
trar que Microsoft y el tercero en cuestin, ADO.NET, ofrecen mejor rendimiento y,
como resultado, mayor escalabilidad. Por otra parte, el nivel de ampliacin y mejora
que .NET garantiza a los proveedores de datos, hace difcil escribir cdigo que sea total-
mente transparente al proveedor.
Los grupos de terceros de proveedores controlados .NET pueden reducir la interoperabilidad con
costes de licencia aadidos. Por ejemplo, DataDirect Technologies ofrece proveedores de datos
controlados para IBM DB2 y DB2 UDB; Oracle 8i, 9i, y 10g; SQLServer 7 y 2000; Sybase
Adaptive 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 travs 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 Pgina 38
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 sentencia
DimFactoryNameAsDbProviderFactory = DbProviderFactories.GetFactory(strProvider).
2. Crear un objeto IdbConnection invocando el mtodo DimConnectionNameAsIDbCon-
nection = FactoryName.CreateConnection().
3. Definir el valor de la propiedad ConnectionName.Connection.String.
4. Crear un objeto IdbCommand invocando el mtodo 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 mtodo ConnectionName.Open().
7. Crear un objeto IdataReader invocando el mtodo DimReaderNameAsIDataReader =
CommandName.ExecuteReader.
El objeto IDataReader tiene los miembros que los DataReaders especficos del proveedor
para ADO.NET 1.x y 2.0, ms el nuevo mtodo GetSchemaTable que se describe en el si-
guiente apartado.
El proyecto de ejemplo DbFactoryTest.sln muestra datos en pantalla de una de las tres
tablas Northwind creando y atravesando los objetos IDataReader de SqlClient, OleDb, u
Odbc, que se especifiquen seleccionando la opcin apropiada. El formulario incluye
tambin un control DataGridView con el que se muestra en pantalla el esquema de tabla
DataTable (del que trata el siguiente apartado) tal como muestra la siguiente figura.
El siguiente listado contiene el cdigo para las declaraciones de variables y el botn de
opcin del manejador de eventos de OleDb DbProviderFactory:
39
Las novedades de ADO.NET 2.0
VisualBasic2005_02.qxp 02/08/2007 16:14 Pgina 39
'OleDb provider settings - Products table
Private 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 Then
PopulateList(strOleDbProvider, strOleDbConn, strOleDbTable)
Me.Text = "DbFactory Test Form - OleDb"
End If
End Sub
El tratador de eventos optOleDB_CheckedChanged pasa los valores requeridos del
parmetro OleDb al procedimiento PopulateList, ampliado con el cdigo siguiente:
Private Sub PopulateList(ByVal strProvider As String, _
ByVal strConn As String, ByVal strTable As String)
Dim cnFactory As IDbConnection = Nothing
Dim drData As IDataReader = Nothing
Try
Dim dpFactory As DbProviderFactory = _
DbProviderFactories.GetFactory(strProvider)
cnFactory = dpFactory.CreateConnection()
cnFactory.ConnectionString = strConn
Dim cmFactory As IDbCommand = cnFactory.CreateCommand
cmFactory.CommandType = CommandType.Text
cmFactory.CommandText = "SELECT * FROM " + strTable
cnFactory.Open()
drData = cmFactory.ExecuteReader(CommandBehavior.KeyInfo)
lstData.Items.Clear()
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
40
Bases de datos con Visual Basic
VisualBasic2005_02.qxp 02/08/2007 16:14 Pgina 40
End If
End With
End With
If dgvSchema.Columns.Count > 0 Then
Dim intCtr As Integer
Dim 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 intCtr
intCtr = 0
End If
Catch exc As Exception
MsgBox(exc.Message + exc.StackTrace)
Finally
If Not drData Is Nothing Then
drData.Close()
End If
If Not cnFactory Is Nothing Then
cnFactory.Close()
End If
End Try
End 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 ms para especificar las diferencias, sutiles o no, entre las diferentes
mejoras de los proveedores de datos, pruebe con DbProviderFactories. Sin embargo,
tenga en cuenta que el cdigo independiente de proveedores tiene que usar tipos de
datos originales .NET, antes que los tipos de datos especficos de cada proveedor para
los diferentes add-in de SQLServer, Oracle, y otros servidores soportados por terceros.
DbProviderFactories mejora la vinculacin de la base de datos, lo que deja en clara desventa-
ja a muchas propiedades del modelo de programacin de ADO.NET. El SQL especfico de un
vendedor y la sintaxis de ejecucin de los procedimientos almacenados hacen que escribir
cdigo transparente al vendedor con los proveedores de datos ADO.NET 2.0 sea difcil, si no
imposible.
2.1.2 Restablecer los esquemas de las tablas base
Los DataReaders de ADO.NET 1.x y 2.0 y los DataTableReaders de ADO.NET 2.0, tienen
un mtodo GetSchemaTable que devuelve los correspondientes esquemas del objeto en
un objeto DataTable. Para dar informacin sobre el tipo de datos utilizado en los proyec-
tos, que substituye el cdigo para los controles de vinculacin que muestran y actuali-
41
Las novedades de ADO.NET 2.0
VisualBasic2005_02.qxp 02/08/2007 16:14 Pgina 41
zan las tablas base, se utilizan los valores de propiedad del esquema DataTable. Los
esquemas DataTable dan valores ColumnLength para definir la propiedad MaxLength en
cuadros de texto y valores IsReadOnly que se pueden aplicar a la propiedad ReadOnly
de los controles de entrada de datos normal. Estos DataTable tambin devuelven infor-
macin clave primaria como son los ndices de columna y detalles de autoincremen-
tacin.
El System.Data.ObjectSpaces.ObjectDataReader, que se inclua en las primeras versiones alpha
y Community Technical Preview de VS 2005, daban miembros similares a los de otros
DataReaders, incluido el mtodo GetSchemaTable. En Mayo del 2004, Microsoft anunci que
ObjectSpaces se lanzara como componente dentro de las mejoras del sistema de archivos
WinFS.
Para crear un esquema DataTable en un DataReader y poblar una DataGridView para
mostrar las propiedades de columna se ha de utilizar cdigo 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 de
propiedades de columna SqlDataReader. OleDbDataReaders y OdbcDataReaders devuel-
ven 18 propiedades; los DataTableReaders tienen 25 campos de propiedades. Como el
objeto 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 Pgina 42
Index SqlDataReader OleDb y Odbc DataTableReader
DataReaders
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 Collection
Database
26 XmlSchema Collection
OwningSchema
27 XmlSchema CollectionName
Las propiedades que se muestran en negrita son miembros de la nueva clase de ADO.NET 2.0
System.Data.Common .SchemaTableColumn y son necesarias. El resto son miembros opciona-
les de la clase SystemData.Common.SchemaOptionalTableColumn. Los campos XmlSchema-
Collection aparecen slo en las tablas del SQLServer 2005 y especifican el esquema, si existe, de
los 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 slo ofrece la lista de
las 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 Pgina 43
Nombre de la propiedad Descripcin
ColumnSize Devuelve 1 si el dato no est disponible, de lo contrario, el
tamao de la columna en bytes.
DataType El tipo de datos original de .NET que corresponde al tipo de dato
de la columna, como en System.Int32 o System.String.
ProviderType El valor ntegro de una enumeracin de tipo de datos especifcos
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 (slo SqlClient)
Expression La expresin calculada para una columna de una DataTable (slo
DataTable)
ColumnMapping Un valor String que especifica la columna de la tabla de destino o
1 si la columna no est mapeada (slo DataTable)
BaseTableNamespace El nombre de espacio XML asignado a la tabla, heredado del
nombre de espacio del DataSet si est vaco (slo DataTable)
BaseColumnNamespace El nombre de espacio XML asignado a la tabla, heredado del
nombre de espacio del DataSet si est vaco (slo 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)
Ms adelante en este captulo, se describe cmo cargar y persistir DataTables desde
bases de datos y archivos XML, y mostrar en pantalla la informacin del esquema de
los objetos DataTable.
2.2 Comprobar las instancias de servidor SQL
disponibles y los proveedores de datos
ADO.NET 2.0
El mtodo System.Data.Common.SqlDataSourceEnumerator.Instance.GetDataSources de-
vuelve una DataTable que tiene una fila para cada instancia de servidor SQL 2000 y 2005
accesibles. Las columnas muestran las propiedades ServerName, InstanceName,
IsClustered, y Version.
Al invocar el mtodo DbProviderFactories.GetFactoryClasses(), ste devuelve una tabla
similar 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 Pgina 44
dos en el sistema, con columnas para las propiedades del proveedor Name, Description,
InvariantName, y AssemblyQualifiedName y el nmero 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 seccin system.data que aade estos provee-
dores a DbProviderFactories. El mtodo GetFactoryClasses lee el archivo machine.config para
proporcionar la lista de proveedores instalados.
El siguiente cdigo, del proyecto de ejemplo DataEnums.sln, puebla dos controles Data-
GridView con una instancia de SQLServer y un proveedor instalado de datos .NET de
Microsoft:
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 un
SQLServer 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 para
escribir este libro.
45
Las novedades de ADO.NET 2.0
VisualBasic2005_02.qxp 02/08/2007 16:14 Pgina 45
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 mtodos tradicio-
nales para aadir filas muy rpidamente a las tablas del SQL Server. ADO.NET 2.0 ofre-
ce una opcin alternativa: programar el nuevo objeto SqlBulkCopy. La fuente ms habi-
tual para las filas son los DataReader en tablas relacionales. Otra alternativa es insertar
filas desde documentos tabulares XML creando un juego de datos en tiempo de ejecu-
cin runtime con una o ms tablas de datos para copiar.
Copiar documentos XML a las tablas del servidor SQL (un proceso llamado shredding) es
mucho ms sencillo con SqlBulkCopy que con la propiedad para cargar de SQLXML3.0. Cargar
requiere un esquema XML anotado para mapear elementos o atributos y aadirlos a las colum-
nas de las tablas base. SqlBulkCopy tiene una coleccin de ColumnMappings que permite defi-
nir la relacin 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 conexin y un comando para los datos fuente. Se puede usar cualquier
proveedor .NET para conectarse a la fuente de datos y crear el DataReader.
2. Aplicar el mtodo Command.ExecuteReader para crear el DataReader.
3. Crear un objeto nuevo SqlBulkCopy que tendr como argumentos el string de cone-
xin y la enumeracin apropiada en SqlBulkCopyOptions.
4. Definir el valor de la propiedad SqlBulkCopy.DestinationTableName.
5. Aadir miembros ColumnMapping a la coleccin ColumnMappings si el esquema de
la tabla destino difiere de la tabla o la peticin 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 Pgina 46
7. Si la operacin de copia implica un nmero muy alto de registros o ejecuciones con
una conexin de red muy lenta, aadir un tratador para el evento SqlBulkCopy.Sql-
RowsCopied a fin de mostrar en pantalla el nmero o el porcentaje de registros
copiados.
8. Invocar el mtodo SqlBulkCopy.WriteToServer para ejecutar la operacin de copia.
9. Aplicar el mtodo SqlBulkCopy.Close() y, si ya ha terminado, cierre la conexin. En
caso contrario, use de nuevo el objeto SqlBulkCopy para realizar cualquier otra ope-
racin.
La tabla siguiente describe los miembros de la enumeracin SqlBulkCopyOptions.
Nombre del miembro Descripcin
CheckConstraints Aplica un chequeo restringido durante el proceso de copia.
Default No utiliza opciones (por defecto) para la operacin de copiar.
FireTriggers Permite a detonadores INSERT dispararse durante el proceso de copia.
KeepIdentity Utiliza valores de identificacin 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-
saccin.
KeepIdentity es el miembro ms importante de la enumeracin SqlBulkCopyOptions para tablas
que usan una columna de identificacin como clave primaria. Si no se especifica esta opcin, las
claves de la tabla destino podran ser distintas de los valores en la tabla fuente. Tambin es con-
veniente aadir la opcin UseInternalTransaction para prevenir copias parciales si ocurriera
alguna excepcin durante el proceso.
El ejemplo ms sencillo de una operacin SqlBulkCopy crea copias de tablas en la misma
base de datos. El siguiente cdigo del proyecto BulkCopySameSchema.sln copia las tablas
de 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 Pgina 47
cmdProds.CommandText = "SELECT * FROM Products"
sdrProds = cmdProds.ExecuteReader()
If chkKeepIdentity.Checked Then
sbcProds = New SqlBulkCopy(strConn, _
SqlBulkCopyOptions.UseInternalTransaction Or _
SqlBulkCopyOptions.KeepIdentity)
Else
sbcProds = New SqlBulkCopy(strConn, _
SqlBulkCopyOptions.UseInternalTransaction)
Dim blnUseCm As Boolean = True
If blnUseCm Then
sbcProds.ColumnMappings.Clear()
Dim intCol As Integer
For intCol = 1 To 9
sbcProds.ColumnMappings.Add(intCol, intCol)
Next intCol
End If
End If
AddHandler sbcProds.SqlRowsCopied, New
SqlRowsCopiedEventHandler(AddressOf ProdRowAdded)
With sbcProds
.DestinationTableName = "ProductsCopy"
.BatchSize = CInt(nudBatchSize.Value)
.BulkCopyTimeout = 30
.NotifyAfter = 1
.WriteToServer(sdrProds)
.Close()
End With
sdrProds.Close()
lngTime = Now.Ticks - lngTime
txtTime.Text = Format(lngTime / 10000000, "0.000")
FillProdsList(True)
Catch excCopy As Exception
MsgBox(excCopy.Message + excCopy.StackTrace, , "Products Bulk Copy
Exception")
Finally
If Not sbcProds Is Nothing Then
sbcProds.Close()
End If
If Not sdrProds Is Nothing Then
sdrProds.Close()
End If
If Not cnnNwind Is Nothing Then
cnnNwind.Close()
End If
btnCopyProds.Enabled = True
End Try
End Sub
48
Bases de datos con Visual Basic
VisualBasic2005_02.qxp 02/08/2007 16:14 Pgina 48
La propiedad SqlBulkCopy.NotifyAfter determina el nmero de filas aadidas antes de
dispararse el evento SqlRowsCopied. A continuacin vemos el cdigo para un tratador
de eventos SqlRowsCopied que muestra el progreso del proceso de copia de las tablas de
productos 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 interaccin al usuario, el valor de la propiedad
NotifyAfter debe ser como mnimo el 10 por ciento del nmero total de registros aadidos.
La siguiente figura muestra el formulario del proyecto BulkCopySameSchema.sln des-
pus de copiar las dos tablas. Los scripts Transact-SQL recrean la tabla en el manejador
de eventos frmBulkCopy_Load. Los cuadros de lista muestran la clave primera de la
tabla fuente y los valores de segunda columna cuando se carga el formulario, y los
valores de la tabla destino despus de la copia. El deslizador Batch Size determina el
nmero de filas por intervalo; 0 (el valor por defecto) intenta enviar todas las filas al
servidor en un solo intervalo. Definiendo 1 para el tamao del intervalo y copiando de
nuevo las tablas se puede comparar el rendimiento de la copia frente a las operaciones
fila por fila.
Cachear datos y cdigo provoca una diferencia considerable entre el tiempo de ejecucin de la
copia bulk inicial y las siguientes. Por lo tanto, habra que comparar los tiempos de ejecucin
con batchs de diferentes tamaos despus de una o dos pruebas con un tamao de batch defini-
do en 0.
Deseleccionando el cuadro de verificacin Keep Source Identity, la opcin KeepIdentity se
elimina del constructor SqlBulkCopy de la tabla de productos. En este caso, los valores
de clave primarios se incrementan en 77 por cada operacin de copia. En el apartado
49
Las novedades de ADO.NET 2.0
VisualBasic2005_02.qxp 02/08/2007 16:15 Pgina 49
siguiente se describe el tratador de eventos para el botn Show Connection Statistics
(Mostrar estadsticas de conexin).
2.2.2 Obtener las estadsticas de conexin del servidor
SQL
El nuevo mtodo SqlConnection.RetrieveStatistics averigua la instancia del servidor SQL
con los datos de la conexin actual y devuelve un objeto IDictionary que contiene los 18
pares nombre/valor que muestra la siguiente figura.
Esta propiedad se ha de permitir explcitamente ejecutando una instruccin SqlConnec-
tion.EnableStatistics=True antes de invocar el mtodo RetrieveStatistic. El mtodo ms
sencillo para para tratar los valores nombre/valor es encrustar el objeto IDictionary en
un tipo HashTable y, despus reiterar la tabla Hash en un bucle ForEach...Next. El cdi-
go siguiente del proyecto BulkCopySameSchema.sln muestra en pantalla las estadsticas
en 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 Pgina 50
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 cdigo anterior y el formulario frmConnStats se pueden aadir a cualquier proyecto
que utilice una SqlConnection. Invoque el mtodo SqlConnection.ResetStatistics para ini-
cializar los datos, excepto ConnectionTime.
Recuperar las estadsticas de conexin requiere establecer de nuevo una conexin con el ser-
vidor, por lo tanto es mejor reservar el uso de esta funcin para diagnosticar problemas de
conexin.
2.3 Ejecutar comandos SQL de forma asincrnica
ADO.NET 2.0 aade los mtodos BeginExecuteReader, BeginExecuteXmlReader, y Begin-
ExecuteNonQuery (junto con los correspondientes mtodos End) para las clases Sql-
Command. Estos mtodos permiten ejecutar cdigo mientras se espera a que un coman-
do complete su ejecucin. Para ejecutar un comando SqlCommand hay que aadir
Async=True a la cadena de comando que se pas al constructor de la SqlConnection. En
los apartados siguientes se describe, con el correspondiente cdigo de ejemplo, para los
tres modelos de ejecucin de comandos SqlCommand asncronos que soporta la interfaz
IasyncResul. La siguiente figura ilustra las bases de datos, conexiones y comandos que
se utilizan con los tres modelos. Obtendr resultados ms interesantes del proyecto de
ejemplo AsyncDataOperations.sln si dispone de dos o tres instancias del servidor SQL
Server 2000 o 2005 con la base de datos de ejemplo de Northwind para cada instancia
(figura en la pgina 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 conexin en cualquier instancia local del SQLServer 2000.
51
Las novedades de ADO.NET 2.0
VisualBasic2005_02.qxp 02/08/2007 16:15 Pgina 51
2.3.1 El modelo Polling
El modelo Polling es el ms sencillo de los tres. La siguiente figura ilustra el flujo del
programa para tres conexiones asncronas (figura en la pgina siguiente).
El cdigo siguiente abre un comando asncrono en la base de datos Northwind, en una
instancia del servidor local SQL y utiliza un bucle While que consulta constantemente
para que se complete el mtodo 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.CreateCommand
With 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 Pgina 52
End While
Dim sdrCusts As SqlDataReader = cmdCusts.EndExecuteReader(asrCustsReader)
'Do something with the data
sdrCusts.Close()
sdrCusts.Dispose()
Catch excAsync As Exception
MsgBox(excAsync.Message + excAsync.StackTrace, , "Async Operation
Exception")
End Try
End Sub
La ejecucin asncrona con polling es muy prctica para las operaciones sencillas den-
tro del bucle While, como mostrar una barra de progresin cuyo valor vienen definido
por las pulsaciones e un contador. Tambin se puede incluir cdigo que permita al
usuario cancelar un comando antes del tiempo indicado por su propiedad Com-
mandTimeout. Al salir del bucle, la ejecucin del mismo queda bloqueada hasta que se
hayan completado todos los comandos o haya expirado su tiempo de ejecuin. El cdi-
go se va ejecutando en el hilo del formulario, por lo que los comandos mltiples se eje-
53
Las novedades de ADO.NET 2.0
VisualBasic2005_02.qxp 02/08/2007 16:15 Pgina 53
cutan secuencialmente en conexiones separadas. Si las operaciones mltiples Data-
Reader.Read son complejas, se pueden ejecutar en un hilo dedicado al nuevo objeto
BackgroundWorker. Esto permite invocar el siguiente mtodo BeginExecuteReader inme-
diatamente despus de que la propiead IAsyncResult.IsComplete cambie a True.
2.3.2 El mdelo Callback
El mdelo asncrono callback es ms flexible que el polling porque utiliza un manejador
de callback que ejecuta su propio hilo, extrado de la consulta. El modelo callback permi-
te entrelazar comandos con bases de datos mltiples que se ejecutan en lo mismos ser-
vidores o en servidores distintos. En ese caso, hay que especificar el tratador callback y
pasarle el comando como objeto al segundo parmetro del mtodo sobrecargado Be-
ginExecuteReader. Al pasar el comando se tiene acceso al mtodo EndExecuteReader con
la propiedad IAsyncResult.AsyncState en el tratador callback. La siguiente figura mues-
tra el flujo del programa en el modo callback. Las lneas punteadas indican la ejecucin
directa de los mtodos Read, sin tener que esperar a que estn disponibles dotos los jue-
gos de filas.
54
Bases de datos con Visual Basic
VisualBasic2005_02.qxp 02/08/2007 16:15 Pgina 54
Acontinuacin mostramos un ejemplo de cdigo para un comando asncrono sencillo
SqlCommand que usa el mtodo 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 cdigo 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 Pgina 55
La mayor parte de los ejemplos de cliente de este libro conectan con finales back en la misma
mquina que el cliente; eso significa que la ejecucin sincrnica de los DataReaders se comple-
ta rpidamente o bien arroja inmediatamente una excepcin. La ejecucin asincrnica resulta
especialmente eficaz en los proyectos con DataReaders mltiples que conectan individualemen-
te a bases de datos remotas, especialmente si una o ms conexines se ejecutan en un WAN.
El proyecto de ejemplo AsyncDataOperations.sln simula una aplicacin de produccin
que conecta a bases de datos trabajadas en red mltiple 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 SQL
puede modificar las cadenas de conexin 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 invocaciones
de los mtodos Connection.Open, BeginExecuteReader, y EndExecuteReader. La siguiente
figura muestra dos instancias del formulario AsyncDataOperations.
Una clase timer VB.NET, escrita por Alastair Dallas, proporciona la resolucin requerida para
obtener datos de sincronizacin con sentido. Los nmeros entre parntesis de las entradas del
cuadro de lista son los valores System.Threading.Thread .CurrentThread.ManagedThreadId de
las instancias del formulario y los tres manejadores de callback El sufijo P indica que los hilos
del manejador son del pool de hilos. La sincronizacin de datos es para una segunda ejecucin
(cacheada).
El cdigo de ejemplo ejecuta objetos Customer desde el host local y objeto Orders y
Order 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 ejecucin en una
LAN de bajo trfico es normalmente suficiente para devolver los datos en la secuencia
BeginExecuteReadercalling, como muestra la figura anterior (izquierda). Todas las opera-
ciones de restablecimiento de datos se ejecutan en un solo hilo (13). Para simular una
conexin WAN con la tabla Orders, el cdigo en OrdersHandler provoca un retraso de
unos segundos mediante mltiples operaciones en cada fila DataReader en un bucle ani-
56
Bases de datos con Visual Basic
VisualBasic2005_02.qxp 02/08/2007 16:15 Pgina 56
dado. En este caso, DataReader de Orders completa la ejecucin antes que el DataReader
de Customers, el cual termina la ejecucin antes que el DataReader de Order Details, tal
como muestra la figura anterior (derecha). En este caso, la restauracin de Order Details
se 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 los
hilos, y los problemas con hilos son difciles de depurar.
2.3.3 El modelo WaitAll
Una alternativa al mtodo callback es utilizar un array WaitHandle y asignarlo a un ele-
mento en cada llamada de mtodo BeginExecuteReader. Un WaitHandle.WaitAll(wh-
Array) detiene la ejecucin del cdigo hasta que todos los DataReaders estn listos para
sus llamadas EndExecuteReader. Este comportamiento hace al modelo WaitAll especial-
mente adecuado para clientes que procesan juegos de filas relacionados, ya que no se
necesita el bucle de sincronizacin que se mostr anteriormente. La siguiente figura
muestra el diagrama de flujo en el modelo WaitAll.
57
Las novedades de ADO.NET 2.0
VisualBasic2005_02.qxp 02/08/2007 16:15 Pgina 57
La manera ms sencilla de ver los resultados del mtodo WaitAll en un entorno de for-
mulario Windows es crear una versin multi-hilo o multi-threaded apartment (MTA) de un
procedimiento habitual Sub Main. Por defecto, los procedimientos de VB.NET utilizan el
modelo de hilo nico (single-threaded apartment, STA) requerido para los formularios basa-
dos en Win32. Llamando WaitAll con mltiples WaitHandles, arroja una excepcin dentro
de los procedimiento STA, por lo que hay que aadir el prefijo <MTAThreadAttribute()> a
la sentencia SharedSubMain. El siguiente listado es una adaptacin del cdigo 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 Pgina 58
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 Pgina 59
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 nmero de elementos que
comandos asncronos. Al igual que con el modelo callback, se abren las conexiones, se
ejecutan las instrucciones SqlCommand.BeginExecuteReader, y se aaden los correspon-
dientes objetos SqlDataReader.AsyncWaitHandle al array WaitHandle sin importar el or-
den. La ejecucin se detiene al llegar a la instruccin WaitHandle.WaitAll(awhHandle)
hasta que se completan todos los DataReaders. Al retomarse la ejecucin, los juegos de
filas se procesan en el orden deseado (en este caso padre, hijo, nieto).
El cdigo Shared Sub Main del proyecto de ejemplo AsyncDataOperations.sln se puede
ejecutar abriendo la ventana de propiedades del proyecto, seleccionando la pgina de
Aplicacin, marcando el cuadro de verificacin Habilitar marco de trabajo de la aplicacin
y 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 independientes
Las tablas de datos de ADO.NET 1.x son miembros, normalmente, de los objetos
DataSet. ADO.NET 2.0 permite crear tablas de datos ligeras, independientes, que com-
parten muchos mtodos DataSet, como ReadXml, ReadXmlSchema, WriteXml, y Write-
XmlSchema. Las tablas de datos tambin soportan interfaces DataReader con el mtodo
Load(DataReader) y el objeto DataTableReader. Tambin se puede asignar un prefijo nom-
bre de espacio a la tabla de datos. Los apartados anteriores de este captulo introduc-
60
Bases de datos con Visual Basic
VisualBasic2005_02.qxp 02/08/2007 16:15 Pgina 60
an las tablas de datos y los controles DataGridView poblados por los mtodos GetSche-
maTable, GetDataSources, y GetFactoryClasses.
El proyecto StandaloneDataTables.sln ilustra las siguientes caractersticas de las tablas de
datos:
Crear una DataTable con un SqlDataReader, ejecutar un DataTableReader, y vincular
la DataTable a una DataGridView editable.
Persistir el contenido de una DataTable en archivos XML slo 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 mtodo ReadXml para cargar una tabla de datos desde el archivo guar-
dado DataSet.xml
Mostrar en pantalla el esquema DataTable con el mtodo DataTable.GetSchemaTable
La siguiente figura muestra el formulario del proyecto para la la tabla de datos inde-
pendiente StandaloneDataTables.sln despus de una mnima edicin de la columna Con-
tactName de la primera fila. Los botones Show... abren documentos XML guardados en
Internet Explorer. La parrilla inferior muestra el esquema DataTable del SqlDataReader o
bien, despus de pulsar el botn Reload from XMLFiles, el esquema de la tabla de datos
primaria.
El procedimiento siguiente carga una base de datos del archivo Northwind Customers,
aade 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 un
DataTableReader, 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 Pgina 61
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 Pgina 62
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 mtodo ExecuteReader method para
aadir la clave primaria con cdigo. Las instrucciones del test prueban que las tablas de datos
no generan juegos de datos de manera oculta.
Las tablas de datos que se cargan desde los DataReaders son actualizables, y se pueden
persistir como archivos de documentos XML en formatos DataSet slo-datos, slo-
esquema o diffGram. El procedimiento SaveXmlFiles genera documentos XML de datos
y 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 en
DataGridView.
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 Pgina 63
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 archivo
guardado DataSet.xml, aplica las ediciones previas guardadas como archivo diffGram y
muestra en pantalla el esquema DataTable.
Si se aade un nombre de espacio a la tabla de datos cuando se importan valores de la tabla base,
se provocar un fallo en la validacin 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 aadir cdigo para definir las relaciones entre los diferentes objetos de las tablas de datos
mltiples. En la mayora de los casos, sin embargo, crear un juego de datos tipificado es lo mejor
cuando se trabaja con proyectos que tienen ms de una tabla de datos relacional (o relacionada).
64
Bases de datos con Visual Basic
VisualBasic2005_02.qxp 02/08/2007 16:15 Pgina 64
2.4 Utilizar tipos Nullable que soporten valores
DBNull
.NET Framework 2.0 aade tipos genricos a VB.NET 2005 cuando aade el parmetro
(OfType) a las declaraciones de variable. Las variables de tipo nullable son una extensin
de tipos genricos que permiten valores Integer, Int16, Decimal, Date, DateTime, y simi-
lares y soportan valores nulos. Asignando Nothing a un valor, ste devuelve el valor por
defecto para el tipo (0 para los tipos numricos y 01/01/0001 12:00:00 AM para las
fechas).
Para hacer posibles los valores nulos hay que remplazar los identificadores del tipo de
valores con Nullable (OfType). Las referencias del tipo String ya soportan de por s valo-
res nulos, por lo que aadir Nullable (OfString) no resulta apropiado. La aplicacin ms
til de las variables de tipo nullable la tenemos en las rbricas de mtodo, donde los
tipos de valores nullable hacen innecesaria la sobrecarga. Por ejemplo, si se inserta una
fila nueva en la tabla de pedidos Northwind (Northwind Orders) desde un juego de
datos tipificado, normalmente se necesitarn las dos rbricas de mtodo Insert, como
mostramos a continuacin, 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 Pgina 65
La primera rbrica de mtodo es vlida si estn presentes todos los valores. Si alguno
de los tipos de valores dados a la funcin es nulo, se necesita la segunda rbrica, no
tipificada. En ese caso el cdigo podra proporcionar un valor String en lugar de Integer
o Decimal, un error que el compilador no detectara. Aadiendo Nullable(OfType) a los
tipos de valores, tal como mostramos aqu, permite manejar valores nulos con una sola
funcin:
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 parmetros INSERT o UPDATE asociados con
tipos nullable, deberemos comprobar que hay un valor con la propiedad HasValue y, si
el valor de HasValue es True, drselo a la propiedad Value, tal como se muestra en el
siguiente fragmento de comando INSERT (al que se han tenido que aadir parmetros):
...
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 Pgina 66
.Nullable(OfType) tambin se puede aplicar a los miembros de clase Public o Private. A
continuacin vemos un ejemplo de un sencillo objeto de negocios con propiedades
Public que hacen un mapa de los campos de la tabla Orders. Las reglas de negocios y las
restricciones de llave fornea 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 definicin.
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
Acontinuacin, una versin 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 Pgina 67
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 es
equivalente a utilizar los tests IfReferenceTypeIsNothingThen...o IfValueType=Nothing-
Then... para los valores de propiedad asignados. Los dos tests del proyecto de ejemplo
NullableTypes.sln con objetos poblados desde un SqlDataReader de la tabla de pedidos
Orders.
2.5 Utilizar objetos persistentes de formulario
Windows de ADO.NET 2.0
Este libro define los objetos persistent como elementos que son visibles (estn en la
superficie) en los formularios Windows o en la bandeja de diseo de los formularios y
cuyos valores se pueden definir en el modo diseo. Los objetos persistent se aaden
desde la categora Datos del Cuadro de herramientas o con herramientas de generacin de
68
Bases de datos con Visual Basic
VisualBasic2005_02.qxp 02/08/2007 16:15 Pgina 68

Potrebbero piacerti anche