Llenar un DataSet desde un DataAdapter

DataSet de ADO.NET es una representación residente en memoria de datos que proporciona un modelo de programación relacional coherente e independiente del origen de los datos. DataSet representa un conjunto completo de datos que incluye tablas y restricciones, así como relaciones entre ellas. Dado que DataSet es independiente del origen de datos, puede incluir datos locales de la aplicación, así como datos de otros muchos orígenes. La interacción con los orígenes de datos existentes se controla mediante el DataAdapter.

Cada proveedor de datos de .NET Framework incluido en .NET Framework tiene un objeto DataAdapter: el proveedor de datos de .NET Framework para OLE DB incluye un objeto OleDbDataAdapter; el proveedor de datos de .NET Framework para SQL Server incluye un objeto SqlDataAdapter y el proveedor de datos de .NET Framework para ODBC incluye un objeto OdbcDataAdapter. Para recuperar datos de un origen de datos y llenar las tablas de un DataSet, se utiliza un DataAdapter. El DataAdapter sirve también para reflejar en el origen de datos los cambios efectuados en el DataSet. El DataAdapter utiliza el objeto Connection del proveedor de datos de .NET Framework para establecer una conexión con un origen de datos, así como objetos Command para recuperar datos de un origen de datos y reflejar en él los cambios que se efectúen.

La propiedad SelectCommand del DataAdapter es un objeto Command que recupera datos del origen de datos. Las propiedades InsertCommand, UpdateCommand y DeleteCommand de DataAdapter son objetos Command que permiten administrar las actualizaciones de los datos en el origen de datos para reflejar las modificaciones efectuadas en el DataSet. Estas propiedades se describen con más detalle en Actualizar la base de datos con un DataAdapter y el DataSet.

El método Fill del DataAdapter se usa para llenar un DataSet con los resultados de la propiedad SelectCommand del DataAdapter. El método Fill acepta como argumentos un DataSet que se debe llenar y un objeto DataTable, o su nombre, que se debe llenar con las filas que devuelve SelectCommand.

El método Fill utiliza el objeto DataReader de forma implícita para devolver los nombres y tipos de columna usados para crear las tablas de DataSet, así como los datos para llenar las filas de las tablas de DataSet. Las tablas y columnas sólo se crean cuando no existen, en caso contrario, Fill usa el esquema existente de DataSet. Los tipos de columna se crean como tipos de .NET Framework conforme se indica en las tablas que aparecen en Asignar los tipos de datos del proveedor de datos de .NET para los tipos de datos de .NET Framework. No se crean claves principales a menos que existan en el origen de datos y se haya dado el valor MissingSchemaAction.AddWithKey a DataAdapter.MissingSchemaAction. Si el método Fill determina que una tabla tiene clave principal, sobrescribe los datos del DataSet con los del origen de datos en aquellas filas en las que los valores de la columna de clave principal coincidan con los de la fila que devuelve el origen de datos. Si no se detecta ninguna clave principal, los datos se agregan a continuación de los ya existentes en las tablas del DataSet. Fill utiliza cualquier TableMappings que pueda existir al llenar el DataSet (vea Configurar las asignaciones de DataTable y DataColumn).

Nota   Si SelectCommand devuelve los resultados de una combinación externa (OUTER JOIN), DataAdapter no establecerá un valor PrimaryKey para la tabla DataTable resultante. El usuario tendrá que definir PrimaryKey para asegurarse de que las filas duplicadas se resuelven correctamente. Para obtener más información, vea Definir una clave principal para una tabla.

En el ejemplo de código siguiente se crea una instancia de un DataAdapter que utiliza un objeto Connection a la base de datos Northwind de Microsoft SQL Server y llena una DataTable en un DataSet con la lista de clientes. La instrucción SQL y los argumentos de Connection que se pasan al constructor DataAdapter se utilizan para crear la propiedad SelectCommand del DataAdapter.

SqlClient

Dim nwindConn As SqlConnection = New SqlConnection("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind")

Dim selectCMD As SqlCommand = New SqlCommand("SELECT CustomerID, CompanyName FROM Customers", nwindConn)
selectCMD.CommandTimeout = 30

Dim custDA As SqlDataAdapter = New SqlDataAdapter
custDA.SelectCommand = selectCMD

nwindConn.Open()

Dim custDS As DataSet = New DataSet
custDA.Fill(custDS, "Customers")

nwindConn.Close()
[C#]
SqlConnection nwindConn = new SqlConnection("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind");

SqlCommand selectCMD = new SqlCommand("SELECT CustomerID, CompanyName FROM Customers", nwindConn);
selectCMD.CommandTimeout = 30;

SqlDataAdapter custDA = new SqlDataAdapter();
custDA.SelectCommand = selectCMD;

nwindConn.Open();

DataSet custDS = new DataSet();
custDA.Fill(custDS, "Customers");

nwindConn.Close();

OleDb

Dim nwindConn As OleDbConnection = New OleDbConnection("Provider=SQLOLEDB;Data Source=localhost;" & _
                                                       "Integrated Security=SSPI;Initial Catalog=northwind")

Dim selectCMD As OleDbCommand = New OleDbCommand("SELECT CustomerID, CompanyName FROM Customers", nwindConn)
selectCMD.CommandTimeout = 30

Dim custDA As OleDbDataAdapter = New OleDbDataAdapter
custDA.SelectCommand = selectCMD

Dim custDS As DataSet = New DataSet
custDA.Fill(custDS, "Customers")
[C#]
OleDbConnection nwindConn = new OleDbConnection("Provider=SQLOLEDB;Data Source=localhost;" +
                                                "Integrated Security=SSPI;Initial Catalog=northwind");

OleDbCommand selectCMD = new OleDbCommand("SELECT CustomerID, CompanyName FROM Customers", nwindConn);
selectCMD.CommandTimeout = 30;

OleDbDataAdapter custDA = new OleDbDataAdapter();
custDA.SelectCommand = selectCMD;

DataSet custDS = new DataSet();
custDA.Fill(custDS, "Customers");

Odbc

Dim nwindConn As OdbcConnection = New OdbcConnection("Driver={SQL Server};Server=localhost;" & _
                                                     "Trusted_Connection=yes;Database=northwind")

Dim selectCMD As OdbcCommand = New OdbcCommand("SELECT CustomerID, CompanyName FROM Customers", nwindConn)
selectCMD.CommandTimeout = 30

Dim custDA As OdbcDataAdapter = New OdbcDataAdapter
custDA.SelectCommand = selectCMD

nwindConn.Open()

Dim custDS As DataSet = New DataSet
custDA.Fill(custDS, "Customers")

nwindConn.Close()
[C#]
OdbcConnection nwindConn = new OdbcConnection("Driver={SQL Server};Server=localhost;" +
                                              "Trusted_Connection=yes;Database=northwind");

OdbcCommand selectCMD = new OdbcCommand("SELECT CustomerID, CompanyName FROM Customers", nwindConn);
selectCMD.CommandTimeout = 30;

OdbcDataAdapter custDA = new OdbcDataAdapter();
custDA.SelectCommand = selectCMD;

nwindConn.Open();

DataSet custDS = new DataSet();
custDA.Fill(custDS, "Customers");

nwindConn.Close();

Tenga en cuenta que el código no abre ni cierra el objeto Connection de forma explícita. El método Fill abre de forma implícita el objeto Connection que utiliza el DataAdapter cuando determina que esa conexión no está abierta. Cuando el método Fill ha abierto la conexión, el mismo método la cierra cuando termina de usarla. Este hecho simplifica el código cuando se trata de una operación única, como un método Fill o Update. Sin embargo, en el caso de que se estén realizando múltiples operaciones que necesiten tener abierta una conexión, puede mejorar el rendimiento de la aplicación si llama explícitamente al método Open de Connection, realiza a continuación las operaciones en el origen de datos y, finalmente, llama al método Close de Connection. Debe intentar mantener abiertas las conexiones con el origen de datos el tiempo mínimo necesario, ya que así el recurso queda libre para que lo usen otras aplicaciones cliente.

Múltiples conjuntos de resultados

Si el DataAdapter encuentra múltiples conjuntos de resultados, crea varias tablas en el DataSet. Las tablas reciben de forma predeterminada el nombre secuencial TableN, comenzando por "Table" que representa Table0. Si se pasa un nombre de tabla como argumento al método Fill, las tablas irán recibiendo de forma predeterminada un nombre secuencial NombreTablaN, comenzando con "NombreTabla" que corresponde a NombreTabla0.

Llenar un DataSet desde múltiples DataAdapter

Con un DataSet se puede usar un número ilimitado de DataAdapters. Cada DataAdapter se puede utilizar para llenar uno o varios objetos DataTable y para reflejar en el origen de datos correspondiente las actualizaciones que sean necesarias. Se pueden agregar de forma local objetos DataRelation y Constraint al DataSet, de manera que se pueden relacionar datos procedentes de múltiples orígenes distintos. Por ejemplo, un DataSet puede contener datos de una base de datos de Microsoft SQL Server, una base de datos de IBM DB2 expuesta mediante OLE DB y de un origen de datos que genera secuencias XML. Para ocuparse de la comunicación con cada origen de datos se pueden usar uno o varios objetos DataAdapter.

En el ejemplo de código siguiente se llena una lista de clientes a partir de la base de datos Northwind de Microsoft SQL Server 2000 y una lista de pedidos a partir de la base de datos Northwind almacenada en Microsoft® Access 2000. Las tablas de datos llenas se relacionan entre sí mediante DataRelation, con lo que se puede mostrar una lista de clientes con los pedidos que ha realizado cada uno. Para obtener más información acerca de los objetos DataRelation, vea Agregar una relación entre tablas y Explorar una relación entre tablas.

Dim custConn As SqlConnection= New SqlConnection("Data Source=localhost;Integrated Security=SSPI;" & _
                                                 "Initial Catalog=northwind;")
Dim custDA As SqlDataAdapter = New SqlDataAdapter("SELECT * FROM Customers", custConn)

Dim orderConn As OleDbConnection = New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;" & _
                                                       "Data Source=c:\Program Files\Microsoft Office\" & _
                                                       "Office\Samples\northwind.mdb;")
Dim orderDA As OleDbDataAdapter = New OleDbDataAdapter("SELECT * FROM Orders", orderConn)

custConn.Open()
orderConn.Open()

Dim custDS As DataSet = New DataSet()

custDA.Fill(custDS, "Customers")
orderDA.Fill(custDS, "Orders")

custConn.Close()
orderConn.Close()

Dim custOrderRel As DataRelation = custDS.Relations.Add("CustOrders", _
                                     custDS.Tables("Customers").Columns("CustomerID"), _ 
                                     custDS.Tables("Orders").Columns("CustomerID"))

Dim pRow, cRow As DataRow

For Each pRow In custDS.Tables("Customers").Rows
  Console.WriteLine(pRow("CustomerID").ToString())

  For Each cRow In pRow.GetChildRows(custOrderRel)
    Console.WriteLine(vbTab & cRow("OrderID").ToString())
  Next
Next 
[C#]
SqlConnection custConn = new SqlConnection("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind;");
SqlDataAdapter custDA = new SqlDataAdapter("SELECT * FROM Customers", custConn);

OleDbConnection orderConn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;" +
                                                "Data Source=c:\\Program Files\\Microsoft Office\\Office\\Samples\\northwind.mdb;");
OleDbDataAdapter orderDA = new OleDbDataAdapter("SELECT * FROM Orders", orderConn);

custConn.Open();
orderConn.Open();

DataSet custDS = new DataSet();

custDA.Fill(custDS, "Customers");
orderDA.Fill(custDS, "Orders");

custConn.Close();
orderConn.Close();

DataRelation custOrderRel = custDS.Relations.Add("CustOrders",
                              custDS.Tables["Customers"].Columns["CustomerID"],    
                              custDS.Tables["Orders"].Columns["CustomerID"]);

foreach (DataRow pRow in custDS.Tables["Customers"].Rows)
{
  Console.WriteLine(pRow["CustomerID"]);
   foreach (DataRow cRow in pRow.GetChildRows(custOrderRel))
    Console.WriteLine("\t" + cRow["OrderID"]);
}

Tipo decimal de SQL Server

DataSet almacena los datos utilizando tipos de datos de .NET Framework. En la mayor parte de las aplicaciones, estos tipos proporcionan una representación adecuada de la información del origen de datos. Sin embargo, esa representación puede ocasionar problemas cuando el tipo de datos del origen de datos es decimal de SQL Server. El tipo de datos decimal de .NET Framework permite tener un máximo de 28 dígitos significativos, mientras que el tipo decimal de SQL Server permite 38 dígitos significativos. Cuando SqlDataAdapter determina, durante una operación Fill, que la precisión de un campo decimal de SQL Server es mayor de 28 caracteres, la fila actual no se agrega a DataTable. En cambio, se produce un evento FillError que le permite decidir si se debe producir o no una pérdida de precisión y tomar las medidas adecuadas. Para obtener más información acerca del evento FillError, vea Trabajar con eventos DataAdapter. Para obtener el valor decimal de SQL Server también puede usar un objeto SqlDataReader y llamar al método GetSqlDecimal.

Capítulos de OLE DB

Se pueden usar conjuntos jerárquicos de filas, o capítulos (tipo DBTYPE_HCHAPTER de OLE DB y tipo adChapter de ADO), para llenar un DataSet. Cuando, durante una operación Fill, DataAdapter encuentra una columna que tiene un capítulo, se crea una DataTable para ella y la tabla se llena con las columnas y filas del capítulo. La tabla creada para la columna con capítulo recibe como nombre el de la tabla primaria y el de la columna con capítulo. El nombre tiene el siguiente formato "NombreTablaPrimariaNombreColumnaCapítulo". Si ya existe en el DataSet una tabla que tenga el nombre de la columna con capítulo, esa tabla se llena con los datos del capítulo. Si ninguna de las columnas de la tabla existente coincide con una de las columnas del capítulo, se agrega una nueva columna a la tabla.

Antes de que se llenen las tablas del DataSet con los datos de las columnas con capítulos, se crea una relación entre las tablas principal y secundaria del conjunto jerárquico de filas; para ello, se agrega una columna de tipo entero a las tablas primaria y secundaria, se establece la propiedad de incremento automático en la columna de la tabla primaria y se crea una DataRelation entre las columnas agregadas de ambas tablas. Para dar nombre a la relación, se utilizan los nombres de la tabla primaria y de la columna con capítulo con el formato "NombreTablaPrimariaNombreColumnaCapítulo".

Tenga en cuenta que la columna relacionada sólo existe en el DataSet. Otras operaciones de llenado que se realicen a continuación desde el origen de datos irán agregando nuevas filas a las tablas en lugar de introducir cambios en las filas ya existentes.

Tenga en cuenta además que, si se usa una sobrecarga de DataAdapter.Fill que acepte una tabla DataTable, ésa será la única tabla que se llene. En este caso también se agrega a la tabla una columna de tipo entero y con incremento automático, aunque no se crea ni rellena ninguna tabla secundaria, ni se crea ninguna relación.

En el ejemplo siguiente se utiliza el proveedor MSDataShape para generar un capítulo con la columna de pedidos realizados por cada uno de los clientes de una lista. A continuación se llena con esos datos un DataSet.

Dim nwindConn As OleDbConnection = New OleDbConnection("Provider=MSDataShape;Data Provider=SQLOLEDB;" & _
                                         "Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind")

Dim custDA As OleDbDataAdapter = New OleDbDataAdapter("SHAPE {SELECT CustomerID, CompanyName FROM Customers} " & _
                                      "  APPEND ({SELECT CustomerID, OrderID FROM Orders} AS Orders " & _
                                      "  RELATE CustomerID TO CustomerID)", nwindConn)

Dim custDS As DataSet = New DataSet()

custDA.Fill(custDS, "Customers")
[C#]
OleDbConnection nwindConn = new OleDbConnection("Provider=MSDataShape;Data Provider=SQLOLEDB;" +
                                  "Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind");

OleDbDataAdapter custDA = new OleDbDataAdapter("SHAPE {SELECT CustomerID, CompanyName FROM Customers} " +
                                      "  APPEND ({SELECT CustomerID, OrderID FROM Orders} AS Orders " +
                                      "  RELATE CustomerID TO CustomerID)", nwindConn);

DataSet custDS = new DataSet();
custDA.Fill(custDS, "Customers");

Cuando la operación Fill se completa, DataSet contiene dos tablas: Customers y CustomersOrders, siendo CustomersOrders la tabla que representa a la columna con capítulo. Se agrega una columna adicional con el nombre Orders a la tabla Customers y una columna adicional con el nombre CustomersOrders a la tabla CustomersOrders. La columna Orders de la tabla Customers se establece con incremento automático. Se crea también una relación DataRelation, CustomersOrders, utilizando las columnas que se han agregado a las tablas donde la tabla Customers es la primaria. Las siguientes tablas muestran algunos ejemplos de los resultados.

TableName: Customers

CustomerID CompanyName Orders
ALFKI Alfreds Futterkiste 0
ANATR Ana Trujillo Emparedados y helados 1

TableName: CustomersOrders

CustomerID OrderID CustomersOrders
ALFKI 10643 0
ALFKI 10692 0
ANATR 10308 1
ANATR 10625 1

Vea también

Utilizar proveedores de datos de .NET Framework para obtener acceso a datos | Asignar los tipos de datos del proveedor de datos de .NET para los tipos de datos de .NET Framework | DataSet (Clase) | DataTable (Clase) | OleDbCommand (Clase) | OleDbConnection (Clase) | OleDbDataAdapter (Clase) | OdbcCommand (Clase) | OdbcConnection (Clase) | OdbcDataAdapter (Clase) | SqlCommand (Clase) | SqlConnection (Clase) | SqlDataAdapter (Clase)