Recuperare dati usando un DataReader

Per recuperare dati usando DataReader, creare un'istanza dell'oggetto Command e quindi creare DataReader chiamando Command.ExecuteReader per recuperare righe da un'origine dati. DataReader fornisce un flusso di dati non archiviati nel buffer che consente alla logica procedurale di elaborare sequenzialmente i risultati provenienti da un'origine dati. L'uso di DataReader è consigliabile quando si recuperano grandi quantità di dati perché i dati non sono memorizzati nella cache.

L'esempio seguente illustra l'uso di DataReader in cui reader rappresenta un oggetto DataReader valido e command rappresenta un oggetto Command valido.

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

Usare il metodo DataReader.Read per ottenere una riga dai risultati della query. È possibile accedere a ogni colonna della riga restituita passando il nome o il numero ordinale della colonna a DataReader. Per migliorare le prestazioni, tuttavia, DataReader fornisce una serie di metodi che consentono di accedere ai valori delle colonne nei tipi di dati nativi, quali GetDateTime, GetDouble, GetGuid, GetInt32 e così via. Per un elenco dei meccanismi di accesso tipizzati per DataReaders specifici del provider di dati, vedere OleDbDataReader e SqlDataReader. L'uso di metodi di funzioni di accesso tipizzate quando si conosce il tipo di dati sottostante riduce il numero di conversioni dei tipi necessarie al momento del recupero del valore della colonna.

L'esempio seguente scorre un oggetto DataReader e restituisce due colonne da ogni riga.

static void HasRows(SqlConnection connection)
{
    using (connection)
    {
        SqlCommand command = new(
          "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

Chiusura dell'oggetto DataReader

Chiamare sempre il metodo Close una volta terminato l'uso dell'oggetto DataReader.

I parametri di output o i valori restituiti presenti nell'oggetto Command non sono disponibili fino a quando l'oggetto DataReader non viene chiuso.

Mentre un oggetto DataReader è aperto, l'oggetto Connection viene usato esclusivamente da tale oggetto DataReader. Non sarà possibile eseguire alcun comando per l'oggetto Connection né creare un altro oggetto DataReader fino a quando l'oggetto DataReader originale non viene chiuso.

Nota

Non chiamare Close o Dispose su un oggetto Connection, un oggetto DataReader o su qualsiasi altro oggetto gestito nel metodo Finalize della classe. Nei finalizzatori rilasciare solo le risorse non gestite che la classe controlla direttamente. Se nella classe non sono presenti risorse non gestite, non includere un metodo Finalize nella definizione della classe. Per altre informazioni, vedere Garbage Collection.

Recupero di più set di risultati tramite NextResult

Se l'oggetto DataReader restituisce più set di risultati, chiamare il metodo NextResult per scorrere in modo sequenziale i set di risultati. Nell'esempio seguente viene illustrata l'elaborazione dei risultati di due dichiarazioni SELECT da parte del tipo SqlDataReader usando il metodo ExecuteReader.

static void RetrieveMultipleResults(SqlConnection connection)
{
    using (connection)
    {
        SqlCommand command = new(
          "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

Recupero di informazioni sullo schema dall'oggetto DataReader

Mentre un oggetto DataReader è aperto, è possibile recuperare le informazioni sullo schema relative al set di risultati corrente usando il metodo GetSchemaTable. GetSchemaTable restituisce un oggetto DataTable popolato con righe e colonne contenenti le informazioni sullo schema del set di risultati corrente. L'oggetto DataTable contiene una riga per ogni colonna del set di risultati. Ogni colonna della tabella dello schema è associata a una proprietà delle colonne restituite nelle righe del set di risultati, in cui ColumnName è il nome della proprietà e il valore della colonna è il valore della proprietà. L'esempio seguente rilascia le informazioni sullo schema per DataReader.

static void GetSchemaInfo(SqlConnection connection)
{
    using (connection)
    {
        SqlCommand command = new(
          "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

Utilizzo dei capitoli OLE DB

I set di righe gerarchici o i capitoli (tipo OLE DB DBTYPE_HCHAPTER, tipo ADO adChapter), possono essere recuperati usando il OleDbDataReader. Quando una query che include un capitolo viene restituita come DataReader, il capitolo viene restituito come colonna in tale DataReader e viene esposto come oggetto DataReader.

Il DataSet (set di dati) ADO.NET può essere usato anche per rappresentare set di righe gerarchici usando relazioni padre-figlio tra tabelle. Per altre informazioni, vedere DataSet, DataTables e DataViews.

Nell'esempio di codice seguente viene usato il provider MSDataShape per generare una colonna del capitolo contenente gli ordini per ogni cliente presente in un elenco di clienti.

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();
        }
    }
}

Restituzione di risultati con i REF CURSOR Oracle

Con il provider di dati .NET Framework per Oracle è possibile usare i REF CURSOR Oracle per la restituzione del risultato di una query. I REF CURSOR Oracle vengono restituiti come OracleDataReader.

È possibile recuperare un oggetto OracleDataReader che rappresenta un REF CURSOR Oracle utilizzando il metodo ExecuteReader. È inoltre possibile specificare un oggetto OracleCommand che restituisce uno o più REF CURSOR Oracle come SelectCommand per un oggetto OracleDataAdapter usato per riempire un oggetto DataSet.

Per accedere a un REF CURSOR restituito da un'origine dati Oracle, creare un oggetto OracleCommand per la query e aggiungere un parametro di output che fa riferimento a REF CURSOR in relazione alla Parametersraccolta dell'oggettoOracleCommand. È necessario che il nome del parametro corrisponda al nome del parametro REF CURSOR della query. Impostare il tipo del parametro su OracleType.Cursor. Il metodo OracleCommand.ExecuteReader() dell'oggetto OracleCommand restituisce un oggetto OracleDataReader per REF CURSOR.

Se OracleCommand restituisce più REF CURSOR, aggiungere più parametri di output. È possibile accedere ai diversi REF CURSOR chiamando il metodo OracleCommand.ExecuteReader(). La chiamata a ExecuteReader() restituisce un oggetto OracleDataReader con riferimento al primo REF CURSOR. È quindi possibile chiamare il metodo OracleDataReader.NextResult() per accedere ai REF CURSOR successivi. Anche se i parametri nella raccolta OracleCommand.Parameters corrispondono ai parametri di output REF CURSOR in base al nome, l’oggetto OracleDataReader accede ad essi nell'ordine in cui sono stati aggiunti alla raccolta Parameters.

Si considerino ad esempio il package e il corpo package Oracle seguenti.

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;

Il codice seguente crea un oggetto OracleCommand che restituisce i REF CURSOR dal pacchetto Oracle precedente aggiungendo due parametri di tipo OracleType.Cursor alla raccolta OracleCommand.Parameters.

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;  

Il codice seguente restituisce i risultati del comando precedente usando i metodi Read() e NextResult() di OracleDataReader. I parametri REF CURSOR vengono restituiti in ordine.

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();  

Nell'esempio seguente viene usato il comando precedente per popolare un DataSet oggetto con i risultati del pacchetto 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

Per evitare un'eccezione OverflowException, è consigliabile gestire anche qualsiasi conversione dal tipo Oracle NUMBER a un tipo .NET Framework valido prima di archiviare il valore in un oggetto DataRow. È possibile utilizzare l'evento FillError per determinare se si è verificata un'eccezione OverflowException. Per altre informazioni sull'evento FillError, vedere Gestione degli eventi DataAdapter.

Vedi anche