Está en la página 1de 20

UNIVERSIDAD TECNOLÓGICA NACIONAL Técnico Superior en Programación Laboratorio IV

ADO.NET

Información General de ADO.NET

UNIVERSIDAD TECNOLÓGICA NACIONAL Técnico Superior en Programación Laboratorio IV ADO.NET Información General de ADO.NET ADO.NET es

ADO.NET es una evolución del modelo de acceso a datos de ADO que controla directamente los requisitos del usuario para programar aplicaciones escalables. Se diseñó específicamente para el Web, teniendo en cuenta la escalabilidad, la independencia y el estándar XML.

ADO.NET utiliza algunos objetos ADO, como Connection y Command, y también agrega objetos nuevos. Algunos de los nuevos objetos clave de ADO.NET son DataSet, DataReader y DataAdapter.

La diferencia más importante entre esta fase evolucionada de ADO.NET y las arquitecturas de datos anteriores es que existe un objeto, DataSet, que es independiente y diferente de los almacenes de datos. Por ello, DataSet funciona como una entidad independiente. Se puede considerar el objeto DataSet como un conjunto de registros que siempre está desconectado y que no sabe nada sobre el origen y el destino de los datos que contiene. Dentro de un objeto DataSet, de la misma manera que dentro de una base de datos, hay tablas, columnas, relaciones, restricciones, vistas, etc.

El objeto DataAdapter es el objeto que se conecta a la base de datos para llenar el objeto DataSet. A continuación, se vuelve a conectar a la base de datos para actualizar los datos de dicha base de datos a partir de las operaciones realizadas en los datos contenidos en el objeto DataSet. En el pasado, el procesamiento de datos se basaba principalmente en la conexión. Ahora, con el fin de proporcionar a las aplicaciones multinivel mayor eficacia, se está adoptando para el procesamiento de datos un enfoque basado en mensajes que manipulan fragmentos de información. En el centro de este enfoque se sitúa el objeto DataAdapter, que proporciona un puente entre un objeto DataSet y un almacén de datos de origen para recuperar y guardar datos. Para ello, envía solicitudes a los comandos SQL apropiados que se ejecutan en el almacén de datos.

El objeto DataSet basado en XML proporciona un modelo de programación coherente que funciona con todos los modelos de almacenamiento de datos: sin formato, relacional o jerárquico. Funciona sin tener 'conocimiento' del origen de los datos y representa a los datos que contiene como colecciones y tipos de datos. Independientemente del origen de los datos del objeto DataSet, éstos se manipulan mediante el mismo conjunto de API estándar expuestas a través del objeto DataSet y sus objetos subordinados.

Aunque el objeto DataSet no tiene conocimiento del origen de sus datos, el proveedor administrado tiene información detallada y específica. La función del proveedor administrado es conectar, llenar y almacenar el objeto DataSet desde almacenes de datos (o viceversa). Los proveedores de datos OLE DB y SQL Server de .NET (System.Data.OleDb y System.Data.SqlClient) que forman parte de .Net Framework proporcionan cuatro objetos básicos: Command, Connection, DataReader y DataAdapter. En el resto de las secciones de este documento, se describirá cada parte del objeto DataSet y los proveedores de datos OLE DB y SQL Server de .NET, con el fin de explicar qué son y cómo se pueden utilizar al programar.

En las siguientes secciones se presentarán algunos objetos que han evolucionado desde la tecnología anterior y otros objetos nuevos. Los objetos son los siguientes:

Objetos Connection. Para conectar con una base de datos y administrar las transacciones en una base de

datos. Objetos Command. Para emitir comandos SQL a una base de datos.

Objetos DataReader. Proporcionan una forma de leer una secuencia de registros de datos sólo hacia delante

desde un origen de datos SQL Server. Objetos DataSet. Para almacenar datos sin formato, datos XML y datos relacionales, así como para configurar

el acceso remoto y programar sobre datos de este tipo. Objetos DataAdapter. Para insertar datos en un objeto DataSet y reconciliar datos de la base de datos.

Nota al trabajar con conexiones a una base de datos, hay dos opciones diferentes: un proveedor de datos de SQL Server de .NET (System.Data.SqlClient) y un proveedor de datos OLE DB de .NET (System.Data.OleDb). En estos ejemplos se utilizará el proveedor de datos SQL Server de .NET. Están programados para comunicarse directamente con Microsoft SQL Server. El proveedor de datos OLE DB de .NET se utiliza para comunicarse con cualquier proveedor OLE DB (ya que utiliza OLE DB como tecnología subyacente).

Conexiones

Para establecer la comunicación con bases de datos, se utilizan las conexiones y se representan mediante clases específicas de proveedor, como SQLConnection. Los comandos viajan por las conexiones y devuelven conjuntos de resultados en forma de secuencias que puede leer un objeto DataReader o que se pueden insertar en un objeto DataSet.

En el ejemplo siguiente se muestra la forma de crear un objeto de conexión. Las conexiones se pueden abrir explícitamente mediante llamadas al método Open de la conexión; también se pueden abrir implícitamente al utilizar un objeto DataAdapter.

namespace HowTo.Samples.ADONET {

using System; using System.Data.SqlClient;

public class adooverview1 {

public static void Main() {

adooverview1 myadooverview1 = new adooverview1();

myadooverview1.Run();

}

public void Run() {

SqlConnection mySqlConnection = new SqlConnection("server=(local)\\NetSDK;Trusted_Connection=yes;database=northwind");

try

{

mySqlConnection.Open(); Console.WriteLine("Conexin con {0} abierta", mySqlConnection.ConnectionString);

// Close the connection explicitly

mySqlConnection.Close(); Console.WriteLine("Conexin cerrada. Es importante cerrar las conexiones explcitamente.");

}

catch

{

Console.WriteLine("No se pudo abrir la conexin con {0}", mySqlConnection.ConnectionString);

}

}

}

}

Comandos

Los comandos contienen la información que se envía a una base de datos y se representan mediante clases específicas de un proveedor, como SQLCommand. Un comando podría ser una llamada a un procedimiento almacenado, una instrucción UPDATE o una instrucción que devuelve resultados. También es posible utilizar parámetros de entrada o de resultados y devolver valores como parte de la sintaxis del comando. En el ejemplo siguiente se muestra la forma de ejecutar una instrucción INSERT en la base de datos Northwind.

namespace HowTo.Samples.ADONET {

using System; using System.Data.SqlClient;

public class adooverview2 {

public static void Main() {

adooverview2 myadooverview2 = new adooverview2();

myadooverview2.Run();

}

public void Run() {

string Message = null;

SqlConnection myConnection = new SqlConnection("server=(local)\\NetSDK;Trusted_Connection=yes;database=northwind");

SqlCommand mySqlCommand = new SqlCommand("INSERT INTO Customers (CustomerId, CompanyName, ContactName, ContactTitle, Address) Values ('ABC','ABC Company', 'John Smith', 'Owner','One My Way')", myConnection); SqlCommand mySqlCleanup = new SqlCommand("DELETE FROM Customers WHERE CustomerId = 'ABC'", myConnection);

try

{

myConnection.Open(); mySqlCleanup.ExecuteNonQuery(); // remove record that may have been entered previously. mySqlCommand.ExecuteNonQuery(); Message = "Se ha insertado un registro nuevo en la tabla Customers de Northwind.";

}

catch(Exception e)

{

Message= "No se pudo insertar el registro: " + e.ToString();

}

finally

{

myConnection.Close();

}

Console.Write(Message);

}

}

}

Objetos DataReader

El objeto DataReader es, en cierto modo, sinónimo de un cursor de sólo lectura y sólo hacia delante para datos. La API de DataReader es compatible con datos sin formato y con datos jerárquicos. Cuando se ejecuta un comando en la base de datos, se devuelve un objeto DataReader. El formato del objeto DataReader devuelto es distinto de un conjunto de registros. Por ejemplo, podría utilizarse el objeto DataReader para mostrar los resultados de una lista de búsqueda en una página Web.

namespace HowTo.Samples.ADONET {

using System; using System.Data; using System.Data.SqlClient;

public class adooverview3 {

public static void Main() {

adooverview3 myadooverview3 = new adooverview3();

myadooverview3.Run();

}

public void Run() {

SqlDataReader myReader = null;

SqlConnection mySqlConnection = new

SqlConnection("server

=(local)\\NetSDK;Trusted_Connection=yes;database=northwind"); SqlCommand mySqlCommand = new SqlCommand("select * from customers", mySqlConnection);

try

{

mySqlConnection.Open(); myReader = mySqlCommand.ExecuteReader();

Console.Write("Customer ID

");

Console.WriteLine("Nombre de la compañía");

while (myReader.Read()) {

Console.Write(myReader["CustomerID"].ToString() + "

");

Console.WriteLine(myReader["CompanyName"].ToString());

}

}

catch(Exception e)

{

Console.WriteLine(e.ToString());

}

finally

{

if (myReader != null) myReader.Close();

if (mySqlConnection.State == ConnectionState.Open) mySqlConnection.Close();

}

}

}

}

Objetos DataSet y DataAdapter

Objetos DataSet

El objeto DataSet es similar al objeto Recordset de ADO, pero más eficaz y con una diferencia importante: DataSet siempre está desconectado. El objeto DataSet representa a una memoria caché de datos, con estructuras análogas a las de una base de datos, como tablas, columnas, relaciones y restricciones. Sin embargo, aunque se puede utilizar un objeto DataSet como una base de datos (y su comportamiento es muy similar), es importante recordar que los objetos DataSet no interactúan directamente con bases de datos ni con otros datos de origen. Esto permite al programador trabajar con un modelo de programación que siempre es coherente, independientemente de dónde resida el origen de datos. En los objetos DataSet se pueden colocar datos provenientes de una base de datos, un archivo XML, código o información escrita por el usuario. A continuación, a medida que se realizan cambios en el objeto DataSet, se puede hacer un seguimiento y una comprobación de los cambios antes de actualizar los datos de origen. El método GetChanges del objeto DataSet crea en realidad otro objeto DataSet que sólo contiene los cambios realizados en los datos. Posteriormente, un objeto DataAdapter u otros objetos, utilizan este objeto DataSet para actualizar el origen de datos original.

El objeto DataSet tiene muchas características de XML, incluida la capacidad de producir y consumir datos XML y esquemas XML. Los esquemas XML se pueden utilizar para describir esquemas intercambiables a través de servicios Web. De hecho, un objeto DataSet con un esquema puede compilarse con seguridad de tipos y finalización automática de instrucciones.

Objetos DataAdapter (OLEDB/SQL)

El objeto DataAdapter funciona como un puente entre el objeto DataSet y los datos de origen. El uso del objeto SqlDataAdapter específico del proveedor (junto con los objetos SqlCommand y SqlConnection asociados) permite aumentar el rendimiento global al trabajar con bases de datos de Microsoft SQL Server. Para otras bases de datos compatibles con OLE DB, se debe utilizar el objeto OleDbDataAdapter y los objetos OleDbCommand y OleDbConnection asociados.

El objeto DataAdapter utiliza comandos para actualizar el origen de datos después de hacer modificaciones en el objeto DataSet. Si se utiliza el método Fill del objetoDataAdapter, se llama al comando SELECT; si se utiliza el método Update se llama al comando INSERT, UPDATE o DELETE para cada fila modificada. Es posible establecer explícitamente estos comandos con el fin de controlar las instrucciones que se utilizan en tiempo de ejecución para resolver cambios, incluido el uso de procedimientos almacenados. En escenarios ad-hoc, un objeto CommandBuilder puede generarlos en tiempo de ejecución a partir de una instrucción de selección. Sin embargo, para generar en tiempo de ejecución hay que hacer un viaje de ida y vuelta adicional al servidor con el fin de recopilar los metadatos necesarios; por tanto, si se proporcionan explícitamente los comandos INSERT, UPDATE y DELETE en tiempo de diseño, el rendimiento en tiempo de ejecución mejorará.

SqlConnection myConnection = new SqlConnection("server= (local)\VSdotNET;Trusted_Connection=yes;database=northwind"); SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter("select * from customers", myConnection); mySqlDataAdapter.InsertCommand.CommandText = "sp_InsertCustomer"; mySqlDataAdapter.InsertCommand.CommandType = CommandType.StoredProcedure; mySqlDataAdapter.DeleteCommand.CommandText = "sp_DeleteCustomer"; mySqlDataAdapter.DeleteCommand.CommandType = CommandType.StoredProcedure; mySqlDataAdapter.UpdateCommand.CommandText = "sp_UpdateCustomers"; mySqlDataAdapter.UpdateCommand.CommandType = CommandType.StoredProcedure; mySqlDataAdapter.Update(myDataSet);

Los registros se asignan a los comandos correspondientes de la forma apropiada.

Figura: Objetos DataAdapter y DataSet En el ejemplo siguiente se ilustra la carga de un objeto

Figura: Objetos DataAdapter y DataSet

En el ejemplo siguiente se ilustra la carga de un objeto DataAdapter a través de una instrucción SELECT. A continuación, se actualizan, eliminan y agregan algunos registros en el objeto DataSet. Por último, se devuelven las actualizaciones a la base de datos de origen a través del objeto DataAdapter. En la página, se muestran los comandos DeleteCommand, InsertCommand y UpdateCommand creados. También se ilustra el uso de varios objetos DataAdapter para cargar varias tablas (Customers y Orders) en el objeto DataSet.

namespace HowTo.Samples.ADONET {

using System; using System.Data; using System.Data.SqlClient;

public class adooverview4 {

public static void Main() {

adooverview4 myadooverview4 = new adooverview4();

myadooverview4.Run();

}

public void Run() {

// Create a new Connection and SqlDataAdapter

SqlConnection myConnection = new SqlConnection("server= (local)\\NetSDK;Trusted_Connection=yes;database=northwind"); SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter("Select * from Region", myConnection); SqlParameter workParam = null;

// Restore database to it's original condition so sample will work correctly.

Cleanup();

// Build the insert Command mySqlDataAdapter.InsertCommand = new SqlCommand("Insert into Region (RegionID, RegionDescription) VALUES (@RegionID, @RegionDescription)", myConnection);

workParam = mySqlDataAdapter.InsertCommand.Parameters.Add("@RegionID", SqlDbType.Int); workParam.SourceColumn = "RegionID"; workParam.SourceVersion = DataRowVersion.Current;

workParam = mySqlDataAdapter.InsertCommand.Parameters.Add("@RegionDescription", SqlDbType.NChar, 50); workParam.SourceVersion = DataRowVersion.Current; workParam.SourceColumn = "RegionDescription";

// Build the update command

mySqlDataAdapter.UpdateCommand

=

new

SqlCommand("Update

Region

Set

RegionDescription

=

@RegionDescription WHERE RegionID = @RegionID" , myConnection);

workParam = mySqlDataAdapter.UpdateCommand.Parameters.Add("@RegionID", SqlDbType.Int); workParam.SourceColumn = "RegionID"; workParam.SourceVersion = DataRowVersion.Original;

workParam = mySqlDataAdapter.UpdateCommand.Parameters.Add("@RegionDescription", SqlDbType.NChar, 50); workParam.SourceVersion = DataRowVersion.Current; workParam.SourceColumn = "RegionDescription";

DataSet myDataSet = new DataSet();

// Set the MissingSchemaAction property to AddWithKey because Fill will not cause primary key & unique key information to be retrieved unless AddWithKey is specified.

mySqlDataAdapter.MissingSchemaAction = MissingSchemaAction.AddWithKey; mySqlDataAdapter.Fill(myDataSet, "Region");

DataRow myDataRow1 = myDataSet.Tables["Region"].Rows.Find(2); myDataRow1[1] = "Se ha cambiado la descripcin de esta regin.";

DataRow myDataRow2 = myDataSet.Tables["Region"].NewRow(); myDataRow2[0] = 901; myDataRow2[1] = "Regin nueva";

myDataSet.Tables["Region"].Rows.Add(myDataRow2);

try

{

mySqlDataAdapter.Update(myDataSet, "Region"); Console.Write("El conjunto de datos se actualizó correctamente.");

}

catch(Exception e)

{

Console.Write(e.ToString());

}

}

public void Cleanup() {

SqlConnection myConnection = new SqlConnection("server= (local)\\NetSDK;Trusted_Connection=yes;database=northwind");

try

{

// Restore database to it's original condition so sample will work correctly.

myConnection.Open(); SqlCommand CleanupCommand = new SqlCommand("DELETE FROM Region WHERE RegionID = '901'", myConnection); CleanupCommand.ExecuteNonQuery();

}

catch(Exception e)

{

Console.Write(e.ToString());

}

finally

{

myConnection.Close();

}

}

}

}

Resumen

  • 1. La tecnología ADO.NET, integrada en .Net Framework, es el siguiente estado de evolución de ADO.

  • 2. Se diseñó teniendo en cuenta los modelos multinivel, la independencia y el estándar XML. Para estos escenarios se proporcionan dos objetos nuevos, DataSet y DataAdapter.

  • 3. Se puede utilizar ADO.NET para obtener datos de una secuencia o para almacenar datos en una memoria caché a fin de realizar actualizaciones.

  • 4. La documentación contiene mucha más información acerca de ADO.NET.

  • 5. Hay que tener en cuenta que se puede ejecutar un comando directamente en la base de datos para realizar inserciones, actualizaciones y eliminaciones. Para insertar, actualizar o eliminar datos no hay que colocarlos primero en un objeto DataSet.

  • 6. Además, se puede utilizar un objeto DataSet para enlazar con los datos, examinarlos y explorar sus relaciones.

Ejecutar un comando

Los comandos se ejecutan en bases de datos con el fin de realizar acciones en almacenes de datos. Por ejemplo, sería posible ejecutar un comando para insertar o eliminar datos. Los comandos incluyen cualquier comando que se pueda ejecutar en una base de datos y, si se trata de un comando OleDbCommand, puede ser específico del almacén de datos. Por ejemplo, se puede emitir una llamada a un procedimiento almacenado para un comando o, quizás, un comando para "set quoted_identifier on". Independientemente de cuál sea el comando, se puede utilizar el objeto OleDbCommand o SqlCommand para ejecutar el comando en el almacén de datos del servidor.

En la tecnología tradicional de ADO se pueden emitir comandos a través de los objetos Command, Connection y Recordset. En ADO.NET, el único objeto que ejecuta comandos es Command.

Para emitir un comando en una base de datos, el objeto Command debe tener dos elementos básicos: un objeto Connection y un objeto CommandText, que se pueden establecer en el constructor. Para ejecutar el comando, es necesario abrir el objeto Connection ,que no debe estar en estado de búsqueda:

String InsertCmdString; InsertCmdString = "Insert into Region (RegionID, RegionDescription) VALUES (100, 'Description')"; SqlCommand mySqlCommand = new SqlCommand(InsertCmdString, myConnection);

En este tema se trata la ejecución de comandos que no generan resultados. Para ejecutar un comando que no devuelve resultados, hay que llamar al método ExecuteNonQuery.

mySqlCommand.ExecuteNonQuery();

Los objetos OleDbCommand y SqlCommand tienen colecciones de parámetros que presentan el mismo comportamiento que las colecciones de parámetros de ADO. Se pueden transferir los parámetros en línea:

mySqlCommand.CommandText = "myStoredProc 'CustId'";

O se puede utilizar la colección Parameters:

workParam = mySqlCommand.Parameters.Add("@CustomerID", SQLDataType.NChar, 5); workParam.Value = "NewID";

El siguiente ejemplo muestra como insertar un comando insert usando SqlCommand.

namespace HowTo.Samples.ADONET {

using System; using System.Data; using System.Data.SqlClient; using System.Data.Common;

public class executingacommand {

public static void Main() {

executingacommand myexecutingacommand = new executingacommand(); myexecutingacommand.Run();

}

public void Run() {

SqlConnection myConnection = new SqlConnection("server= (local)\\NetSDK;Trusted_Connection=yes;database=northwind"); SqlCommand myCommand = new SqlCommand(); SqlTransaction myTrans;

// Open the connection.

myConnection.Open();

// Assign the connection property.

myCommand.Connection = myConnection;

// Begin the transaction.

myTrans = myConnection.BeginTransaction();

// Assign transaction object for a pending local transaction

myCommand.Transaction = myTrans;

try

{

// Restore database to near it's original condition so sample will work correctly.

myCommand.CommandText = "DELETE FROM Region WHERE (RegionID = 100) OR (RegionID = 101)"; myCommand.ExecuteNonQuery();

// Insert the first record.

myCommand.CommandText = "Insert into Region (RegionID, RegionDescription) VALUES (100, 'MidWestern')";

myCommand.ExecuteNonQuery();

// Insert the second record.

myCommand.CommandText = "Insert into Region (RegionID, RegionDescription) VALUES (101, 'MidEastern')";

myCommand.ExecuteNonQuery();

myTrans.Commit(); Console.WriteLine("Ambos registros se han escrito en la base de datos.");

}

catch(Exception e)

{

myTrans.Rollback(); Console.WriteLine(e.ToString()); Console.WriteLine("Ninguno de los registros se ha escrito en la base de datos.");

}

finally

{

myConnection.Close();

}

}

}

}

Obtener parámetros de resultados de un procedimiento almacenado

Algunos procedimientos almacenados devuelven valores a través de parámetros. Cuando un parámetro de una instrucción SQL o un procedimiento almacenado está declarado como parámetro de resultados ("out"), se devuelve su valor al llamador. El valor se almacena en un parámetro de la colección Parameters de los objetos OleDbCommand o SqlCommand.

A diferencia de lo que ocurre en el ejemplo siguiente, cuando no se establecen ni la conexión ni el nombre del comando, se pueden establecer los parámetros, pero será necesario crear la colección de parámetros y definir los tipos esperados.

workParam = myCommand.Parameters.Add("@CustomerID", SQLDataType.NChar(5);

workParam.Value = "CUSTID";

En el siguiente ejemplo se muestra la forma de utilizar un procedimiento almacenado que devuelva parámetros de resultados. El comando se ejecuta para crear un procedimiento almacenado en la base de datos Northwind.

namespace HowTo.Samples.ADONET {

using System; using System.Data; using System.Data.SqlClient;

public class outparamswithacommand {

public static void Main() {

outparamswithacommand myoutparamswithacommand = new outparamswithacommand(); myoutparamswithacommand.Run();

}

public void Run() {

String MsgString = null;

// Create a new Connection and SqlDataAdapter

SqlConnection

myConnection

=

new

SqlConnection("server=(local)\\NetSDK;Trusted_Connection=yes;database=northwind");

// Create stored procedure with out parameter

try

{

SqlCommand CreateProcCommand = new SqlCommand("CREATE PROCEDURE GetCompanyName @CustomerId nchar(5), @CompanyName nchar(40) out as select @CompanyName = CompanyName from Customers where CustomerId = @CustomerId",myConnection); SqlCommand DropProcCommand = new SqlCommand("IF EXISTS (SELECT name FROM sysobjects WHERE name = 'GetCompanyName' AND type = 'P') DROP PROCEDURE GetCompanyName", myConnection);

myConnection.Open();

DropProcCommand.ExecuteNonQuery();

// remove procedure if it exists

CreateProcCommand.ExecuteNonQuery(); // create procedure

SqlCommand myCommand = new SqlCommand("GetCompanyName", myConnection); myCommand.CommandType = CommandType.StoredProcedure;

// Fill the parameters collection based upon stored procedure. SqlParameter workParam = null;

workParam = myCommand.Parameters.Add("@CustomerID", SqlDbType.NChar, 5);

// ParameterDirection.Input is the default for the Direction property. Thus the following line is not // needed here. To set the Direction property to its default value, use the following line. // workParam.Direction = ParameterDirection.Input;

workParam = myCommand.Parameters.Add("@CompanyName", SqlDbType.NChar, 40); workParam.Direction = ParameterDirection.Output;

myCommand.Parameters["@CustomerID"].Value = "ALFKI";

myCommand.ExecuteNonQuery(); MsgString = "Nombre de la compaa = " + myCommand.Parameters["@CompanyName"].Value.ToString();

}

catch(Exception e)

{

MsgString = e.ToString();

}

finally

{

myConnection.Close();

}

Console.Write(MsgString);

}

}

}

Llenar un objeto DataSet a partir de una base de datos

El hecho de recuperar datos de una base de datos y manipularlos es más fácil que nunca. Si desea mostrar los resultados de una base de datos como una secuencia de datos de sólo lectura y sólo hacia delante, es posible ejecutar un comando y recuperar los resultados mediante el objeto DataReader. En lo que se refiere a operaciones más interactivas, como enlazar datos, recorrer datos o la configuración remota de los resultados de una consulta de base de datos, es posible colocar los resultados en un objeto DataSet, de la forma mostrada en este ejemplo.

El concepto más importante que hay que recordar es que el objeto DataSet es una estructura de datos independiente y distinta de un almacén de datos. Aunque en el ejemplo se obtienen datos desde una base datos, no importa cuál sea su origen porque el objeto DataSet siempre presentará un modelo de programación coherente. Se trata de una colección de datos sencilla, con características de base de datos relacional. No se incluyen métodos Load, Open ni Execute, ya que no se sabe de dónde provienen los datos. En esta sección se describe la forma de utilizar un objeto SqlDataAdapter para cargar en el objeto DataSet datos de una base de datos.

Un objeto SqlDataAdapter puede utilizarse para recuperar datos de una base de datos, pero también se puede utilizar para insertar datos en la base de datos. En esta sección se tratará la obtención de datos.

El

primer paso

es crear

un objeto SqlDataAdapter. Esto se

hace

de

la

misma

manera que se crea un objeto

SqlCommand.

 

String SelectCmdString = "select * from customers"; SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter(SelectCmdString, myConnection);

//

...

or

this can also be done as follows:

SqlCommand mySelectCommand = New SqlCommand("select * from customers", myConnection); SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter(mySelectCommand);

Los objetos SqlDataAdapter y SqlCommand son muy similares, con la diferencia de los métodos Fill y Update. El método Fill llena un objeto DataSet. El método Update recoge los cambios realizados en un objeto DataSet y los aplica en la base de datos. Para ello se utilizan cuatro comandos especificados en el objeto DataAdapter. Estos comandos son: SelectCommand, UpdateCommand, InsertCommand y DeleteCommand. Es posible establecer explícitamente estos comandos para controlar las instrucciones que se utilizan en tiempo de ejecución con el fin de resolver cambios, incluido el uso de procedimientos almacenados. En escenarios ad-hoc, un objeto CommandBuilder puede generarlos en tiempo de ejecución a partir de una instrucción de. Sin embargo, para generar en tiempo de ejecución hay que hacer un viaje de ida y vuelta adicional al servidor a fin de recopilar los metadatos necesarios; por tanto, si se proporcionan explícitamente los comandos de inserción, actualización y eliminación en tiempo de diseño, el rendimiento en tiempo de ejecución será mayor.

NOTA: Visual Studio ayuda en gran medida a establecer objetos SqlDataAdapter y DataSet, y crea procedimientos almacenados de forma prácticamente automática. Estudie esta característica mediante los objetos ComponentDesigner y Database.

Cuando se haya establecido el objeto SqlDataAdapter, se le podrá pasar un objeto DataSet para llenarlo:

myDataSet = new DataSet(); mySqlDataAdapter.Fill(myDataSet,"Customers");

Ahora el objeto DataSet contiene el resultado de la consulta. De hecho, puede contener los resultados de varias consultas e incluso relacionarlos. Como contiene varios resultados, el objeto DataSet contiene un conjunto de tablas. Hay que tener en cuenta que el método Fill utiliza "Customers" como segundo argumento. Se trata del nombre de la tabla que se va a llenar en el objeto DataSet. Si la tabla no existe, se crea.

Como los datos se almacenan en una colección de filas de la tabla, es posible utilizar fácilmente una instrucción foreach para recorrer las filas:

foreach (DataRow myDataRow in myDataSet.Tables["Customers"].Rows) {

Console.WriteLine(myDataRow["CustomerId"].ToString());

}

De hecho, también se puede utilizar la misma instrucción foreach para recorrer las columnas. En el siguiente ejemplo se muestra la combinación de todo el código de este documento.

namespace HowTo.Samples.ADONET {

using System; using System.Data; using System.Data.SqlClient;

public class gettingdata {

public static void Main() {

gettingdata mygettingdata = new gettingdata(); mygettingdata.Run();

}

public void Run() {

SqlConnection myConnection = new SqlConnection("server= (local)\\NetSDK;Trusted_Connection=yes;database=northwind"); SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter("select * from customers", myConnection);

try

{

DataSet myDataSet = new DataSet();

mySqlDataAdapter.Fill(myDataSet,"Customers");

foreach (DataRow myDataRow in myDataSet.Tables["Customers"].Rows)

{

 

Console.WriteLine(myDataRow["CustomerId"].ToString());

}

}

catch(Exception e)

{

Console.WriteLine(e.ToString());

}

}

}

}

Actualizar una base de datos a partir de un objeto DataSet

En este tema se ilustra la forma de actualizar datos de una base de datos mediante un objeto DataSet.

Algunos de los temas que se explican en Llenar un objeto DataSet a partir de una base de datos son la carga de un objeto DataSet a partir de una base de datos y la diferencia e independencia entre un objeto DataSet y una base de datos. Cuando se haya cargado el objeto DataSet, se podrán modificar los datos y el objeto DataSet hará un seguimiento de los cambios.

El objeto DataSet se puede considerar una caché en memoria de datos obtenidos a partir de una base de datos. El objeto DataSet contiene un conjunto de tablas, relaciones y restricciones. En este ejemplo se muestra la forma de utilizar el método Add en el objeto DataTable para agregar datos nuevos a un objeto DataSet. El método Add utiliza una matriz de las columnas de datos esperadas o un objeto DataRow.

// Create a new Connection and SqlDataAdapter

SqlConnection

myConnection

=

new

SqlConnection("server=(local)\\VSdotNET;Trusted_Connection=yes;database=northwind"); SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter("Select * from Customers", myConnection); DataSet myDataSet = new DataSet(); DataRow myDataRow;

// Create command builder. This line automatically generates the update commands for you, so you don't // have to provide or create your own. SqlCommandBuilder mySqlCommandBuilder = new SqlCommandBuilder(mySqlDataAdapter);

// Set the MissingSchemaAction property to AddWithKey because Fill will not cause primary // key & unique key information to be retrieved unless AddWithKey is specified. mySqlDataAdapter.MissingSchemaAction = MissingSchemaAction.AddWithKey;

mySqlDataAdapter.Fill(myDataSet, "Customers");

myDataRow = myDataSet.Tables["Customers"].NewRow(); myDataRow["CustomerId"] = "NewID"; myDataRow["ContactName"] = "New Name"; myDataRow["CompanyName"] = "New Company Name";

myDataSet.Tables["Customers"].Rows.Add(myDataRow);

Hay que tener en cuenta que el objeto DataTable debe devolver un objeto DataRow a través del método NewRow. El método devuelve un objeto DataRow con el esquema apropiado del objeto DataTable. El nuevo objeto DataRow será independiente de la tabla hasta que se agregue a la colección RowsCollection.

Para modificar datos de un objeto DataRow hay que tener acceso al objeto DataRow. Se puede utilizar el índice de la fila en la colección RowsCollection mediante la propiedad Rows:

myDataSet.Tables["Customers"].Rows[0]["ContactName"]="Peach";

También se puede tener acceso a una fila específica a través del valor de la clave principal:

DataRow myDataRow1 = myDataSet.Tables["Customers"].Rows.Find("ALFKI");

myDataRow1["ContactName"]="Peach";

donde "ALFKI" corresponde al valor de la clave principal "CustomerID" de la tabla "Customers". Cuando se utiliza el objeto SqlDataAdapter, la clave se establece a partir de la base de datos. También se puede establecer la clave si no se utiliza la base de datos mediante la propiedad PrimaryKey.

Utilice el método Delete para quitar el objeto Row. Hay que tener en cuenta que se produce una eliminación lógica en el objeto DataSet, que sólo será una eliminación definitiva cuando se actualice la base de datos a partir del objeto DataSet. De forma similar, se puede utilizar RejectChanges en el objeto DataSet, en cuyo caso se restaurará el objeto Row.

myDataSet.Tables["Customers"].Rows[0].Delete();

Se mantendrán en la fila los valores originales y los valores nuevos. El evento RowChanging permite tener acceso a valores nuevos y valores originales con el fin de decidir si se desea seguir adelante con la modificación. Como se mantienen valores nuevos y valores originales, se pueden establecer escenarios como el bloqueo optimista o cambios de clave.

Antes de devolver los datos enviados a la base de datos, es necesario configurar los comandos InsertCommand, UpdateCommand y DeleteCommand para cotejar los cambios en la base de datos. En escenarios limitados se puede utilizar la clase SqlCommandBuilder para generar automáticamente estos comandos de la forma mostrada en el siguiente ejemplo:

SqlCommandBuilder mySqlCommandBuilder =new SqlCommandBuilder(mySqlDataAdapter);

Para enviar los datos desde el objeto DataSet a la base de datos, hay que utilizar el método Update en el objeto SqlDataAdapter.

mySqlDataAdapter.Update(myDataSet, "Customers");

En el siguiente ejemplo

se muestra

la

forma de obtener datos

de

una base

de datos

mediante un objeto

SqlDataAdapter, modificar los datos dentro del objeto DataSet y después devolverlos a la base de datos a través del

objeto SqlDataAdapter.

namespace HowTo.Samples.ADONET {

using System; using System.Data; using System.Data.SqlClient;

public class updatingdata {

public static void Main() {

updatingdata myupdatingdata = new updatingdata(); myupdatingdata.Run();

}

public void Run() {

SqlConnection

myConnection

=

new

SqlConnection("server=(local)\\NetSDK;Trusted_Connection=yes;database=northwind"); SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter("select * from customers", myConnection); SqlDataAdapter mySqlDataAdapter1 = new SqlDataAdapter("select * from orders", myConnection);

// Restore database to it's original condition so sample will work correctly.

Cleanup();

try

{

DataSet myDataSet = new DataSet(); DataRow myDataRow;

// Create command builder. This line automatically generates the update commands for you, so you don't // have to provide or create your own. SqlCommandBuilder mySqlCommandBuilder = new SqlCommandBuilder(mySqlDataAdapter);

// Set the MissingSchemaAction property to AddWithKey because Fill will not cause primary // key & unique key information to be retrieved unless AddWithKey is specified.

mySqlDataAdapter.MissingSchemaAction = MissingSchemaAction.AddWithKey; mySqlDataAdapter1.MissingSchemaAction = MissingSchemaAction.AddWithKey;

mySqlDataAdapter.Fill(myDataSet,"Customers"); Console.WriteLine("Se han cargado los datos de la tabla Customers en el conjunto de datos.");

mySqlDataAdapter1.Fill(myDataSet,"Orders");

Console.WriteLine("Datos de la tabla Orders cargados en el conjunto de datos.");

// ADD RELATION

myDataSet.Relations.Add("CustOrders",myDataSet.Tables["Customers"].Columns["CustomerId"],myDataSet.Tables

["Orders"].Columns["CustomerId"]);

// EDIT

myDataSet.Tables["Customers"].Rows[0]["ContactName"]="Melocotn";

// ADD

myDataRow = myDataSet.Tables["Customers"].NewRow(); myDataRow["CustomerId"] ="NewID"; myDataRow["ContactName"] = "Nuevo nombre"; myDataRow["CompanyName"] = "Nuevo nombre de compaa"; myDataSet.Tables["Customers"].Rows.Add(myDataRow); Console.WriteLine("Se insert una fila nueva en la tabla Customers.");

// Update Database with SqlDataAdapter mySqlDataAdapter.Update(myDataSet, "Customers"); Console.WriteLine("Actualizacin enviada a la base de datos.");

Console.WriteLine("El procesamiento del conjunto de datos se complet correctamente.");

}

catch(Exception e)

{

Console.WriteLine(e.ToString());

}

}

public void Cleanup() {

SqlConnection

myConnection

=

new

SqlConnection("server=(local)\\NetSDK;Trusted_Connection=yes;database=northwind");

try

{

// Restore database to it's original condition so sample will work correctly.

myConnection.Open(); SqlCommand CleanupCommand = new SqlCommand("DELETE FROM Customers WHERE CustomerId = 'NewID'", myConnection); CleanupCommand.ExecuteNonQuery();

} catch (Exception e) {

Console.WriteLine(e.ToString());

}

finally

{

myConnection.Close();

}

}

}

}

Utilizar transacciones de base de datos

Las transacciones de base de datos se utilizan para controlar la confirmación de datos en bases de datos. Por ejemplo, en los procedimientos estándar de cuentas, es necesario retirar fondos de una cuenta e ingresarlos a su vez en otra. Como los equipos pueden sufrir interrupciones de funcionamiento (por interrupción del suministro eléctrico, problemas de red, etc.), podría darse el caso de que se actualizara o agregara un registro determinado, pero no el otro. Para evitar estas situaciones, se utilizan transacciones. Las transacciones de ADO.NET se controlan de la misma manera que en ADO, en el nivel de la base de datos y, para ello, la base de datos debe ser compatible con el uso de transacciones.

Existen tres comandos básicos para las transacciones: BeginTransaction, Commit y Rollback. BeginTransaction marca el principio de una transacción. Todo lo que ocurra entre la ejecución del comando BeginTransaction y la del siguiente comando (ya sea Rollback o Commit) se considerará como parte de la transacción. El siguiente fragmento de código ilustra el uso de las transacciones.

SqlConnection myConnection = new SqlConnection("server= (local)\\VSdotNET;Trusted_Connection=yes;database=northwind"); SqlCommand myCommand = new SqlCommand(); SqlTransaction myTrans;

// Open the connection.

myConnection.Open();

// Assign the connection property. myCommand.Connection = myConnection;

// Begin the transaction. myTrans = myConnection.BeginTransaction();

// Assign transaction object for a pending local transaction myCommand.Transaction = myTrans;

try

{

// Restore database to near its original condition so sample will work correctly. myCommand.CommandText = "DELETE FROM Region WHERE (RegionID = 100) OR (RegionID = 101)"; myCommand.ExecuteNonQuery();

// Insert the first record. myCommand.CommandText = "Insert into Region (RegionID, RegionDescription) VALUES (100, 'MidWestern')"; myCommand.ExecuteNonQuery();

// Insert the second record. myCommand.CommandText = "Insert into Region (RegionID, RegionDescription) VALUES (101, 'MidEastern')"; myCommand.ExecuteNonQuery();

myTrans.Commit(); Console.WriteLine("Both Records are written to the database!");

}

catch(Exception e)

{

myTrans.Rollback(); Console.WriteLine(e.ToString()); Console.WriteLine("Neither record is written to the database!");

}

finally

{

myConnection.Close();

}

En el ejemplo se muestra que si no se realiza correctamente la inserción, se deshacen las dos operaciones para volver al estado original. Si se realizan las dos correctamente, se confirma la transacción.

Al igual que ocurre en la tecnología ADO tradicional, se pueden controlar transacciones a través del objeto connection. De hecho, el uso de una conexión OleDbConnection se basa en el mismo modelo de transacciones OLE DB subyacente. Por tanto, si era posible confirmar transacciones en la base de datos con la tecnología ADO tradicional, también se podrán confirmar con ADO.NET. En el siguiente fragmento de código se ilustra el uso de SqlConnection y SqlCommand para insertar dos registros en la tabla "Region". Si uno de los dos no se ejecuta correctamente, se deshacen los cambios.

El objeto DataSet también tiene un modelo de confirmaciones (AcceptChanges, RejectChanges), pero no afecta a la base de datos. El modelo de confirmación se utiliza para el almacenamiento de datos en caché, únicamente en el caso del objeto DataSet. Para enviar los datos desde el objeto DataSet a la base de datos, hay que utilizar el método Update del objeto SqlDataAdapter

Controlar errores

Además de proporcionar las funciones Try/Catch y las excepciones, la nueva arquitectura de datos de ADO.NET permite agregar mensajes de error a cada fila de datos de un objeto DataSet. Los objetos SqlDataAdapter asocian mensajes de error a las filas si no se pueden realizar correctamente actualizaciones u otras acciones. Además, permiten filtrar por aquellas filas que producen error para presentarlas al usuario o pasarlas a funciones de control de errores.

Los errores se conservan con el objeto DataSet aún cuando se transfieran mediante XML o servicios Web de XML. Es posible utilizar la propiedad RowError para establecer el mensaje de error de un objeto DataRow perteneciente a un objeto DataSet.

myDataSet.Tables["Customers"].Rows[0].RowError = "An error was added"; myDataSet.Tables["Customers"].Rows[1].RowError = "This is another error message";

Ahora es posible examinar el error en un objeto DataTable con el método GetErrors(). También se puede hacer una prueba de detección de errores con HasErrors.

if ( myDataSet.Tables["Customers"].HasErrors ) {

DataRow[] ErrDataRows = myDataSet.Tables["Customers"].GetErrors(); "

 

"

"

Console.WriteLine("DataTable ErrDataRows.Length.ToString() + " Error(s)!");

+

for (int i = 0; i <= ErrDataRows.Length -1; i++) {

myDataSet.Tables["Customers"].TableName

+

has

+

Console.WriteLine("Row Error for row " + ErrDataRows[i]["CustomerID"].ToString() + " -- ErrDataRows[i].RowError); }

Error Msg="

+

}

else

{

Console.WriteLine("================="); Console.WriteLine("DataTable " + myDataSet.Tables["Customers"].TableName + " Has no errors");

}

En el ejemplo siguiente se carga un objeto DataSet, se generan algunos errores y después de muestran los errores de las filas.

namespace HowTo.Samples.ADONET {

using System; using System.Data; using System.Data.SqlClient;

public class handleerrors {

public static void Main() {

handleerrors myhandleerrors = new handleerrors(); myhandleerrors.Run();

}

public void Run() {

// Create a new Connection and SqlDataAdapter

SqlConnection myConnection = new SqlConnection("server= (local)\\NetSDK;Trusted_Connection=yes;database=northwind"); SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter("select * from customers", myConnection);

// Create the new instance of the DataSet DataSet myDataSet = new DataSet();

try

{

// Load the customer table from the database into a table called Customers in the dataset

mySqlDataAdapter.Fill(myDataSet,"Customers");

// Create a new dataview instance on the Customers table that was just created DataView myDataView = new DataView(myDataSet.Tables["Customers"]);

// Sort the view based on the FirstName column

myDataView.Sort = "CustomerID";

myDataSet.Tables["Customers"].Rows[0].RowError = "Se ha agregado un error"; myDataSet.Tables["Customers"].Rows[1].RowError = "Otro mensaje de error";

if ( myDataSet.Tables["Customers"].HasErrors ) { DataRow[] ErrDataRows = myDataSet.Tables["Customers"].GetErrors(); Console.WriteLine("DataTable myDataSet.Tables["Customers"].TableName,ErrDataRows.Length.ToString());

{0}

for (int i = 0; i <= ErrDataRows.Length -1; i++)

tiene

{1}

error(es).",

{

Console.WriteLine("Error de fila en la fila {0} -- Mensaje de error={1}",ErrDataRows[i] ["CustomerID"].ToString(),ErrDataRows[i].RowError); }

}

else

{

Console.WriteLine("================="); Console.WriteLine("DataTable {0} no tiene errores", myDataSet.Tables["Customers"].TableName);

}

}

catch(Exception e)

{

Console.WriteLine(e.ToString());

}

}

}

}

Trabajar con datos relacionales

Un objeto DataSet puede contener tablas no relacionadas o tablas relacionadas. Se puede considerar que un objeto DataSet es un documento de datos. De hecho, los documentos de datos XML son así, con la diferencia de que se basan en un paradigma jerárquico. Como los datos se suelen almacenar en bases de datos relacionales, el objeto DataSet puede controlar relaciones jerárquicas y las relaciones de clave y clave externa. Las relaciones también pueden tener distintos tipos de obligatoriedad. De forma predeterminada, las eliminaciones y actualizaciones se aplican en cascada: si se elimina una fila de Customer, también se eliminarán las filas relacionadas de Orders; si se actualiza la clave de una fila Customer, también se actualizarán los valores de clave externa asociados de la tabla Orders.

Un objeto DataSet contiene una colección Relations. Es posible agregar una relación a esta colección mediante la columna (o las columnas, si la clave es de varias columnas) de las tablas relacionadas. En el siguiente ejemplo se crea una relación entre Customers y Orders y se asigna a la relación el nombre CustOrders.

myDataSet.Relations.Add("CustOrders",myDataSet.Tables["Customers"].Columns["CustomerID"],

myDataSet.Tables["Orders"].Columns["CustomerID"]);

Después de agregar una relación entre la clave CustomerID de la tabla Customers y la clave externa CustomerID de la tabla Orders del objeto DataSet, se pueden recorrer los datos.

foreach (DataRow myDataRow1 in myDataSet.Tables["Customers"].Rows) { Console.WriteLine("Customer: " + myDataRow1["ContactName"].ToString());

// Iterate over orders data. foreach (DataRow myDataRow2 in myDataRow1.GetChildRows(myDataSet.Relations["CustOrders"])) {

Console.WriteLine("Order #" + myDataRow2["OrderID"].ToString());

}

Console.WriteLine();

}

En el siguiente ejemplo se recorren los datos y se muestran de forma jerárquica en una página Web.

namespace HowTo.Samples.ADONET {

using System; using System.Data; using System.Data.SqlClient;

public class relationaldata {

public static void Main() {

relationaldata myrelationaldata = new relationaldata(); myrelationaldata.Run();

}

public void Run() {

DataSet myDataSet = new DataSet();

SqlConnection myConnection = new SqlConnection("server= (local)\\NetSDK;Trusted_Connection=yes;database=northwind"); SqlDataAdapter mySqlDataAdapter1 = new SqlDataAdapter("select * from customers", myConnection); SqlDataAdapter mySqlDataAdapter2 = new SqlDataAdapter("select * from orders", myConnection);

try

{

mySqlDataAdapter1.Fill(myDataSet,"Customers");

mySqlDataAdapter2.Fill(myDataSet,"Orders");

// ADD RELATION

myDataSet.Relations.Add("CustOrders",myDataSet.Tables["Customers"].Columns["CustomerId"],myDataSet.Tables

["Orders"].Columns["CustomerId"]);

// Iterate over data of Customers and their orders foreach (DataRow myDataRow1 in myDataSet.Tables["Customers"].Rows) {

Console.WriteLine("Cliente: " + myDataRow1["ContactName"].ToString());

// Iterate over orders data. foreach (DataRow myDataRow2 in myDataRow1.GetChildRows(myDataSet.Relations["CustOrders"])) {

Console.WriteLine("Nmero de pedido " + myDataRow2["OrderId"].ToString());

}

Console.WriteLine();

}

}

catch(Exception e)

{

Console.WriteLine(e.ToString());

}

}

}

}

Trabajar con datos con tipo

La tecnología ADO tradicional proporciona acceso en tiempo de ejecución a valores de un conjunto de registros mediante variables con uso no estricto de tipos. El código de ADO.NET permite obtener acceso a los datos contenidos en el objeto DataSet mediante una metáfora con "tipos estrictos". Esto significa que se puede obtener acceso a tablas y columnas que forman parte del objeto DataSet con nombres descriptivos y variables con tipos estrictos. Por tanto, en lugar de escribir el código ADO tradicional que se muestra a continuación:

AdoRecordset.Find("CustomerID = 'ALFKI'"); AdoRecordSet.Fields["FirstName"].Value = "Bob";

con un objeto DataSet con tipos estrictos, se puede escribir el siguiente código ADO.NET:

CustomerDataSet.Customers["ALFKI"].CustomerName = "Bob";

Además, el conjunto de datos con tipos estrictos proporciona acceso a valores de fila como los valores correctos con el tipo estricto. En ADO se utilizan tipos Variant al asignar y recuperar datos. Si el valor asignado es del tipo incorrecto, ADO produciría un error en tiempo de ejecución. En ADO.NET, si el valor es de tipo entero y se intenta asignar una cadena, se producirá un error en tiempo de compilación.

Dado un esquema XML compatible con el estándar XSD, es posible generar un conjunto de datos con tipos estrictos mediante la herramienta XSD.exe proporcionada con el SDK de .NET Framework. La sintaxis para generar un conjunto de datos mediante esta herramienta es:

xsd.exe /d /l:CS {XSDSchemaFileName.xsd}

La directiva /d dirige la herramienta para generar un conjunto de datos; la directiva /l: especifica el lenguaje que se debe utilizar ("C#" o "VB").

En el siguiente ejemplo se utiliza un conjunto de datos con tipos estrictos denominado "myCustDS" para cargar una lista de clientes desde la base de datos Northwind. Cuando se hayan cargado los datos mediante el método Fill, el código recorrerá cada cliente de la tabla "Customers" mediante el objeto myDataRow con tipo. Hay que tener en cuenta que se utiliza acceso directo al campo "CustomerID" en lugar de la colección Fields.

Antes de ejecutar el siguiente ejemplo hay que ejecutar "nmake /a" en el directorio " ventana de consola para crear el archivo TypedData.dll.

..

\typeddata\DllSrc" desde una

namespace HowTo.Samples.ADONET {

using System; using System.Data; using System.Data.SqlClient; using customerDataSet;

public class typeddata {

public static void Main() {

typeddata mytypeddata = new typeddata(); mytypeddata.Run();

}

public void Run() {

SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter("Select * from customers", "server=(local)\\NetSDK;Trusted_Connection=yes;database=northwind");

try

{

custDS myCustDS = new custDS(); mySqlDataAdapter.Fill(myCustDS, "Customers");

Console.WriteLine("CustomerID\n");

foreach (custDS.customersRow myDataRow in myCustDS.customers) Console.WriteLine(myDataRow.CustomerID);

}

catch(Exception e)

{

Console.Write(e.ToString());

}

}

}

}

Filtrar datos

Existen muchas formas de filtrar datos. Una forma consiste en filtrar datos en el nivel de comandos de base de datos, mediante una cláusula en la consulta. Otra forma consiste en filtrar los datos cuando ya están en el objeto DataSet. En este tema se explica el filtrado en el objeto DataSet.

Cuando los datos ya están en un objeto DataSet, se pueden utilizar métodos del objeto DataSet para manipular subconjuntos de datos.

Filtrar con el método Select

Considere un objeto DataSet con las tablas Customers y Orders. Para filtrar datos de la tabla Customers que tengan Kelly como valor de FirstName, se puede utilizar el método Select, que devuelve una matriz de filas.

myRowArray = dsCustomers.Select("ContactName like 'Kelly%'");

Observe que las instrucciones son del tipo ANSI-SQL. Dichas instrucciones son básicamente un subconjunto de ANSI- SQL, con esta diferencia: dado que DataSet puede contener múltiples tablas relacionadas, el filtro también puede aplicarse sobre estas tablas relacionadas. En el ejemplo siguiente, utilice la palabra clave Child para filtrar las solicitudes y los clientes.

myRowArray = dsCustomers.Select("ContactName like 'Kelly%' AND [child].OrderDate = '7/26/68'");

Estas funciones devuelven una matriz de filas. Puede iterar en la matriz utilizando la instrucción foreach.

Filtrar y ordenar con objetos DataView

Con el objeto DataView, puede colocar múltiples filtros en DataSet, configurar el enlace de datos a ellos, etc. Un objeto DataView se puede filtrar utilizando las mismas reglas de lenguaje que en Select pero, en este caso, el filtro es dinámico. Por tanto, si se agrega una fila a los datos y cumple los criterios del filtro, aparecerá en la vista. Las vistas se pueden ordenar y filtrar. Los filtros pueden ser de datos y de versión (actual, nueva, modificada, eliminada). Para configurar un objeto DataView, constrúyalo de forma que controle a DataTable dentro de DataSet:

DataView myDataView = new DataView(myDataSet.Tables["Customers"]);

Es posible configurar un filtro con la propiedad RowFilter mediante el mismo lenguaje de filtro de consultas que el del ejemplo del método Select anterior. Para configurar una ordenación, utilice una lista delimitada por comas de ordenaciones de columnas, seguida de ASC (configuración predeterminada y opcional) o DESC, con el fin de indicar orden ascendente o descendente.

// Sort the view based on the FirstName column myDataView.Sort = "CustomerID";

// Filter the dataview to only show customers with the CustomerID of ALFKI myDataView.RowFilter = "CustomerID='ALFKI'";

También puede filtrar en función del estado de las filas (eliminadas, nuevas, etc.). A continuación se muestra un ejemplo de filtro en filas eliminadas:

myDataView.RowStateFilter = DataViewRowState.Deleted;

En el siguiente ejemplo se muestra el llenado de un objeto DataSet y el posterior filtrado con un objeto DataView. Hay que tener en cuenta que es fácil enlazar un objeto DataView a datos. Vea los temas de enlace de datos en formularios de Windows o ASP.NET.

namespace HowTo.Samples.ADONET {

using System; using System.Data; using System.Data.SqlClient;

public class filterdatatable {

public static void Main() {

filterdatatable myfilterdatatable = new filterdatatable(); myfilterdatatable.Run();

}

public void Run() {

// Create a new Connection and SqlDataAdapter

SqlConnection

myConnection

=

new

SqlConnection("server=(local)\\NetSDK;Trusted_Connection=yes;database=northwind"); SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter("select * from customers", myConnection);

try

{

// Create the new instance of the DataSet DataSet myDataSet = new DataSet();

// Load the customer table from the database into a table called Customers in the dataset

mySqlDataAdapter.Fill(myDataSet,"Customers");

// Create a new dataview instance on the Customers table that was just created DataView myDataView = new DataView(myDataSet.Tables["Customers"]);

// Sort the view based on the FirstName column

myDataView.Sort = "CustomerID";

// Filter the dataview to only show customers with the CustomerID of ALFKI

myDataView.RowFilter = "CustomerID='ALFKI'";

for (int i = 0; i < myDataView.Count; i++) {

Console.Write(myDataView[i]["CustomerID"].ToString() + " - " + myDataView[i]["CompanyName"].ToString());

}

}

catch(Exception e)

{

Console.WriteLine(e.ToString());

}

}

}

}