Abrufen von Daten mithilfe eines DataReader-Objekts

Wenn Sie Daten mit einem DataReader-Objekt abrufen möchten, müssen Sie zunächst eine Instanz des Command-Objekts und anschließend ein DataReader-Objekt erstellen, indem Sie Command.ExecuteReader aufrufen, um Zeilen aus einer Datenquelle abzurufen. Das DataReader-Objekt stellt einen ungepufferten Datenstrom bereit, mit dem prozeduraler Logik eine effektive sequenzielle Verarbeitung der Ergebnisse aus einer Datenquelle ermöglicht wird. Das DataReader-Objekt ist gut für das Abrufen großer Datenmengen geeignet, da die Daten nicht im Arbeitsspeicher zwischengespeichert werden.

Im folgenden Beispiel wird die Verwendung eines DataReader-Objekts gezeigt, wobei reader für ein gültiges DataReader-Objekt und command für ein gültiges Command-Objekt steht.

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

Verwenden Sie die DataReader.Read-Methode, um eine Zeile aus den Abfrageergebnissen abzurufen. Sie können auf jede Spalte der zurückgegebenen Zeile zugreifen, indem Sie dem DataReader-Objekt den Namen oder die Ordinalzahl der Spalte übergeben. Die beste Leistung erzielen Sie jedoch, wenn Sie die vom DataReader-Objekt bereitgestellten Methoden verwenden, mit denen Sie auf Spaltenwerte in ihren nativen Datentypen zugreifen können (z. B. GetDateTime, GetDouble, GetGuid oder GetInt32). Eine Liste mit typisierten Zugriffsmethoden für anbieterspezifische DataReaders finden Sie unter OleDbDataReader und SqlDataReader. Mit den typisierten Accessormethoden wird die Menge der beim Abrufen des Spaltenwerts erforderlichen Typkonvertierungen reduziert, wenn der zugrunde liegende Datentyp bekannt ist.

Im folgenden Beispiel wird ein DataReader-Objekt durchlaufen, wobei von jeder Zeile zwei Spalten zurückgegeben werden.

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

Schließen des "DataReader"

Wenn das DataReader-Objekt nicht mehr benötigt wird, sollten Sie immer die Close-Methode aufrufen.

Wenn Ihr Command-Objekt Ausgabeparameter oder Rückgabewerte enthält, sind diese Werte erst nach dem Schließen des DataReader-Objekts verfügbar.

Solange ein DataReader-Objekt geöffnet ist, wird das Connection-Objekt ausschließlich von diesem DataReader-Objekt verwendet. Sie können erst dann Befehle für das Connection-Objekt ausführen, z. B. um ein anderes DataReader-Objekt zu erstellen, wenn das ursprüngliche DataReader-Objekt geschlossen wurde.

Hinweis

Rufen Sie Close oder Dispose nicht für Connection- oder DataReader-Objekte oder andere verwaltete Objekte in der Finalize-Methode Ihrer Klasse auf. Geben Sie in einer Finalize-Methode nur nicht verwaltete Ressourcen frei, die der Klasse direkt gehören. Wenn Ihre Klasse keine nicht verwalteten Ressourcen aufweist, verwenden Sie in Ihrer Klassendefinition keine Finalize-Methode. Weitere Informationen finden Sie unter Garbage Collection.

Abrufen mehrerer Resultsets mithilfe von „NextResult“

Wenn das DataReader-Objekt mehrere Resultsets zurückgibt, rufen Sie die NextResult-Methode auf, um die Resultsets nacheinander zu durchlaufen. Im folgenden Beispiel werden die Ergebnisse von zwei SELECT-Anweisungen mit der SqlDataReader-Methode von ExecuteReader verarbeitet.

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

Abrufen von Schemainformationen aus „DataReader“

Solange ein DataReader-Objekt geöffnet ist, können Sie mit der GetSchemaTable-Methode Schemainformationen zum aktuellen Resultset abrufen. GetSchemaTable gibt ein DataTable-Objekt zurück, das mit Zeilen und Spalten gefüllt ist, die die Schemainformationen für das aktuelle Resultset enthalten. Das DataTable-Objekt enthält eine Zeile für jede Spalte des Resultsets. Jede Spalte der Schematabelle ist einer Eigenschaft der zurückgegebenen Spalten in den Zeilen des Resultsets zugeordnet, wobei ColumnName der Name der Eigenschaft und der Spaltenwert der Wert der Eigenschaft ist. Im folgenden Beispiel werden die Schemainformationen für DataReader abgerufen.

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

Arbeiten mit OLE DB-Kapiteln

Hierarchische Rowsets oder Kapitel (OLE DB-Typ DBTYPE_HCHAPTER, ADO-Typ adChapter) können mit OleDbDataReader abgerufen werden. Wenn eine Abfrage mit einem Kapitel als DataReader zurückgegeben wird, wird das Kapitel als Spalte in diesem DataReader zurückgegeben und als DataReader-Objekt verfügbar gemacht.

Mit dem ADO.NET-DataSet können auch hierarchische Rowsets dargestellt werden, indem über- und untergeordnete Tabellen verwendet werden. Weitere Informationen finden Sie unter DataSets, DataTables und DataViews.

Im folgenden Codebeispiel wird mit dem MSDataShape-Anbieter eine Kapitelspalte mit Bestellungen für jeden Kunden in einer Kundenliste generiert.

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

Zurückgeben von Ergebnissen mit REF CURSOR-Datentypen von Oracle

Der .NET Framework-Datenanbieter für Oracle unterstützt die Verwendung von Oracle-REF CURSORs zur Rückgabe eines Abfrageergebnisses. Ein Oracle-REF CURSOR wird als OracleDataReader zurückgegeben.

Mithilfe der ExecuteReader-Methode können Sie ein OracleDataReader-Objekt abrufen, das einen REF CURSOR-Datentyp von Oracle darstellt. Außerdem können Sie für ein OracleDataAdapter-Objekt, das zum Auffüllen von DataSet verwendet wird, ein OracleCommand-Objekt angeben, das den REF CURSOR-Datentyp von Oracle als SelectCommand zurückgibt.

Erstellen Sie zum Zugreifen auf einen von einer Oracle-Datenquelle zurückgegebenen REF CURSOR-Datentyp einen Oracle-Befehl (OracleCommand) für die Abfrage, und fügen Sie der Parameters-Auflistung von OracleCommand einen Ausgabeparameter hinzu, der auf REF CURSOR verweist. Der Name des Parameters muss mit dem Namen des REF CURSOR-Parameters in der Abfrage übereinstimmen. Legen Sie den Typ des Parameters auf OracleType.Cursor fest. Die OracleCommand.ExecuteReader()-Methode von OracleCommand gibt OracleDataReader für REF CURSOR zurück.

Wenn OracleCommand mehrere REF CURSOR-Datentypen zurückgibt, fügen Sie mehrere Ausgabeparameter hinzu. Durch Aufrufen der OracleCommand.ExecuteReader()-Methode können Sie auf die verschiedenen REF CURSOR-Datentypen zugreifen. Der Aufruf von ExecuteReader() gibt einen OracleDataReader zurück, der auf den ersten REF CURSOR-Datentyp verweist. Sie können dann mit der OracleDataReader.NextResult()-Methode auf die nachfolgenden REF CURSOR-Datentypen zugreifen. Obwohl die Parameter der OracleCommand.Parameters-Auflistung dem Namen nach den REF CURSOR-Ausgabeparametern entsprechen, greift OracleDataReader auf diese in der Reihenfolge zu, in der sie der Parameters-Auflistung hinzugefügt wurden.

Betrachten Sie z. B. das folgende Oracle-Paket und den Paketkörper.

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;

Im folgenden Codebeispiel wird ein Oracle-Befehl (OracleCommand) erstellt, der die REF CURSOR-Datentypen aus dem oben stehenden Oracle-Paket zurückgibt. Hierzu werden der OracleCommand.Parameters-Auflistung zwei Parameter vom Typ OracleType.Cursor hinzugefügt.

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;  

Im folgenden Code werden die Ergebnisse des vorherigen Befehls mithilfe der Methode Read() und NextResult() von OracleDataReader zurückgegeben. Die REF CURSOR-Parameter werden der Reihe nach zurückgegeben.

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

Im folgenden Beispiel wird der Befehl aus dem vorherigen Codebeispiel verwendet, um DataSet mit den Ergebnissen des Oracle-Pakets aufzufüllen.

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

Hinweis

Zur Vermeidung von OverflowException wird empfohlen, alle notwendigen Konvertierungen vom Oracle-Typ NUMBER in einen gültigen .NET Framework-Typ vor dem Speichern des Werts in DataRow auszuführen. Mit dem FillError-Ereignis kann festgestellt werden, ob OverflowException aufgetreten ist. Weitere Informationen zum FillError-Ereignis finden Sie unter Umgang mit DataAdapter-Ereignissen.

Weitere Informationen