分享方式:


使用 DataReader 擷取數據

若要使用 DataReader 擷取數據,請建立 Command 對象的實例,然後呼叫 Command.ExecuteReader 來擷取數據源中的數據列,以建立 DataReaderDataReader 提供未經緩衝區處理的數據串流,可讓程序邏輯有效率地處理數據源的結果。 當您擷取大量數據時, DataReader 是不錯的選擇,因為數據不會在記憶體中快取。

下列範例說明如何使用 DataReader,其中 reader 代表有效的 DataReader,並 command 代表有效的 Command 物件。

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

使用 DataReader.Read 方法從查詢結果取得數據列。 您可以將資料行的名稱或序號傳遞至 DataReader,以存取傳回數據列的每個數據行。 不過,為了達到最佳效能, DataReader 提供了一系列方法,可讓您存取其原生數據類型中的數據行值(GetDateTimeGetDoubleGetGuidGetInt32 等等)。 如需數據提供者特定 DataReaders 的型別化存取方法清單,請參考 OleDbDataReaderSqlDataReader。 當您知道基礎數據類型時,使用類型化存取方法可減少擷取欄位值時所需的類型轉換需求。

下列範例會逐一查看 DataReader 物件, 並從每個數據列傳回兩個數據行。

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($"{reader.GetInt32(0)}\t{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

關閉 DataReader

當您使用 DataReader 物件完成時,請一律呼叫 Close 方法。

如果您的 Command 包含輸出參數或傳回值,則在 DataReader 關閉之前,這些值將無法使用。

DataReader 開啟時, Connection 會由該 DataReader 獨佔使用。 在原始 DataReader 關閉之前,您無法對此 Connection 執行任何命令,包括建立另一個 DataReader

備註

請勿在 ConnectionDataReader 或類別的 Finalize 方法中呼叫 CloseDispose 的任何其他 Managed 物件。 在完成項中,只需釋放類別直接擁有的 Unmanaged 資源。 如果您的類別未擁有任何 Unmanaged 資源,請勿在類別定義中包含 Finalize 方法。 如需詳細資訊,請參閱記憶體回收

使用 NextResult 擷取多個結果集

如果 DataReader 傳回多個結果集,請呼叫 NextResult 方法,以循序逐一查看結果集。 下列範例示範 SqlDataReader 使用 ExecuteReader 方法處理兩個 SELECT 語句的結果。

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{reader.GetName(0)}\t{reader.GetName(1)}");

            while (reader.Read())
            {
                Console.WriteLine($"\t{reader.GetInt32(0)}\t{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

從 DataReader 取得架構資訊

DataReader 開啟時,您可以使用 GetSchemaTable 方法來擷取目前結果集的架構資訊。 GetSchemaTable 會傳回一個物件,其中填充了數據行和數據列,這些行和列包含目前結果集的架構資訊。 DataTable 會針對結果集的每個欄位包含一列資料。 架構數據表的每個數據行都會對應至結果集數據列中傳回之數據行的屬性,其中 ColumnName 是屬性的名稱,而數據行的值則是 屬性的值。 下列範例會寫出 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

使用 OLE DB 章節

您可以使用 擷取階層式數據列集或章節(OLE DB 類型DBTYPE_HCHAPTER、ADO 類型 OleDbDataReader)。 當包含章節的查詢以 DataReader 傳回時,該章節會以該 DataReader 中的數據行的形式傳回,並公開為 DataReader 物件。

ADO.NET DataSet 也可用來使用數據表之間的父子式關聯性來表示階層式數據列集。 如需詳細資訊,請參閱 DataSets、DataTables 和 DataViews

下列程式碼範例會使用 MSDataShape Provider,為客戶清單中的每個客戶生成包含訂單信息的章節列。

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

使用 Oracle REF CURSOR 傳回結果

.NET Framework Data Provider for Oracle 支援使用 Oracle REF CURSOR 傳回查詢結果。 Oracle REF CURSOR 會作為 OracleDataReader 傳回。

您可以使用 OracleDataReader 方法來擷取一個代表 Oracle REF CURSOR 的 ExecuteReader 物件。 您也可以指定 ,OracleCommand將傳回一或多個 Oracle REF CURSOR 做為 SelectCommandOracleDataAdapter,用於填滿 DataSet

若要存取從 Oracle 數據源返回的 REF CURSOR,請為您的查詢建立 OracleCommand,並將引用 REF CURSOR 的輸出參數新增到您的 ParametersOracleCommand 集合中。 參數的名稱必須符合查詢中的 REF CURSOR 參數名稱。 將參數的類型設定為 OracleType.CursorOracleCommand.ExecuteReader() 方法的 OracleCommand 會傳回一個 REF CURSOR 的 OracleDataReader

如果您的OracleCommand返回多個 REF CURSORS,請新增多個輸出參數。 您可以呼叫 OracleCommand.ExecuteReader() 方法來存取不同的 REF CURSOR。 呼叫 ExecuteReader() 時會傳回參考第一個 REF CURSOR 的 OracleDataReader。 然後,您可以呼叫 OracleDataReader.NextResult() 方法來存取後續的 REF CURSOR。 雖然 OracleCommand.Parameters 集合中的參數名稱符合 REF CURSOR 輸出參數,但 OracleDataReader 會按照它們被新增至 Parameters 集合的順序進行存取。

例如,請考慮下列 Oracle 套件和套件主體。

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;

下列程式碼會建立一個 OracleCommand,透過將兩個類型為 OracleType.Cursor 的參數新增至 OracleCommand.Parameters 集合,從先前的 Oracle 套件傳回 REF CURSOR。

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;  

使用 Read()NextResult()OracleDataReader 方法,以下程式碼傳回上一個命令的結果。 REF CURSOR 參數會依序傳回。

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

下列範例會使用上一個命令,將 Oracle 套件的結果填入 DataSet

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

備註

若要避免 OverflowException,建議您也先處理從 Oracle NUMBER 類型到有效 .NET Framework 類型的任何轉換,再將值儲存在 DataRow 中。 您可以使用 FillError 事件來判斷 是否發生 OverflowException 。 如需事件的詳細資訊 FillError ,請參閱 處理 DataAdapter 事件

另請參閱