Recuperación de datos con un objeto DataReader

Para recuperar datos mediante un objeto DataReader, cree una instancia del objeto Command y, después, un objeto DataReader mediante la llamada a Command.ExecuteReader para recuperar filas de un origen de datos. DataReader proporciona un flujo de datos no almacenado en búfer que permite a la lógica de procedimientos procesar de forma eficaz y secuencial los resultados de un origen de datos. DataReader es la mejor opción cuando se trata de recuperar grandes cantidades de datos, ya que estos no se almacenan en la memoria caché.

En el ejemplo siguiente se muestra cómo se usa un objeto DataReader, donde reader representa una instancia válida de DataReader y command un objeto Command válido.

reader = command.ExecuteReader();  
reader = command.ExecuteReader()

Use el método DataReader.Read para obtener una fila de los resultados de la consulta. Para acceder a cada columna de la fila devuelta, puede pasar al objeto DataReader el nombre o número ordinal de la columna. Pero para obtener el mejor rendimiento, el objeto DataReader proporciona una serie de métodos que permiten acceder a los valores de columna en sus tipos de datos nativos (GetDateTime, GetDouble, GetGuid, GetInt32, etc.). Para obtener una lista de métodos de accessor con tipo para DataReaders específicos del proveedor de datos, vea OleDbDataReader y SqlDataReader . Al usar los métodos de descriptor de acceso con tipo cuando se conoce el tipo de datos subyacente, se reduce el número de conversiones de tipo necesarias para recuperar el valor de columna.

En el ejemplo de código siguiente se realiza una iteración por un objeto DataReader y se devuelven dos columnas de cada fila.

static void HasRows(SqlConnection connection)
{
    using (connection)
    {
        SqlCommand command = new SqlCommand(
          "SELECT CategoryID, CategoryName FROM Categories;",
          connection);
        connection.Open();

        SqlDataReader reader = command.ExecuteReader();

        if (reader.HasRows)
        {
            while (reader.Read())
            {
                Console.WriteLine("{0}\t{1}", reader.GetInt32(0),
                    reader.GetString(1));
            }
        }
        else
        {
            Console.WriteLine("No rows found.");
        }
        reader.Close();
    }
}
Private Sub HasRows(ByVal connection As SqlConnection)
    Using connection
        Dim command As SqlCommand = New SqlCommand( _
          "SELECT CategoryID, CategoryName FROM Categories;", _
          connection)
        connection.Open()

        Dim reader As SqlDataReader = command.ExecuteReader()

        If reader.HasRows Then
            Do While reader.Read()
                Console.WriteLine(reader.GetInt32(0) _
                  & vbTab & reader.GetString(1))
            Loop
        Else
            Console.WriteLine("No rows found.")
        End If

        reader.Close()
    End Using
End Sub

Cerrar el DataReader

Llame siempre al método Close cuando haya terminado de usar el objeto DataReader.

Si Command contiene parámetros de salida o valores devueltos, no estarán disponibles hasta que se cierre el objeto DataReader.

Mientras un objeto DataReader está abierto, usa de forma exclusiva el objeto Connection. No se podrá ejecutar ningún comando para el objeto Connection hasta que se cierre el objeto DataReader original, incluida la creación de otro objeto DataReader.

Nota

No llame a Close o Dispose para objetos Connection o DataReader, ni para ningún otro objeto administrado en el método Finalize de la clase. En un finalizador, libere solo los recursos no administrados que pertenezcan directamente a su clase. Si la clase no dispone de recursos no administrados, no incluya un método Finalize en la definición de clase. Para obtener más información, consulte Recolección de elementos no utilizados.

Recuperación de varios conjuntos de resultados mediante NextResult

Si el objeto DataReader devuelve varios resultados, llame al método NextResult para realizar una iteración por los conjuntos de resultados de forma secuencial. En el siguiente ejemplo se muestra el SqlDataReader mientras procesa los resultados de las dos instrucciones SELECT mediante el método ExecuteReader.

static void RetrieveMultipleResults(SqlConnection connection)
{
    using (connection)
    {
        SqlCommand command = new SqlCommand(
          "SELECT CategoryID, CategoryName FROM dbo.Categories;" +
          "SELECT EmployeeID, LastName FROM dbo.Employees",
          connection);
        connection.Open();

        SqlDataReader reader = command.ExecuteReader();

        while (reader.HasRows)
        {
            Console.WriteLine("\t{0}\t{1}", reader.GetName(0),
                reader.GetName(1));

            while (reader.Read())
            {
                Console.WriteLine("\t{0}\t{1}", reader.GetInt32(0),
                    reader.GetString(1));
            }
            reader.NextResult();
        }
    }
}
Private Sub RetrieveMultipleResults(ByVal connection As SqlConnection)
    Using connection
        Dim command As SqlCommand = New SqlCommand( _
          "SELECT CategoryID, CategoryName FROM Categories;" & _
          "SELECT EmployeeID, LastName FROM Employees", connection)
        connection.Open()

        Dim reader As SqlDataReader = command.ExecuteReader()

        Do While reader.HasRows
            Console.WriteLine(vbTab & reader.GetName(0) _
              & vbTab & reader.GetName(1))

            Do While reader.Read()
                Console.WriteLine(vbTab & reader.GetInt32(0) _
                  & vbTab & reader.GetString(1))
            Loop

            reader.NextResult()
        Loop
    End Using
End Sub

Obtención de información de esquema de DataReader

Mientras un objeto DataReader está abierto, puede usar el método GetSchemaTable para recuperar información del esquema sobre el conjunto de resultados actual. GetSchemaTable devuelve un objeto DataTable rellenado con filas y columnas que contienen la información del esquema del conjunto de resultados actual. DataTable contiene una fila por cada una de las columnas del conjunto de resultados. Cada columna de la tabla de esquema se asocia a una propiedad de las columnas devueltas del conjunto de resultados, donde ColumnName es el nombre de la propiedad y el valor de la columna es el de la propiedad. En el ejemplo siguiente se escribe la información del esquema de DataReader.

static void GetSchemaInfo(SqlConnection connection)
{
    using (connection)
    {
        SqlCommand command = new SqlCommand(
          "SELECT CategoryID, CategoryName FROM Categories;",
          connection);
        connection.Open();

        SqlDataReader reader = command.ExecuteReader();
        DataTable schemaTable = reader.GetSchemaTable();

        foreach (DataRow row in schemaTable.Rows)
        {
            foreach (DataColumn column in schemaTable.Columns)
            {
                Console.WriteLine(String.Format("{0} = {1}",
                   column.ColumnName, row[column]));
            }
        }
    }
}
Private Sub GetSchemaInfo(ByVal connection As SqlConnection)
    Using connection
        Dim command As SqlCommand = New SqlCommand( _
          "SELECT CategoryID, CategoryName FROM Categories;", _
          connection)
        connection.Open()

        Dim reader As SqlDataReader = command.ExecuteReader()
        Dim schemaTable As DataTable = reader.GetSchemaTable()

        Dim row As DataRow
        Dim column As DataColumn

        For Each row In schemaTable.Rows
            For Each column In schemaTable.Columns
                Console.WriteLine(String.Format("{0} = {1}", _
                  column.ColumnName, row(column)))
            Next
            Console.WriteLine()
        Next
        reader.Close()
    End Using
End Sub

Trabajar con OLE DB capítulos

Los conjuntos de filas jerárquicos o los capítulos (OLE DB tipo DBTYPE_HCHAPTER, tipo ADO adChapter), se pueden recuperar mediante OleDbDataReader . Cuando se devuelve una consulta que incluye un capítulo como DataReader, el capítulo se devuelve como una columna en ese Objeto DataReader y se expone como un objeto DataReader.

El ADO.NET DataSet también se puede usar para representar conjuntos de filas jerárquicos mediante relaciones de elementos primarios y secundarios entre tablas. Para obtener más información, vea DataSets, DataTables y DataViews.

En el ejemplo de código 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.

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

    Using custCMD As OleDbCommand = New OleDbCommand(
        "SHAPE {SELECT CustomerID, CompanyName FROM Customers} " &
        "APPEND ({SELECT CustomerID, OrderID FROM Orders} AS CustomerOrders " &
        "RELATE CustomerID TO CustomerID)", connection)

        connection.Open()

        Using custReader As OleDbDataReader = custCMD.ExecuteReader()

            Do While custReader.Read()
                Console.WriteLine("Orders for " & custReader.GetString(1))
                ' custReader.GetString(1) = CompanyName  

                Using orderReader As OleDbDataReader = custReader.GetValue(2)
                    ' custReader.GetValue(2) = Orders chapter as DataReader  

                    Do While orderReader.Read()
                        Console.WriteLine(vbTab & orderReader.GetInt32(1))
                        ' orderReader.GetInt32(1) = OrderID  
                    Loop
                    orderReader.Close()
                End Using
            Loop
            ' Make sure to always close readers and connections.  
            custReader.Close()
        End Using
    End Using
End Using
using (OleDbConnection connection = new OleDbConnection(
    "Provider=MSDataShape;Data Provider=SQLOLEDB;" +
    "Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind"))
{
    using (OleDbCommand custCMD = new OleDbCommand(
        "SHAPE {SELECT CustomerID, CompanyName FROM Customers} " +
        "APPEND ({SELECT CustomerID, OrderID FROM Orders} AS CustomerOrders " +
        "RELATE CustomerID TO CustomerID)", connection))
    {
        connection.Open();

        using (OleDbDataReader custReader = custCMD.ExecuteReader())
        {

            while (custReader.Read())
            {
                Console.WriteLine("Orders for " + custReader.GetString(1));
                // custReader.GetString(1) = CompanyName  

                using (OleDbDataReader orderReader = (OleDbDataReader)custReader.GetValue(2))
                {
                    // custReader.GetValue(2) = Orders chapter as DataReader  

                    while (orderReader.Read())
                        Console.WriteLine("\t" + orderReader.GetInt32(1));
                    // orderReader.GetInt32(1) = OrderID  
                    orderReader.Close();
                }
            }
            // Make sure to always close readers and connections.  
            custReader.Close();
        }
    }
}

Devolver resultados con ref CURSORs de Oracle

El proveedor de datos .NET Framework para Oracle admite el uso de cursores REF CURSOR de Oracle para devolver los resultados de una consulta. Un REF CURSOR de Oracle se devuelve en forma de objeto OracleDataReader.

Puede recuperar un objeto OracleDataReader que representa un CURSOR REF de Oracle mediante el método ExecuteReader . También puede especificar un que devuelve uno o varios REF CURSOR de Oracle como SelectCommand para un objeto OracleCommand utilizado para rellenar un objeto OracleDataAdapter DataSet .

Para acceder a un REF CURSOR devuelto desde un origen de datos de Oracle, cree un para la consulta y agregue un parámetro de salida que haga referencia a REF CURSOR a la OracleCommand Parameters colección de OracleCommand . El nombre del parámetro debe coincidir con el nombre del parámetro REF CURSOR de la consulta. Establezca el tipo del parámetro en OracleType.Cursor . El OracleCommand.ExecuteReader() método de devuelve un para REF OracleCommand OracleDataReader CURSOR.

Si devuelve OracleCommand varios CURSORES REF, agregue varios parámetros de salida. Puede acceder a los distintos REF CURSOR llamando al OracleCommand.ExecuteReader() método . La llamada a ExecuteReader() devuelve una referencia al primer REF OracleDataReader CURSOR. A continuación, puede llamar al OracleDataReader.NextResult() método para acceder a los REF CURSOR posteriores. Aunque los parámetros de la colección coinciden con los parámetros de salida REF CURSOR por nombre, el tiene acceso a ellos en el orden en que se agregaron OracleCommand.Parameters OracleDataReader a la Parameters colección.

Por ejemplo, considere el siguiente paquete de Oracle y, concretamente, el cuerpo del paquete.

CREATE OR REPLACE PACKAGE CURSPKG AS
  TYPE T_CURSOR IS REF CURSOR;
  PROCEDURE OPEN_TWO_CURSORS (EMPCURSOR OUT T_CURSOR,
    DEPTCURSOR OUT T_CURSOR);
END CURSPKG;  
  
CREATE OR REPLACE PACKAGE BODY CURSPKG AS
  PROCEDURE OPEN_TWO_CURSORS (EMPCURSOR OUT T_CURSOR,
    DEPTCURSOR OUT T_CURSOR)
  IS
  BEGIN
    OPEN EMPCURSOR FOR SELECT * FROM DEMO.EMPLOYEE;
    OPEN DEPTCURSOR FOR SELECT * FROM DEMO.DEPARTMENT;
  END OPEN_TWO_CURSORS;
END CURSPKG;

El código siguiente crea un que devuelve los REF CURSORs del paquete anterior de Oracle agregando dos OracleCommand parámetros de tipo a la OracleType.Cursor OracleCommand.Parameters colección.

Dim cursCmd As OracleCommand = New OracleCommand("CURSPKG.OPEN_TWO_CURSORS", oraConn)  
cursCmd.Parameters.Add("EMPCURSOR", OracleType.Cursor).Direction = ParameterDirection.Output  
cursCmd.Parameters.Add("DEPTCURSOR", OracleType.Cursor).Direction = ParameterDirection.Output  
OracleCommand cursCmd = new OracleCommand("CURSPKG.OPEN_TWO_CURSORS", oraConn);  
cursCmd.Parameters.Add("EMPCURSOR", OracleType.Cursor).Direction = ParameterDirection.Output;  
cursCmd.Parameters.Add("DEPTCURSOR", OracleType.Cursor).Direction = ParameterDirection.Output;  

El código siguiente devuelve los resultados del comando anterior mediante los Read() NextResult() métodos y de OracleDataReader . Los parámetros REF CURSOR se devuelven en orden.

oraConn.Open()  
  
Dim cursCmd As OracleCommand = New OracleCommand("CURSPKG.OPEN_TWO_CURSORS", oraConn)  
cursCmd.CommandType = CommandType.StoredProcedure  
cursCmd.Parameters.Add("EMPCURSOR", OracleType.Cursor).Direction = ParameterDirection.Output  
cursCmd.Parameters.Add("DEPTCURSOR", OracleType.Cursor).Direction = ParameterDirection.Output  
  
Dim reader As OracleDataReader = cursCmd.ExecuteReader()  
  
Console.WriteLine(vbCrLf & "Emp ID" & vbTab & "Name")  
  
Do While reader.Read()  
  Console.WriteLine("{0}" & vbTab & "{1}, {2}", reader.GetOracleNumber(0), reader.GetString(1), reader.GetString(2))  
Loop  
  
reader.NextResult()  
  
Console.WriteLine(vbCrLf & "Dept ID" & vbTab & "Name")  
  
Do While reader.Read()  
  Console.WriteLine("{0}" & vbTab & "{1}", reader.GetOracleNumber(0), reader.GetString(1))  
Loop  
' Make sure to always close readers and connections.  
reader.Close()  
oraConn.Close()  
oraConn.Open();  
  
OracleCommand cursCmd = new OracleCommand("CURSPKG.OPEN_TWO_CURSORS", oraConn);  
cursCmd.CommandType = CommandType.StoredProcedure;  
cursCmd.Parameters.Add("EMPCURSOR", OracleType.Cursor).Direction = ParameterDirection.Output;  
cursCmd.Parameters.Add("DEPTCURSOR", OracleType.Cursor).Direction = ParameterDirection.Output;  
  
OracleDataReader reader = cursCmd.ExecuteReader();  
  
Console.WriteLine("\nEmp ID\tName");  
  
while (reader.Read())  
  Console.WriteLine("{0}\t{1}, {2}", reader.GetOracleNumber(0), reader.GetString(1), reader.GetString(2));  
  
reader.NextResult();  
  
Console.WriteLine("\nDept ID\tName");  
  
while (reader.Read())  
  Console.WriteLine("{0}\t{1}", reader.GetOracleNumber(0), reader.GetString(1));  
// Make sure to always close readers and connections.  
reader.Close();  
oraConn.Close();  

En el ejemplo siguiente se usa el comando anterior para rellenar DataSet con los resultados del paquete de Oracle.

Dim ds As DataSet = New DataSet()  
  
Dim adapter As OracleDataAdapter = New OracleDataAdapter(cursCmd)  
adapter.TableMappings.Add("Table", "Employees")  
adapter.TableMappings.Add("Table1", "Departments")  
  
adapter.Fill(ds)  
DataSet ds = new DataSet();  
  
OracleDataAdapter adapter = new OracleDataAdapter(cursCmd);  
adapter.TableMappings.Add("Table", "Employees");  
adapter.TableMappings.Add("Table1", "Departments");  
  
adapter.Fill(ds);  

Nota

Para evitar una excepción OverflowException, se recomienda controlar también cualquier conversión del tipo NUMBER de Oracle a un tipo .NET Framework válido antes de almacenar el valor en DataRow . Puede usar el evento FillError para determinar si se ha producido una excepción OverflowException. Para obtener más información sobre el FillError evento , vea Controlar eventos de DataAdapter.

Consulte también