Share via


Abrufen von Daten mit dem DataReader-Objekt

Mit dem ADO.NET-DataReader-Objekt können Sie schreibgeschützte Vorwärtsdatenstreams aus einer Datenbank abrufen. Die Ergebnisse werden bereits während der Ausführung der Abfrage zurückgegeben und im Netzwerkpuffer auf dem Client gespeichert, bis sie mit der Read-Methode des DataReader-Objekts abgefragt werden. Durch den Einsatz des DataReader-Objekts können Sie die Leistung der Anwendung in zweierlei Hinsicht erhöhen: zum einen werden Daten abgerufen, sobald sie verfügbar sind, anstatt darauf zu warten, dass die vollständigen Ergebnisse der Abfrage zurückgegeben werden, zum anderen wird die Belastung des Systems reduziert, da (standardmäßig) immer nur eine Zeile gleichzeitig gespeichert wird.

Nachdem Sie eine Instanz des Command-Objekts erstellt haben, erstellen Sie ein DataReader-Objekt, indem Sie eine Command.ExecuteReader-Methode aufrufen, um Zeilen aus einer Datenquelle abzurufen, wie im folgenden Beispiel gezeigt.

Dim myReader As SqlDataReader = myCommand.ExecuteReader()
[C#]
SqlDataReader myReader = myCommand.ExecuteReader();

Zur Anzeige einer Zeile aus den Abfrageergebnissen verwenden Sie die Read-Methode des DataReader-Objekts. Sie können auf jede Spalte der zurückgegebenen Zeile zugreifen, indem Sie den Namen oder den Ordinalverweis der Spalte an das DataReader-Objekt übergeben. Die beste Leistung erzielen Sie jedoch, wenn Sie die vom DataReader-Objekt bereitgestellten Methoden verwenden, mit denen Sie auf Spaltenwerte in ihren systemeigenen Datentypen zugreifen können (GetDateTime, GetDouble, GetGuid, GetInt32 usw.). Eine Liste der typisierten Accessormethoden finden Sie unter der OleDbDataReader-Klasse und SqlDataReader-Klasse. Wenn Sie die typisierten Accessormethoden verwenden und der zugrunde liegende Datentyp bekannt ist, wird der Umfang der erforderlichen Typkonvertierung beim Abrufen des Spaltenwertes reduziert.

Hinweis   Version 1.1 von .NET Framework umfasst HasRows, eine zusätzliche Eigenschaft für den DataReader, anhand derer Sie vor dem Lesen des DataReader feststellen können, ob dieser Ergebnisse zurückgegeben hat.

Das folgende Codebeispiel durchläuft ein DataReader-Objekt und gibt zwei Spalten aus jeder Zeile zurück.

If myReader.HasRows Then
  Do While myReader.Read()
    Console.WriteLine(vbTab & "{0}" & vbTab & "{1}", myReader.GetInt32(0), myReader.GetString(1))
  Loop
Else
  Console.WriteLine("No rows returned.")
End If

myReader.Close()
[C#]
if (myReader.HasRows)
  while (myReader.Read())
    Console.WriteLine("\t{0}\t{1}", myReader.GetInt32(0), myReader.GetString(1));
else
  Console.WriteLine("No rows returned.");

myReader.Close();

Das DataReader-Objekt stellt einen nicht gepufferten Datenstream bereit, der der prozeduralen Logik eine effektive, sequenzielle Verarbeitung der Ergebnisse aus einer Datenquelle ermöglicht. Das DataReader-Objekt bietet sich an, wenn große Datenmengen abgerufen werden, weil die Daten nicht zwischengespeichert werden.

Schließen des DataReader-Objekts

Nach der Verwendung des DataReader-Objekts sollten Sie stets die Close-Methode aufrufen.

Wenn das Command-Objekt Ausgabeparameter oder Rückgabewerte enthält, stehen diese erst zur Verfügung, wenn das DataReader-Objekt geschlossen ist.

Während ein DataReader-Objekt geöffnet ist, wird das Connection-Objekt exklusiv von diesem DataReader-Objekt verwendet. Befehle können erst dann für das Connection-Objekt ausgeführt werden, wenn das DataReader-Ausgangsobjekt geschlossen ist. Dies gilt auch für die Erstellung eines anderen DataReader-Objekts.

Hinweis   Rufen Sie für ein Connection-Objekt, ein DataReader-Objekt oder ein beliebiges anderes in der Finalize-Methode einer Klasse verwaltetes Objekt nicht Close oder Dispose auf. Geben Sie in einem Finalizer nur nicht verwaltete Ressourcen frei, deren direkter Eigentümer die jeweilige Klasse ist. Wenn eine Klasse keine nicht verwalteten Ressourcen besitzt, fügen Sie in der Klassendefinition keine Finalize-Methode ein. Weitere Informationen finden Sie unter Programmieren für die Garbage Collection.

Mehrere Resultsets

Wenn mehrere Resultsets zurückgegeben werden, stellt das DataReader-Objekt die NextResult-Methode zur Verfügung, um die Resultsets der Reihe nach zu durchlaufen, wie im folgenden Beispiel gezeigt.

Dim myCMD As SqlCommand = New SqlCommand("SELECT CategoryID, CategoryName FROM Categories;" & _
                                         "SELECT EmployeeID, LastName FROM Employees", nwindConn)
nwindConn.Open()

Dim myReader As SqlDataReader = myCMD.ExecuteReader()

Dim fNextResult As Boolean = True
Do Until Not fNextResult
  Console.WriteLine(vbTab & myReader.GetName(0) & vbTab & myReader.GetName(1))

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

  fNextResult = myReader.NextResult()
Loop

myReader.Close()
nwindConn.Close()
[C#]
SqlCommand myCMD = new SqlCommand("SELECT CategoryID, CategoryName FROM Categories;" +
                                  "SELECT EmployeeID, LastName FROM Employees", nwindConn);
nwindConn.Open();

SqlDataReader myReader = myCMD.ExecuteReader();

do
{
  Console.WriteLine("\t{0}\t{1}", myReader.GetName(0), myReader.GetName(1));

  while (myReader.Read())
    Console.WriteLine("\t{0}\t{1}", myReader.GetInt32(0), myReader.GetString(1));

} while (myReader.NextResult());

myReader.Close();
nwindConn.Close();

Abrufen von Schemainformationen aus dem DataReader-Objekt

Während ein DataReader-Objekt geöffnet ist, können Sie mit der GetSchemaTable-Methode Schemainformationen zum aktuellen Resultset abrufen. Die GetSchemaTable-Methode gibt ein DataTable-Objekt mit Zeilen und Spalten zurück, die Schemainformationen für das aktuelle Resultset beinhalten. Das DataTable-Objekt enthält eine Zeile für jede Spalte des Resultsets. Jede Spalte der Schematabellenzeile entspricht einer Eigenschaft der im Resultset zurückgegebenen Spalte, wobei ColumnName der Name der Eigenschaft und der Spaltenwert der Wert der Eigenschaft ist. Das folgende Codebeispiel gibt die Schemainformationen für das DataReader-Objekt aus.

Dim schemaTable As DataTable = myReader.GetSchemaTable()

Dim myRow As DataRow
Dim myCol As DataColumn

For Each myRow In schemaTable.Rows
  For Each myCol In schemaTable.Columns
    Console.WriteLine(myCol.ColumnName & " = " & myRow(myCol).ToString())
  Next
  Console.WriteLine()
Next
[C#]
DataTable schemaTable = myReader.GetSchemaTable();

foreach (DataRow myRow in schemaTable.Rows)
{
  foreach (DataColumn myCol in schemaTable.Columns)
    Console.WriteLine(myCol.ColumnName + " = " + myRow[myCol]);
  Console.WriteLine();
}

OLE DB-Kapitel

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 bereitgestellt.

Mit dem ADO.NET-DataSet können auch hierarchische Rowsets dargestellt werden, die über- und untergeordnete Tabellen verwenden. Weitere Informationen finden Sie unter Erstellen und Verwenden von DataSets.

Im folgenden Beispiel wird der MSDataShape-Provider verwendet, um eine Kapitelspalte mit Bestellungen für jeden Kunden in einer Kundenliste zu erstellen.

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

Dim custCMD As OleDbCommand = New OleDbCommand("SHAPE {SELECT CustomerID, CompanyName FROM Customers} " & _
                                         "  APPEND ({SELECT CustomerID, OrderID FROM Orders} AS CustomerOrders " & _
                                         "  RELATE CustomerID TO CustomerID)", nwindConn)
nwindConn.Open()

Dim custReader As OleDbDataReader = custCMD.ExecuteReader()
Dim orderReader As OleDbDataReader

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

  orderReader = 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()
Loop

custReader.Close()
nwindConn.Close()
[C#]
OleDbConnection nwindConn = new OleDbConnection("Provider=MSDataShape;Data Provider=SQLOLEDB;" +
                                                "Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind");

OleDbCommand custCMD = new OleDbCommand("SHAPE {SELECT CustomerID, CompanyName FROM Customers} " +
                                      "  APPEND ({SELECT CustomerID, OrderID FROM Orders} AS CustomerOrders " +
                                      "  RELATE CustomerID TO CustomerID)", nwindConn);
nwindConn.Open();

OleDbDataReader custReader = custCMD.ExecuteReader();
OleDbDataReader orderReader;

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

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

custReader.Close();
nwindConn.Close();

Oracle REF CURSORs

Für die Rückgabe von Abfrageergebnissen unterstützt der .NET Framework-Datenprovider für Oracle die Verwendung von Oracle REF CURSORs. Ein Oracle REF CURSOR wird als OracleDataReader zurückgegeben.

Ein OracleDataReader-Objekt, das einem Oracle REF CURSOR entspricht, können Sie mit der OracleCommand.ExecuteReader-Methode abrufen. Außerdem können Sie für einen OracleDataAdapter, der zum Auffüllen eines DataSet verwendet wird, ein OracleCommand angeben, das einen oder mehrere Oracle REF CURSORs als SelectCommand zurückgibt.

Um auf einen von einer Datenquelle zurückgegebenen REF CURSOR zuzugreifen, erstellen Sie ein OracleCommand für eine Abfrage, und fügen der Parameters-Auflistung des OracleCommand einen Ausgabeparameter hinzu, der auf den REF CURSOR verweist. Der Name des Parameters muss mit dem Namen des REF CURSOR-Parameters in der Abfrage übereinstimmen. Legen Sie für den Typ des Parameters OracleType.Cursor fest. Die ExecuteReader-Methode des OracleCommand gibt einen OracleDataReader für den REF CURSOR zurück.

Wenn das OracleCommand mehrere REF CURSORs zurückgibt, fügen Sie mehrere Ausgabeparameter hinzu. Durch einen Aufruf der OracleCommand.ExecuteReader-Methode können Sie auf die verschiedenen REF CURSORs zugreifen. Der Aufruf von ExecuteReader gibt einen OracleDataReader zurück, der auf den ersten REF CURSOR verweist. Mit der OracleDataReader.NextResult-Methode können Sie dann auf die übrigen REF CURSORs zugreifen. Obwohl die Namen der Parameter in der OracleCommand.Parameters-Auflistung den Namen der REF CURSOR-Ausgabeparameter entsprechen, greift der 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; 

Der folgende Code erstellt ein OracleCommand, das die REF CURSORs aus dem oben stehenden Oracle-Paket zurückgibt, indem der Parameters-Auflistung zwei Parameter vom Typ OracleType.Cursor hinzugefügt werden.

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
[C#]
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 Codebeispiel werden die Ergebnisse des Befehls aus dem vorherigen Beispiel mit den Methoden Read und NextResult des 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 rdr As OracleDataReader = cursCmd.ExecuteReader()

Console.WriteLine(vbCrLf & "Emp ID" & vbTab & "Name")

Do While rdr.Read()
  Console.WriteLine("{0}" & vbTab & "{1}, {2}", rdr.GetOracleNumber(0), rdr.GetString(1), rdr.GetString(2))
Loop

rdr.NextResult()

Console.WriteLine(vbCrLf & "Dept ID" & vbTab & "Name")

Do While rdr.Read()
  Console.WriteLine("{0}" & vbTab & "{1}", rdr.GetOracleNumber(0), rdr.GetString(1))
Loop

rdr.Close()
oraConn.Close()
[C#]
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 rdr = cursCmd.ExecuteReader();

Console.WriteLine("\nEmp ID\tName");

while (rdr.Read())
  Console.WriteLine("{0}\t{1}, {2}", rdr.GetOracleNumber(0), rdr.GetString(1), rdr.GetString(2));

rdr.NextResult();

Console.WriteLine("\nDept ID\tName");

while (rdr.Read())
  Console.WriteLine("{0}\t{1}", rdr.GetOracleNumber(0), rdr.GetString(1));

rdr.Close();
oraConn.Close();

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

**Hinweis   **Um eine OverflowException zu vermeiden, wird empfohlen, vor der Speicherung des Wertes in einer DataRow alle notwendigen Konvertierungen vom Oracle-Typ NUMBER in einen gültigen .NET Framework-Typ durchzuführen. Mit Hilfe des FillError-Ereignisses können Sie feststellen, ob eine OverflowException aufgetreten ist. Weitere Informationen zum FillError-Ereignis finden Sie unter Arbeiten mit DataAdapter-Ereignissen.

Dim ds As DataSet = New DataSet()

Dim oraDa As OracleDataAdapter = New OracleDataAdapter(cursCmd)
oraDa.TableMappings.Add("Table", "Employees")
oraDa.TableMappings.Add("Table1", "Departments")

oraDa.Fill(ds)
[C#]
DataSet ds = new DataSet();

OracleDataAdapter oraDa = new OracleDataAdapter(cursCmd);
oraDa.TableMappings.Add("Table", "Employees");
oraDa.TableMappings.Add("Table1", "Departments");

oraDa.Fill(ds);

Siehe auch

Datenzugriff mit .NET Framework-Datenprovidern | DataTable-Klasse | OleDbDataReader-Klasse | SqlDataReader-Klasse