Anda di halaman 1dari 22

Por: Saul Mamani Mamani 1

ENLACE A DATOS CON SQL SERVER Y ADO.NET


Contenido
Sin lugar a dudas uno de los ámbitos más importantes de un lenguaje o entorno de programación es su
capacidad de acceso a datos. Prácticamente todas las aplicaciones conllevan la realización de accesos a
datos.

Le gustará saber que la plataforma .NET, y por lo tanto ASP.NET, ofrecen un potente modelo de acceso
a fuentes de datos. Se le conoce con el nombre genérico de ADO.NET.

Nota:
No se deje engañar por el nombre: ADO.NET no tiene casi nada que ver
con el anterior ADO utilizado en los tiempos de ActiveX y COM. Sí, dispone
de conexiones, comandos e incluso una clase que recuerda a los Recordset,
pero créame cuando le digo que es mejor que se olvide para siempre de
todos ellos. Tanto la filosofía de trabajo como la tecnología son diferentes
por completo y es mejor que utilice una estrategia de "ojos limpios" para
acercarse correctamente a la nueva tecnología.

Los conocimientos adquiridos en este módulo le servirán para cualquier tipo de desarrollo con .NET, no
sólo para aplicaciones Web. Los conceptos explicados son válidos también para cualquier versión de
.NET no sólo para la 2.0.

Lección 1: Introducción a ADO.NET


o Introducción
o La capa conectada
o La capa desconectada
o Vinculación de datos a controles Web
Lección 2: Acceso a datos manual
o Escritura manual de código
o DataAdapter: puente entre dos mundos
o Consultas parametrizadas
o Altas bajas y modificaciones
Lección 3: Acceso a datos con Visual Studio 2005
o Controles de datos
 Orígenes de datos
 Controles enlazados
 Concurrencia optimista
 Uso de los controles enlazados en la práctica
o DataSets tipados

Introducción a ADO.NET

Como cualquier otro modelo de acceso a datos, ADO.NET es un conjunto de clases relacionadas entre sí
que están especializadas en ofrecer toda la funcionalidad que un programador necesita para realizar
acceso a datos y manejarlos una vez los ha obtenido.

Las clases genéricas expuestas por ADO.NET se encuentran bajo el espacio de nombres System.Data.
Este espacio de nombres define clases genéricas de acceso a datos que posteriormente son extendidas
para ofrecer características y funciones específicas de cada proveedor.
Por: Saul Mamani Mamani 2

El objeto más importante a la hora de trabajar con el nuevo modelo de acceso a datos es el DataSet.
Sin exagerar demasiado podríamos calificarlo casi como un motor de datos relacionales en memoria.
Aunque hay quien lo asimila a los clásicos Recordsets su funcionalidad va mucho más allá como se verá
en breve.

Arquitectura de ADO.NET

El concepto más importante que hay que tener claro sobre ADO.NET es su modo de funcionar, que se
revela claramente al analizar su arquitectura:

Figura 4.1.- Arquitectura de ADO.NET

Existen dos capas fundamentales dentro de su arquitectura: la capa conectada y la desconectada.

La capa conectada

La capa conectada de ADO.NET contiene objetos especializados en la conexión con los orígenes de
datos. Así, la clase genérica Connection se utiliza para establecer conexiones a los orígenes de datos.
La clase Command se encarga de enviar comandos de toda índole al origen de datos. Por fin la clase
DataReader está especializada en leer los resultados de los comandos.

La clase DataAdapter hace uso de las tres anteriores para actuar de puente entre la capa conectada y
la desconectada como veremos después.

Estas clases son abstractas, es decir, no tienen una implementación real de la que se pueda hacer uso
directamente. Es en este punto en donde entran en juego los proveedores de datos. Cada origen de
datos tiene un modo especial de comunicarse con los programas que los utilizan, además de otras
particularidades que se deben contemplar. Un proveedor de datos de ADO.NET es una implementación
concreta de las clases conectadas abstractas que hemos visto, que hereda de éstas y que tiene en
cuenta ya todas las particularidades del origen de datos en cuestión.

Así, por ejemplo, las clases específicas para acceder a SQL Server se llaman SqlConnection,
SqlCommand, SqlDataReader y SqlDataAdapter y se encuentran bajo el espacio de nombres
Por: Saul Mamani Mamani 3

System.Data.SqlClient. Es decir, al contrario que en ADO clásico no hay una única clase Connection o
Command que se use en cada caso, si no que existen clases especializadas para conectarse y recuperar
información de cada tipo de origen de datos.

Nota:
El hecho de utilizar clases concretas para acceso a las fuentes de datos no significa que
no sea posible escribir código independiente del origen de datos. Todo lo contrario. La
plataforma .NET ofrece facilidades de escritura de código genérico basadas en el uso de
herencia e implementación de interfaces. De hecho la versión 2.0 de .NET ofrece grandes
novedades específicamente en este ámbito.

Existen proveedores nativos, que son los que se comunican directamente con el origen de datos (por
ejemplo el de SQL Server o el de Oracle), y proveedores "puente", que se utilizan para acceder a
través de ODBC u OLEDB cuando no existe un proveedor nativo para un determinado origen de datos.

Nota:
Estos proveedores puente, si bien muy útiles en determinadas circunstancias, ofrecen un
rendimiento menor debido a la capa intermedia que están utilizando (ODBC u OLEDB). Un
programador novel puede sentir la tentación de utilizar siempre el proveedor puente para
OLEDB y así escribir código compatible con diversos gestores de datos de forma muy
sencilla. Se trata de un error y siempre que sea posible es mejor utilizar un proveedor
nativo.

La plataforma .NET proporciona "de serie" los siguientes proveedores de acceso a datos.

Proveedor Espacio de nombres Descripción

ODBC .NET Data Provider System.Data.Odbc Permite conectar nuestras


aplicaciones a fuentes de datos a
través de ODBC.

OLE DB .NET Data Provider System.Data.OleDb Realiza la conexión utilizando un


proveedor OLEDB, al igual que el
ADO clásico.

Oracle Client .NET Data System.Data.OracleClient Proveedor de datos para acceder a


Provider Oracle.

SQL Server .NET Data System.Data.SqlClient Permite la conexión optimizada a


Provider SQL Server 7.0 o posterior,
incluyenbdo la última versión SQL
Server 2005.

Los proveedores de acceso a datos que distribuye Microsoft en ADO.NET y algunos desarrollados por
otras empresas o terceros, contienen los mismos objetos, aunque los nombres de éstos, sus
propiedades y métodos, pueden ser diferentes.

Existen, por supuesto, proveedores para tipos de orígenes de datos de cualquier gestor de datos
existente en el mercado. Éstos los desarrolla normalmente la empresa responsable del producto. Si bien
éstos optimizan el acceso a estos orígenes de datos nosotros siempre podremos realizarlo mediante
ODBC u OLEDB si tenemos necesidad.
Por: Saul Mamani Mamani 4

En resumen: con la capa conectada de ADO.NET realizamos la conexión y comunicación con los
orígenes de datos. Cada proveedor de datos implementa su propia versión de las clases Connection,
Command, DataReader y DataAdapter (entre otras).

Las clases derivadas de Connection se utilizan para realizar la conexión y enviar y recibir
información.
Las clases derivadas de Command permiten ejecutar sentencias SQL y procedimientos
almacenados en el gestor de datos.
Las clases derivadas de DataReader se emplean para obtener los posibles resultados de un
comando utilizando para ello el conducto de comunicación establecido por Connection.

La capa desconectada

Una vez que ya se han recuperado los datos desde un origen de datos la conexión a éste ya no es
necesaria. Sin embargo sigue siendo necesario trabajar con los datos obtenidos de una manera flexible.
Es aquí cuando la capa de datos desconectada entra en juego. Además, en muchas ocasiones es
necesario tratar con datos que no han sido obtenidos desde un origen de datos relacional con el que se
requiera una conexión. A veces únicamente necesitamos un almacén de datos temporal pero que
ofrezca características avanzadas de gestión y acceso a la información.

Por otra parte las conexiones con las bases de datos son uno de los recursos más escasos con los que
contamos al desarrollar. Su mala utilización es la causa más frecuente de cuellos de botella en las
aplicaciones y de que éstas no escalen como es debido. Esta afirmación es especialmente importante en
las aplicaciones Web en las que se pueden recibir muchas solicitudes simultáneas de cualquier parte del
mundo.

Finalmente otro motivo por el que es importante el uso de los datos desconectado de su origen es la
transferencia de información entre capas de una aplicación. Éstas pueden encontrarse
distribuidas por diferentes equipos, e incluso en diferentes lugares del mundo gracias a Internet. Por
ello es necesario disponer de algún modo genérico y eficiente de poder transportar los datos entre
diferentes lugares, utilizarlos en cualquiera de ellos y posteriormente tener la capacidad de conciliar los
cambios realizados sobre ellos con el origen de datos del que proceden. Todo esto y mucho más es lo
que nos otorga el uso de los objetos DataSet, núcleo central de la capa desconectada de ADO.NET.

Nota:
Otra interesante características de los DataSet es que permiten gestionar
simultáneamente diversas tablas (relaciones) de datos, cada una de un origen diferente si
es necesario, teniendo en cuenta las restricciones y las relaciones existentes entre ellas.

Los DataSet, como cualquier otra clase no sellada de .NET, se pueden extender mediante herencia. Ello
facilita una técnica avanzada que consiste en crear tipos nuevos de DataSet especializados en la gestión
de una información concreta (por ejemplo un conjunto de tablas relacionadas). Estas nuevas tipos
clases se denominan genéricamente DataSet Tipados, y permiten el acceso mucho más cómodo a los
datos que representan, verificando reglas de negocio, y validaciones de tipos de datos más estrictas.

Los objetos DataSet no dependen de proveedor de datos alguno y su funcionamiento es independiente


de cómo hayan sido obtenidos los datos que contienen. Este es el concepto más importante que
debemos recordar.

El proceso general de trabajo de ADO.NET para acceder a un gestor de datos (SQL Server, por ejemplo)
es casi siempre el mismo: se abre una conexión (clase Connection), se lanza una consulta SQL o
procedimiento almacenado mediante un objeto de la clase Command, y se almacenan en memoria los
resultados dentro de un objeto DataSet, cerrando la conexión.
Por: Saul Mamani Mamani 5

Nota:
Aunque este es el comportamiento habitual de una aplicación de datos existen casos en
los que no es recomendable almacenar en memoria (en un DataSet) todos los resultados
de una consulta, bien por ser muchos registros o por contener datos muy grandes. En este
tipo de casos se puede usar u objeto DataReader directamente para leer la información,
tratarla y no almacenarla en lugar alguno. De todos modos lo más frecuente es realizar el
proceso descrito.

Unión entre capa conectada y desconectada

La clase DataAdapter se ha incluido anteriormente en la capa conectada por que está implementada por
cada proveedor de un modo diferente. En realidad es una clase que pone sus pies en ambos mundos
(conectado y sin conexión) y sirve de nexo entre ellos.

Un DataAdapter sabe omo manejar correctamente los objetos proporcionados por su proveedor
específico (fundamentalmente los vistos: Connection, Command y DataReader) y proporciona métodos
para trasegar información desde el servidor a DataSets desconectados y viceversa haciendo uso de
dichos objetos específicos del proveedor.

Así, por ejemplo, el método Fill de un DataSet se utiliza para introducir los resultados de una consula
dentro de un DataSet para luego trabajar con ellos sin preocuparnos de su origen. Del mismo modo su
método Update se utiliza para conciliar automáticamente con el origen de datos los datos modificados
en un DataSet mientras no había conexión.

Otras clases dependientes de DataSet

Como hemos dicho antes un DataSet podría asimilarse a un pequeño gestor de datos en memoria.
Como tal un DataSet permite mantener diversas tablas así como las relaciones entre ellas, incluso
forzando que se cumplan restricciones de creación y actualización, como en una base de datos "real".
Para ello se apoya en el uso de otras clases especializadas que son las siguientes:

DataTable: representa una tabla o relación de datos. Se puede asimilar a una tabla de un gestor
de datos. Los datos obtenidos de una consulta de tipo SELECT de SQL se almacenan en un objeto
de esta clase.
DataColumn: ofrece información sobre cada uno de los campos de los registros almacenados en
un DataTable, como su nombre o su tipo.
DataRow: representa un registro de la tabla virtual definida por el DataTable. Contiene tantos
elementos como campos tiene la tabla, cada uno del tipo definido por el objeto DataColumn
correspondiente.
Constraint: las clases Constraint se emplean para definir resticciones en los tipos de datos
contenidos en un DataTable. Por ejemplo se puede usar un objeto de esta clase para indicar que
un determinado campo debe ser único o que se trata de una clave externa que debe ser tenida en
cuenta en actualizaciones o borrados en cascada.
DataRelations: define la relación existente entre dos objetos DataTable. Normalmente se suelen
utilizar un identificador común a ambas tablas aunque pueden ser combinaciones de más de uno
de ellos. De este modo se sabe cómo obtener información de una tabla a partir de información en
otra. Por ejemplo el identificador de una factura (su número, por ejemplo) puede servir para
relacionar su registro con los registros de detalle de factura que están contenidos en otra tabla.
DataView: representa una vista concreta de un DataTable. Normalmente se trata de
ordenaciones o filtros sobre los datos originales. Todas las tablas disponen de una vista por dfecto
(propiedad DefaultView) que ofrece los datos tal y como se han introducido en ésta y es la vista
que se usa habitualmente.
Por: Saul Mamani Mamani 6

Vinculación de datos a controles Web

Otra característica fundamental de ASP.NET que lo convierte en una herramienta ventajosa para el
desarrollo de aplicaciones Web es la capacidad de vincular datos a controles Web.

La mayor parte de los controles que podemos arrastrar sobre una página ASPX se pueden vincular a los
objetos DataSet, DataTable y DataReader o a informaciones concretas contenidas en éstos.

Ello facilita mucho el trabajo con datos desde la interfaz de usuario ya que no hay que molestarse en
generar tablas con ellos, escribir JavaScript o proporcionar complejos medios propios para permitir su
edición o navegación si hacemos un uso adecuado de la vinculación y los controles disponibles.

Todo ello, gracias a ASP.NET y Visual Studio, equipara en muchos aspectos el desarrollo Web al clásico
desarrollo de aplicaciones de escritorio donde este tipo de facilidades han estado disponibles desde hace
años. Sin embargo en una aplicación Web donde no existe una conexión permanente disponible entre la
visualización (navegador) y el lugar en el que se ejecuta el código no es algo fácil de conseguir. El uso
de un modelo consistente como ADO.NET (idéntico en Web, escritorio y otros entornos) junto con las
capacidades nativas de ASP.NET para abstraernos de estas dificultades (ViewState, Postback...)
consiguen el "milagro".

En este módulo veremos también lo sencillo que resulta crear interfaces para explotar los datos desde
una página Web.

Acceso a datos manual

Tras haber aprendido un poco de teoría sobre ADO.NET a continuación explicaremos cómo se utilizan
las clases de acceso datos para escribir código de acceso a datos de manera manual.

Si bien es un tema algo árido y además en un gran porcentaje de los casos utilizaremos herramientas
que nos faciliten la creación automática de código, es fundamental conocer la forma de trabajar sin
ayuda para entender el funcionamiento real de los objetos de datos.

Así que ánimo y estudie con detenimiento esta lección.

Lección 2: Acceso a datos manual


o Escritura manual de código
o DataAdapter: puente entre dos mundos
o Consultas parametrizadas
o Altas bajas y modificaciones

Escritura manual de código

En este apartado vamos a analizar cómo es el código necesario para recuperar y actualizar datos con
ADO.NET. Posteriormente veremos como sacar partido a las facilidades del entorno de desarrollo Visual
Studio 2005 para no tener que escribir el código a mano. Sin embargo es útil aprender a hacerlo de
esta manera para entender bien su funcionamiento.

Comandos de selección simples

La mayor parte de las consultas que se lanzan contra una base de datos suelen utilizarse para obtener
un conjunto de registros para tratar. Este tipo de consultas suelen ser expresiones SQL de tipo SELECT.
El siguiente fragmento de código muestra los pasos necesarios para mostrar en una página los registros
resultantes de una consulta:
Por: Saul Mamani Mamani 7

No se trata de un código optimizado (es más bien burdo) pero nos ayudará a entender perfectamente el
proceso. Los datos los obtendremos de la conocida base de datos de ejemplo Northwind (que viene con
todas las versiones de SQL Server).

Nota:
Para poder escribir código de acceso a datos en nuestro módulo debemos agregar
referencias a los espacios de nombres que contienen las clases que vamos a utilizar. Para
ello usamos las dos sentrencias Imports siguientes:

La primera de ellas agrega las clases genéricas de acceso a datos (como DataSet) y la
siguiente las específicas de SQL Server. Si no lo hacemos recibiremos un error.

Lo primero que se debe hacer es instanciar un objeto que represente la conexión a la base de datos.
Dado que nos estamos conectando a SQL Server esta conexión será del tipo SqlConnection. Es lo que
se hace en la primera línea del código anterior. La conexión debe realizarse con un servidor de datos y
un esquema de datos concreto. Esto se indica mediante la cadena de conexión (al igual que se hacía
en ADO tradicional). En este caso la cadena de conexión es la típica de SQL Server. Cada gestor de
datos tiene la suya y hay que construirla de manera adecuada. El entorno de desarrollo Visual Studio
2005 nos ayuda a crearlas como veremos luego.

Una vez creado el objeto con el que nos conectaremos hay que definir el comando a lanzar a la base de
datos. Para ello se utiliza un objeto SqlCommand. Las propiedades básicas que hay que establecer para
éste son la consulta que se lanzará (propiedad CommandText) y la conexión que se empleará para
lanzarla (propiedad Connection) que es lo que se refleja en las líneas 6 y 7.

Ahora que ya sabemos cómo nos conectaremos y qué queremos obtener debemos lanzar la consulta y
recoger el resultado de alguna manera.

La clase Command dispone de diversos métodos para ejecutar consultas:


Por: Saul Mamani Mamani 8

ExecuteReader: este método lanza la consulta a la base de datos y devuelve una referencia a una
instancia de la clase DataReader (SqlDataReader en el caso de SQL Server). Podemos utilizar el
DataReader para recorrer los datos y procesarlos.
ExecuteNonQuery: ejecuta la consulta sin devolver resultado alguno. Simplemente envía la
consulta al servidor y devuelve el número de registros afectados por ésta. Útil para realizar
consultas de inserción (INSERT), actualización (UPDATE) y borrado (DELETE).
ExecuteScalar: lanza la consulta y devuelve un objeto con el valor del primer campo del primer
registro que se obtenga de dicha consulta. Es útil para lanzar consultas de agregación como sumas
(SUM), cuentas (SELECT COUNT * ....) y similares de las que sólo necesitamos un valor como
resultado.

En el ejemplo hemos utilizado el primer método ya que requerimos que devuelva varios registros con
diferentes campos. Entonces lo que hacemos es (líneas a partir de la 9):

1. Abrir la conexión.
2. Crear una variable para contener una referencia a un objeto de la clase DataReader que es el que
nos permitirá acceder a los resultados. Fíjese en que no se puede instanciar un DataReader (la
declaración no lleva la palabra clave New).
3. Se obtiene el resultado mediante el método ExecuteReader, recogiéndolo en la variable (dr)
recién declarada.
4. Se procesan los resultados (líneas 14-18).
5. Se cierra la conexión.

El objeto DataReader es asimilable a un cursor de servidor de sólo lectura y hacia adelante (conocidos
como de manguera de bombero o firehose). Es decir, los datos devueltos por el DataReader sólo se
pueden leer y no actualizar. Además de esto sólo se pueden leer en secuencia hacia adelante (no hay
forma de regresar sobre lo andado).

La propiedad HasRows nos indica si hay o no resultados devueltos. El método Read avanza una
posición en los registros devolviendo True si quedan registros pendientes de leer. Con esta información
es muy fácil entender las líneas 14 a 18.

La cláusula Using

¿Qué ocurre si se produce un error durante el procesamiento del bucle anterior en el que se trata el
DataReader?. La respuesta es que la conexión, que debemos tener abierta durante el procesamiento,
no se cerrará pues el código no llega al final.

Esto es algo muy grave ya que las conexiones que no se cierran no se pueden reutilizar y por lo tanto
puede llegar un momento en que no tengamos conexiones disponibles, lo que limita enormemente la
escalabilidad del sistema.

Podemos evitar el problema escribiendo:


Por: Saul Mamani Mamani 9

De este modo, con la cláusula Finally nos aseguramos que siempre se va a cerrar la conexión.

De todos modos escribir este código es algo tedioso sobre todo si queremos que la excepción se
replique y sólo metemos la cláusula Finally por el hecho de cerrar la conexión.

Para facilitar el trabajo VB.NET en .NET 2.0 incluye una cláusula especial denominada Using que
habilita la destrucción automática de los objetos a los que se hace referencia. Así el código anterior
quedaría simplemente:

Al terminar la cláusula Using (aunque haya un error por medio) se llama de manera automática al
método Dispose del objeto utilizado (en este caso una conexión). Entre otras cosas este método se
encarga de cerrar el objeto si estaba abierto, por lo que no nos tendremos que preocupar de este
aspecto.

Grupos de registros

Aunque los DataReader se asemejan al funcionamiento de un cursor firehose, en realidad difieren


bastante de éstos. Imaginemos que conectamos con la base de datos en el ejemplo anterior y, mientras
estamos procesando el bucle de los registros, se interrumpe la conexión a la base de datos pr el motivo
que sea.

En principio en el caso de un cursor firehose tradicional obtendríamos un error porque se ha roto la


conexión con el servidor. En el caso de un DataReader es posible que sigamos ejecutando varias vueltas
más del bucle sin problemas. Esto se debe a que, en realidad, el DataReader obtiene los registros en
grupos a través de la conexión y va solicitando nuevos grupos a medida que los necesita.
Por: Saul Mamani Mamani 10

Es algo que hay que tener en cuenta a la hora de utilizarlos.

Ventajas e inconvenientes

El código anterior, aunque sencillo, es un poco lioso y el uso de los DataReader está algo limitado dada
su idiosincrasia (de sólo lectura y hacia adelante). Este código es adecuado si no necesitamos
almacenar los resultados de la consulta en memoria ni regresar sobre ellos una vez procesados una
primjera vez. También es muy útil para obtener resultados con miles o millones de registros que
queremos tratar secuencialmente pero no almacenar en memoria.

Sin embargo para un uso cotidiano se trata de un código muy poco útil y complicado de utilizar salvo
para cosas muy sencillas. Además sólo hemos utilizado clases de la capa conectada de ADO.NET.
Todavía debemos aprender a obtener los resultados dentro de un DataSet para su explotación de
manera cómoda. Hay que tender un puente entre ambos mundos (conectado y desconectado): el
DataAdapter.

DataAdapter: puente entre mundos

Si lo que deseamos es poder almacena temporalmente en memoria los datos obtenidos de una consulta
debemos recurrir al uso de objetos de la clase DataSet. Como sabemos se trata de un almacenamiento
en memoria desvinculado por completo del origen de los datos.

Si el ejemplo anterior lo modificamos para convertirlo en una función que nos devuelva un DataSet con
los datos obtenidos a partir de la consulta, el resultado sería similar a este:

La primera parte del código es como la anterior. Se crean una conexión y un comando. Lo que difiere
bastante es la segunda parte. Hay varias diferencias importantes:

1. Ya no aparece en el código objeto DataReader de tipo alguno.


2. No se abre ni se cierra la conexión a la base de datos.
3. Se crea un nuevo DataSet y aparece un objeto de la clase DataAdapter.

Un DataAdapter es una clase que conoce las particularidades de la capa conectada de un determinado
proveedor y es capaz de "comunicar" información entre ésta y la capa desconectada formada por los
DataSet.
Por: Saul Mamani Mamani 11

El método Fill de un DataAdapter se encarga e rellenar un DataSet con los datos obtenidos a partir de
una consulta (o procedimiento almacenado) definida a través de un comando. Su propiedad
SelectCommand se usa para indicar qué comando se utilizará para rellenar el DataSet.

Internamente el método Fill emplea el objeto DataReader devuelto por ExecuteReader y rellena el
DataSet con él por lo que sería factible crear un código equivalente. Sin embargo es muy cómodo y nos
evita problemas y el tener que "reinventar la rueda" en cada proyecto.

El objeto DataSet

Los objetos DataSet contienen a su vez objetos DataTable que son los que contienen realmente los
datos. Para acceder a las tablas de un DataSet se utiliza su colección Tables. Se puede acceder a las
tablas por posición (números enteros) o por nombre si lo sabemos.

En un ejemplo sencillo como el anterior (y por otro lado uno de los más comunes) se crea una única
tabla en el DataSet de nombre "Table1" y posición 0.

Las tablas contienen dos colecciones interesantes:

Columns: conjunto de objetos de tipo DataColumn que ofrecen información sobre los campos
de la tabla (nombre, tipo, longitud...).
Rows: colección de objetos de la clase DataRow que contienen información concreta sobre cada
campo de un registro de la base de datos.

Con esta información resulta muy sencillo tratar los datos de una consulta. Podemos acceder
directamente a cualquier registro de la tabla usando su posición en la colección de filas. Por ejemplo
para acceder al quinto registro de una tabla basta con escribir:

Dim dr As DataRow

dr = ds.Tables(0).Rows(4)

(nótese que las colecciones comienzan a numerarse en 0, de ahí que el quinto registro tenga índice 4).

Podemos acceder a cualquier campo del registro usando su posición o bien su nombre, así:

ds.Tables(0).Rows(4)("CompanyName")

que devolverá un objeto del tipo adecuado para el campo que representa (una cadena, un objeto de
fecha, un booleano, etc...).

Nota:
Es muy sencillo definir objetos DataTable que dispongan de los campos que deseemos sin
depender de origen alguno de datos. Emplee el método Add de la colección Columns para
crear nuevos campos, algunos de los cuales pueden ser incluso derivados mediante una
fórmula de los valores de otros. Esto permite definir estructuras de almacenamiento a
medida en memoria sin preocuparnos de usar una base de datos para ello.

Ventajas del uso de objetos DataSet

Es posible cargar datos de varias tablas en un mismo DataSet, incluso aunque procedan de bases de
datos diferentes, y relacionarlas en memoria. Es posible establecer relaciones entre ellas para mantener
la consistencia, así como hacer un mantenimiento en memoria de los datos (altas, bajas y
modificaciones). Posteriormente se puede sincronizar el DataSet con el servidor usando un
DataAdapter, realizando el proceso contrario al de obtención de datos. Luego lo veremos.
Por: Saul Mamani Mamani 12

La principal ventaja de un DataSet es que podemos almacenarlo en donde queramos (en una variable
global, en caché, incluso a disco a una base de datos) para trabajar con él sin estar conectado a una
base de datos. De hecho se pueden transferir DataSet completos entre diferentes máquinas o por
Internet, trabajar con ellos en remoto, almacenarlos, recuperarlos y finalmente transferirlos en
cualquier momento de nuevo al origen (enteros o sólo los cambios) para sincronizarlos.

Es posible moverse con libertad entre los registros de una tabla y sus registros relacionados en otras
tablas. Y sobre todo, se pueden vincular con elementos de la interfaz gráfica para mostrar los datos
automáticamente.

Consultas parametrizadas

Las consultas simples como la que acabamos de utilizar en los ejemplos anteriores son muy raras. En la
realidad las consultas son mucho más complejas, suelen intervenir varias tablas y dependen de
diversos parámetros que le añaden condiciones.

Por ejemplo, si en la base de datos Northwind queremos obtener sólo aquellos clientes de un país
determinado. Podríamos escribir de nuevo la función DameClientes para que se pareciese a la
siguiente:

Esta función acepta un país como parámetro y lo único qe hace es concatenar a la consulta una nueva
condición que introduce el país.

Esto, sin duda, funcionaría. Sin embargo presenta multitud de problemas seguramente no demasiado
obvios para los programadores noveles. Los más importantes son los siguientes:

1. Este código es vulnerable a problemas de seguridad provocados por ataques de inyección de


SQL. Esto puede poner en peligro fácilmente nuestra aplicación e incluso todo nuestro sistema
en casos graves. El estudio de esta problemática se sale del ámbito de este curso, pero créame
cuando le digo que se trata de un gravísimo problema que debemos evitar a toda costa.
2. Si se solicitan 100 consultas idénticas al servidor en las que sólo varía el nombre del país por el
que se filtra, éste no tiene modo de saber que son las mismas y sacar factor común. Es decir, no
se puede reutilizar el plan de consulta de la primera de ellas para las demás por lo que se
debe calcular de nuevo cada vez, incidiendo en el rendimiento y la escalabilidad de la aplicación.
Obviamente en consultas más compllicadas es un problema más importante que en esta.
Por: Saul Mamani Mamani 13

3. Este código es más difícil de transportar a otros sistemas de bases de datos ya que hay que
incluir los delimitadores y notaciones específicos de cada gestor. Por ejemplo en SQL Server los
delimitadores de fechas son comillas simples ('), mientras que en Access son almohadillas (#) y
la sentencia usada no se puede reutilizar.

La forma correcta de realizar este tipo de consultas es utilizar parámetros en la consulta. Ello evita los
problemas enumerados.

Los objetos de tipo Command disponen de una colección llamada Parameters que sirve para asignar
dichos parámetros. Éstos previamente se definen en la consulta utilizando comodines que marquen su
ubicación.

Nota:
Cada proveedor de datos utiliza su propia convención para indicar la posición de los
parámetros en una consulta. En el caso de SQL Server se indican con una arroba '@'
seguida de un identificador. En otros proveedores no se puede definir nombre para los
parámetros, sólo se marca su posición con un caracter especial.

La función anterior empleando parámetros sería la siguiente:

Se ha resaltado los cambios.

Como vemos en lugar de concatenar cadenas se marca la posición de las partes de la consulta que
varían con un parámetro que consta de una arroba seguida de un identificador.

Luego hay que declarar el parámetro añadiéndolo a la colección de parámetros. Para ello se indica su
nombre y el tipo de datos que representa (en este caso un NVarChar de SQL Server, que es una cadena
de longitud variable).

Por fin se asigna su valor concreto antes de lanzar la consulta.


Por: Saul Mamani Mamani 14

El proveedor de datos crea la consulta de la manera correcta, usando los delimitadores adecuados y
tratando los datos del modo que sea preciso para asegurar que es correcta. Ello elimina los problemas
de los que hablábamos anteriormente y permite optimizar el uso de consultas.

Altas bajas y modificaciones

Con lo que hemos visto hasta ahora ya estamos en condiciones de escribir código para realizar altas,
bajas y modificaciones de registros. Al fin y al cabo éstas son simplemente consultas SQL del tipo
adecuado (INSERT, DELETE o UPDATE) que se deben enviar al servidor.

Así pues, para crear un nuevo cliente podemos escribir una función como la siguiente:

Es un código muy similar al anterior que realizaba una selección de datos. En este caso se ha definido
una consulta de inserción con tres parámetros. En lugar de usar ExecuteReader o un DataAdapter en
este caso se utiliza el método ExecuteNonQuery. Éste lanza la consulta (es decir, se inserta el nuevo
registro) y devuelve el número de registros afectados por la consulta (que en el caso de una inserción
siempre es 1 si se inserta correctamente o 0 si ha habido un error y lo capturásemosr).

Las actualizaciones y eliminaciones de registros se podrían conseguir de modo similar.

Trabajando desconectados

El código anterior funciona perfectamente pero no es la forma óptima de trabajar cuando tenemos que
tratar con datos desconectados contenidos en objetos DataSet.
Por: Saul Mamani Mamani 15

Los DataSet normalmente circulan, independientes de cualquier origen de datos, entre las diferentes
capas de una aplicación e incluso se mueven entre contextos de ejecución y máquinas (por ejemplo a
través de un servicio Web como veremos). Hemos dicho que son como pequeñas bases de datos, por lo
que la forma habitual de trabajar con ellos es añadir, eliminar y modificar registros directamente sobre
sus tablas, sin pensar en la ubicación real de estos datos.

Así pues, para crear un nuevo registro en un DataSet debemos usar el método NewRow de la
DataTable en la que lo queremos insertar. El nuevo registro tiene la misma estructura (el mismo
"esquema") que el resto de registros y sólo tendremos que rellenar sus datos. Una vez rellenados
añadiremos el nuevo registro a la colección de filas de la tabla. Así por ejemplo si tenemos almacenado
un DataSet con una tabla con los datos de los clientes obtenidos con la función de ejemplo
DameClientes, podríamos agregar uno nuevo de la siguiente manera:

Es decir, se crea la nueva fila/registro, se rellenan sus campos y se añade a la colección de filas con el
método Add de ésta.

La actualización de datos es más sencilla aún ya que basta con acceder directamente al registro que
nos interesa modificar y cambiar los valores de sus campos. Por ejemplo, para modificar la dirección del
cliente cuyos datos están en el quinto registro de nuestra tabla sólo hay que escribir:

Por fin, para eliminar un registro sólo hay que usar su método Delete, así:

que borraría el quinto registro de nuestra tabla en memoria.

Conciliando los cambios con el origen

Es muy fácil trabajar con los Dataset en memoria de este modo. Sólo hay un "pequeño" problema: los
cambios se realizan en memoria pero, al no estar vinculado el DataSet con origen de datos alguno, no
los veremos reflejados en la base de datos que es lo que buscamos.

Debemos realizar una sincronización entre la representación en memoria de los datos y su ubicación
física real, para lo cual debemos hacer que los cambios trasciendan a la capa conectada. Al igual que
cuando los recuperamos, el trasiego en el otro sentido se realiza con la ayuda del puente que
representa la clase DataAdapter.

Al igual que utilizábamos la propiedad SelectCommand para indicar cómo se recuperaban los datos
hacia un DataSet, ahora debemos utilizar las propiedades InsertCommand, UpdateCommand y
DeleteCommand para indicar qué comandos se deben usar para agregar, modificar y eliminar los
registros modificados en memoria a través del DataSet.

Una vez especificados estos comandos sólo resta llamar al método Update del DataAdapter para que se
ocupe de sincronizar automáticamente todos los cambios que hayamos realizado en las tablas en
memoria. Este método acepta como argumentos un DataSet completo, una DataTable o incluso un
DataRow si sólo queremos actualizar un único registro.
Por: Saul Mamani Mamani 16

Definición de los comandos

Los comandos de inserción, modificación o borrado para el DataAdapter se definen del mismo modo que
en el código de ejemplo al principio de este apartado, es decir, se define la consulta apropieada y sus
parámetros. En esta ocasión como los parámetros serán rellenados automáticamente por el
DataAdapter hay qeu utilizar una sobrecarga del método Add de la colección de parámetros que incluye
el nombre del campo asociado con el parámetro, así:

En este caso se define el parámetro "@Dirección", de tipo NVarChar y longitud 60 que se refiere
siempre al valor del campo "Address" dela tabla.

Por ejemplo, para definir la consulta de eliminación de registros de la tabla de clientes usaríamos el
siguiente código:

siendo CustomerID la clave primaria de la tabla.

Una vez definidos los tres parámetros de alta, baja y modificación sólo resta llamar a Update para que
el DataAdapter se ocupe de toda la sincronización.

Ventajas

Este modelo está lleno de ventajas aunque a primera vista pueda parecer algo engorroso.

Nota:
Luego veremos que podemos usar las herramientas que nos proporciona Visual Studio
2005 para definir de manera automática los comandos de manipulación de datos sin
necesidad de pasar el trabajo de hacerlo manualmente.

Para empezar podemos trabajar con los datos en total libertad sin preocuparnos de si existe conexión o
no con el origen y aunque el DataSet se manipule en una ubicación física a miles de kilómetros del
origen y desconectado de éste o los cambios se almacenen a disco y se concilien días más tarde.

Se pueden realizar multitud de modificaciones en los datos y luego conciliarlas simultáneamente en


lugar de hacerlo en tiempo real de una en una.

El paso de datos entre capas y funciones se simplifica. Lo habitual antes era definir funciones con tantos
parámetros como datos se quisieran modificar en un registro. Por ejemplo, para insertar un registro en
una tabla que tiene 20 campos se definía una función con 20 parámetros (muchos de ellos opcionales)
o, en el mejor de los casos, se pasaba una clase creada a medida que representaba lo valores del
registro. Ahora basta con pasar un DataSet, un DataTable o un dataRow que ya contiene toda la
información que necesitamos saber sobre los registros y su tabla asociada.

Lo mejor es que es posible saber mediante ciertas propiedades qué registros han cambiado (nuevos,
modificados, borrados) y mover entre capas exclusivamente estos. La propiedad HasChanges de los
DataSet, DataTable y DataRow nos informa de si el objeto ha sufrido cambios de algún tipo.
Por: Saul Mamani Mamani 17

El método GetChanges de los objetos DataSet y DataTable devuelve un subconjunto de los datos que
contiene exclusivamente los cambios. Así, aunque en un DataSet tengamos 1000 registros, si sólo
hemos modificado 2 sólo será necesario trasegar la información de estos a la hora de enviarlos a otra
capa o función para sincronizarlos con la base de datos.

El método GetChanges se puede invocar sin parámetros o indicando qué tipo de cambios queremos
obtener, lo que se indica con un valor de la enumeración DataRowState:

Valor Significado

Added Registros que no estaban originalmente.


Deleted Registros que se han eliminado

Modified Registros cuyos valores se han


modificado

UnChanged Registros que no se han modificado

Detached Registros que se han desasignado de una


tabla (pero no borrado con Delete)

Se puede dejar un DataSet en estado sin modificar llamando a su método AceptChanges. Esto es lo
que hace un DataAdapter tras haber sincronizado los cambios con el origen de datos.

Acceso a datos con Visual Studio 2005

Ahora que ya hemos visto la forma de trabajo manual con ADO.NET y dominamos los fundamentos,
vamos a sacar partido a todas las ventajas que nos proporciona un entorno como Visual Studio 2005
que, como veremos, nos permiten hacer casi cualquier tarea de datos sin necesidad de escribir código.

Lección 3: Acceso a datos con Visual Studio 2005


o Controles de datos
 Orígenes de datos
 Controles enlazados
 Concurrencia optimista
 Uso de los controles enlazados en la práctica
o DataSets tipados

Controles de datos

Aparte de la escritura manual de código y el uso directo de objetos de


ADO.NET, ASP.NET 2.0 proporciona un nuevo modelo de trabajo
declarativo para acceso a datos que nos permite realziar tareas
comunes de acceso a datos sin escribir código alguno.

ASP.NET 2.0 presenta dos nuevos tipos de controles Web que participan
en este modelo declarativo de enlace a datos. Estos controles nos
abstraen por completo de las complejidades de manejo de las clases de
ADO.NET por un lado, y de las dificultades inherentes al modo de
trabajo desconectado de las páginas Web. Equiparan en muchos
Por: Saul Mamani Mamani 18

aspectos el desarrollo web al tradicional desarrollo de aplicaciones de escritorio.

Estos controles se encuentran agrupados en el cuadro de herramientas bajo el nombre de "Datos", tal y
como se ve en la figura.

Orígenes de datos

Estos controles de datos representan conexiones con diferentes tipos de orígenes de información que
van desde bases de datos a objetos de negocio. No disponen de apariencia visual pero se arrastran
igualmente sobre los formularios Web para trabajar con ellos visualmente y poder usar sus paneles de
tareas. Abstraen al programador de las complejidades relacionadas con el manejo de los datos,
permitiendo de manera automática selecionarlos, actualizarlos, ordenarlos, paginarlos, etc..

ASP.NET 2.0 ofrece los siguientes controles de origen de datos que se pueden ver en la figura anterior:

Control Descripción

SqlDataSource Enlaza con cualquier base de datos para


que exista un proveedor de ADO.NET.
AccessdataSource Esta especializado en trabajar con bases
de datos Microsoft Access.
ObjectDataSource Se enlaza con objetos de negocio y capas
personalizadas de acceso a datos.
XmlDataSource Trata datos contenidos en documentos
XML.
SiteMapDataSource Se enlaza con la jerarquía de clases
expuesta por el modelo de navegación de
sitios de ASP.NET 2.0.

¡ATENCIÓN!: El control SqlDataSource sirve para enlazar con cualquier gestor de


datos relacional para la que haya proveedor ADO.NET disponible, no sólo para enlazar
con SQL Server. No se deje despistar por su nombre.

Concurrencia optimista

Durante la configuración de un origten de datos SQL (luego lo verá en el primer vídeo de esta lección)
una de las opciones avanzadas que se presenta habla de la Concurrencia optimista. La concurrencia
optimista evita la actualización de registros en el caso de que haya variado cualquiera de sus campos
desde que se obtuvieron de la fuente de datos. Este comportamiento puede ser bueno en ciertas
ocasiones, cuando queremos preservar los cambios realizados por cualquier usuario. En otras sin
embargo no resulta de utilidad y sí añade una gran sobrecarga al acceso a datos.

Se le denomina concurrencia optimista porque parte de la base de que la posibilidad de coincidencia de


dos usuarios sobre el mismo registro es baja y es un caso que apenas se dará. Al caso contrario a la
concurrencia optimista se le denomina concurrencia pesimista.

Nota:
En mi opinión debería llamarse "concurrencia realista" ya que, lo cierto es que si se
analiza con detenimiento la posibilidad de conflicto en un sistema grande tiende a ser
realmente pequeña en la mayoría de los casos. Y de todos modos el sobreescribir cierta
información no suele ser un problema grave salvo cuando hablamos de cuestiones de
dinero, facturas y similares.

Cuando se establece la concurrencia optimista las consultas que genera el asistente de Visual Studio
incluyen todos los campos del registro como condición de búsqueda del mismo, por ejemplo:
Por: Saul Mamani Mamani 19

DELETE FROM [Customers] WHERE [CustomerID] = @original_CustomerID AND

[CompanyName] = @original_CompanyName AND [ContactName] = @original_ContactName

AND [ContactTitle] = @original_ContactTitle AND [Address] = @original_Address AND

[City] = @original_City AND [Region] = @original_Region AND [PostalCode] =

@original_PostalCode AND [Country] = @original_Country AND [Phone] =

@original_Phone AND [Fax] = @original_Fax

mientras que en un caso de concurrencia pesimista se emplea simplemente la clave primaria del
registro para localizarlo:

DELETE FROM [Customers] WHERE [CustomerID] = @original_CustomerID

Es decisión suya qué tipo de actualización utilizar pero en la mayor parte de los casos usará
seguramente la pesimista que, de hecho, es la que usted utiliza normalmente si escribe las funciones a
mano de manera independiente.

Controles enlazados a datos

Se trata de controles de interfaz de usuario que, haciendo uso de los anteriores, muestran la
información a través de la página. Pueden sacar partido a todas las propiedades de los orígenes de
datos y por lo tanto habilitan de manera sencilla la edición, eliminación, ordenación, filtrado y
paginación de los datos entre otras cosas.

Para conectar un control enlazado a un DataSource sólo hay que establecer su propiedad
DataSourceID indicando el nombre apropiado. Así de facil.

Nota:
Si usted ya conoce los controles enlazados a datos de ASP.NET 1.x como el DataGrid, el
DataRepeater, etc... estos controles le resultarán más cómodos de utilizar pues al
contrario que con aquellos no hay que escribir código alguno. Aunque estos controles
"antiguos" no aparecen en el cuadro de herramientas siguen estando soportados y los
puede seguir utilizando. Incluso si lo desea puede añadirlos al cuadro de herramientas.
De todos modos se recomienda utilizar el nuevo modelo declarativo de acceso a datos.

Uso de los controles de datos en la práctica

La mejor forma de conocer la manera de trabajar con estos controles es viéndolos actuar en la práctica.
Los vídeos de esta lección muestran un ejemplo completo de uso de los controles en el que se explotan
unos datos para su visualización, edición y eliminación. Por favor, examínelo con atención y luego trate
de practicarlo por su cuenta con ejemplos similares.

DataSet tipados

La clase DataSet, como cualquier otra clase no sellada de .NET, puede ser extendida mediante herencia
para añadirle nuevas funcionalidades y aprovechar las ya existentes. Si creamos una nueva clase que
herede de DataSet y ésta la especializamos en el tratamiento de un conjunto de datos determinado que
conocemos de antemano nos encontramos un DataSet especializado.

Por ejemplo, podemos crear un DataSet especial que tengan en cuenta las particularidades de los datos
que maneja para validarlos antes de permitir su inserción, que verifique las relaciones con otros datos o
que los transforme o controle el acceso a los mismos. Nosotros sólo tendríamos que ocuparnos de estas
Por: Saul Mamani Mamani 20

tareas dejando a la clase DataSet de la que hemos heredado todos los puntos complejos que tienen que
ver con la gestión de datos pura y dura.

Esta es, en esencia, la idea que subyace bajo los DataSet tipados de Visual Studio 2005. Se trata de
clases especializadas derivadas de DataSet que ofrecen una forma más rápida y sencilla de acceder a
los datos que albergan. Además Visual Studio nos permite crear este tipo de DataSet de forma visual y
usando asistentes. En el vídeo se ilustra la manera de conseguirlo.

Los DataSet tipados parten del conocimiento preciso de la estructura de una base de datos para definir
su funcionalidad. Esta es su principal diferencia con los DataSet normales puesto que éstos son
genéricos y no saben nada acerca de los datos que albergan. Los tipados sólo sirven para albergar una
información muy concreta. Ganan en especialización y pierden en versatilidad.

Para agregar un DataSet tipado a nuestro proyecto sólo hay que presionar con el botón secundario
sobre la carpeta App_Code y en el diálogo que aparece elegir el icono de DataSet. La extensión del
archivo generado es '.xsd' porque lo que en realidad albergan es un esquema XML que define la
estructura de los datos en los que se van a especializar.

Una vez agregado el DataSet aparece una superficie de diseño y un asistente como el de la figura.

Figura 4.5.- Primer paso del asistente de configuración de un TableAdapter.

Este asistente nos permite configurar un objeto TableAdapter que se encargará de trasegar datos
entre el DataSet tipado que estamos creando y la base de datos. Un TableAdapter es una clase que
encapsula a un DataAdapter especializado en los datos que vamos a procesar con la tabla del dataSet
tipado que estamos definiendo. De hecho sus métodos son, por defecto, los mismos que los de un
DataAdapter normal (Fill, Update...).

Con él dispondremos de métodos para recuperar información, crear nuevos registros, actualizarlos y
eliminarlos, sólo que los correspondientes comandos estarán creados de manera automática o
asistiéndonos en ello. Así, por defecto, se definen un par de métodos para obtener los datos
subyacentes rellenando un DataSet que se le pase (método Fill) o devolviendo directamente un
DataTable (método GetData). Además el método Update sirve para conciliar automáticamente los
cambios del un DataSet tipado con la base de datos original.

No vamos a analizar desde el texto la definición de estos objetos adaptadores pero puede conocerlo
viendo el vídeo de esta lección.
Por: Saul Mamani Mamani 21

Truco:
Podemos ver el código que se genera de manera automática para crear el DataAdapter si
hacemos doble-clic sobre él desde el examinador de objetos de Visual Studio
(CTRL+ALT+J). Esto hará que se abra el archivo de código auto-generado por ASP.NET
desde la ubicación real de ejecución (normalmente una ruta del estilo
C:\Windows\Microsoft.NET\....). Es muy interesante echarle un vistazo a este código para
aprender el funcionamiento interno de los DataSet tipados.

Partes de un DataSet tipado

Al igual que un DataSet normal, uno tipado consta de un conjunto de tablas y relaciones entre ellas. En
este caso sin embargo podemos acceder a las tablas y a sus campos utilizando directamente sus
nombres en lugar de recorrer la colección de tablas, lo cual lo hace más fácil de usar.

Cada una de las tablas del DataSet lleva asociado como mínimo un TableAdapter. Entre los dos objetos
(el DataTable y el o los TableAdapter relacionados) se reparten el trabajo. El DataTable tipado mantiene
en memoria la información y el TableAdapter actúa de puente con la tabla real en la base de datos.

Nota:
Como sabemos (y veremos en el vídeo también) las tablas de un DataSet no tienen
porqué coincidir con tablas reales de una base de datos ya que pueden ser resultados
obtenidos de una consulta compleja que involucre a varias tablas. En estos casos es más
complicada la actualziación y se suelen usar únicamente para recuperar datos. la
alternativa habitual es tratar de replicar la estructura física de la base de datos en la
estructura en memoria del DataSet de modo que se tiene acceso estructurado a la misma
información y gracias a las relaciones y restricciones se conserva la consistencia de los
datos tambiñen estando desconectados.

El DataSet tipado dispone de propiedades que coinciden con los nombres de los objetos que contienen.
Así, por ejemplo, si tenemos una tabla "Clientes" con un campo "Nombre" podemos acceder a él
directamente con este código:

ds.Clientes(0).Nombre

que nos daría el nombre del primer cliente de la tabla de clientes en el DataSet 'ds'. esta propiedad
nombre además ya sería un campo de tipo String que es el tipo adecuado para la información
albergada, por lo que se simplifica mucho su uso.

En un DataSet normal para obtener lo mismo tendríamos que haber escrito:

ds..Tables(0).Rows(0)("Nombre").ToString()

que obviamente es mucho menos legible.

La cosa no termina aquí ya que además se definen clases específicas para representar los registros de
las tablas. Por ejemplo si la tabla se llama 'Clientes' existirá una clase ClientesRow que dispone de
propiedades con el mismo nombre y tipo que los campos correspondientes en la tabla de la base de
datos y que hemos usado en la línea de ejemplo. Así, para añadir un cliente podríamos escribir:

Dim cliente As New Cliente


Por: Saul Mamani Mamani 22

cliente.Nombre = "Pepe"

....

ds.Clientes.AddClientesRow(cliente)

Nótese que el método Add del DataTable se ha sustituido por uno con nombre especializado que nos
ayuda a saber mejor por donde pisamos, pero su función es idéntica.

Para rellenar una tabla de un DataSet tipado se usa su correspondiente TableAdapter así:

Dim clientes As New ClientesDS


Dim ta As New ClientesTableAdapters.ClientesTableAdapter()
ta.Fill(clientes.Clientes)

clientes.Clientes(0).Nombre = "Pepe"

....

o también usando el método GetData para obtener la tabla directamente si sólo nos interesa esa parte
concreta del DataSet (que puede constar de varias tablas):

Dim clientes As Clientes.ClientesDataTable


Dim ta As New ClientesTableAdapters.ClientesTableAdapter()
clientes = ta.GetData()

clientes(0).Nombre = "Pepe"

....

Para un mismo DataTable tipado se pueden definir diversos TableAdapter especializados aparte del
básico que permite llenar todos los datos: para filtrar podr diversos parámetros normalmente.

El uso de DataSet tipados es muy recomendable puesto que simplifica mucho el trabajo puesto que
podemos realizar casi todo el trabajo utilizando asistentes y accediendo a la información de manera
muy cómodo. Además es un modo muy sencillo de separar la funcionalidad de la base de datos del
resto del código. Así, si se hace necesario en el futuro, se puede exponer esta parte de manera
independiente mediante un, por ejemplo, un servicio Web que utilice el DataSet tipado y sus
TableAdapters para acceder a los datos desde una ubicación remota.

Anda mungkin juga menyukai