使用 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("{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

關閉 DataReader

用完 DataReader 物件後,請一律呼叫 Close 方法。

如果您的 Command 包含輸出參數或傳回值,則必須等到 DataReader 關閉後才能使用這些值。

DataReader 開啟期間,Connection 只能供該 DataReader 使用。 必須等到原始 DataReader 關閉後,才能執行 Connection 的任何命令 (包括建立其他 DataReader)。

注意

請不要在 Connection 上呼叫 CloseDispose、呼叫 DataReader,或您類別之 Finalize 方法中的任何其他 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{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

從 DataReader 取得結構描述資訊

DataReader 開啟期間,您可以使用 GetSchemaTable 方法擷取目前結果集的結構描述資訊。 GetSchemaTable 會傳回 DataTable 物件,並填入內含目前結果集之結構描述資訊的資料列和資料行。 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 章節

您可以使用 OleDbDataReader 擷取階層式資料列集或章節 (OLE DB 型別 DBTYPE_HCHAPTER、ADO 型別 adChapter)。 包含章節的查詢傳回為 DataReader 時,章節會傳回為 DataReader 的資料行並公開為 DataReader 物件。

您也可以使用 ADO.NET DataSet 來表示階層式資料列集,方法是在資料表間使用父子關係。 如需詳細資訊,請參閱 DataSets、DataTables 與 DataViews

下列程式碼範例使用 MSDataShape 提供者,替客戶清單中每位客戶的訂單產生章節資料行。

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 來傳回結果

Oracle 的 .NET Framework 資料提供者支援使用 Oracle REF CURSOR 傳回查詢結果。 Oracle REF CURSOR 以 OracleDataReader 傳回。

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

若要存取從 Oracle 資料來源傳回的 REF CURSOR,請為查詢建立 OracleCommand,並參考 REF CURSOR 的輸出參數,將它加入至 OracleCommandParameters 集合。 參數的名稱必須符合查詢中 REF CURSOR 參數的名稱。 將參數的型別設定為 OracleType.CursorOracleCommandOracleCommand.ExecuteReader() 方法傳回 REF CURSOR 的 OracleDataReader

如果您的 OracleCommand 傳回多個 REF CURSOR,請加入多個輸出參數。 您可以呼叫 OracleCommand.ExecuteReader() 方法來存取不同的 REF CURSOR。 對 ExecuteReader() 的呼叫會傳回參考第一個 REF CURSOR 的 OracleDataReader。 接著即可呼叫 OracleDataReader.NextResult() 方法存取後續的 REF CURSOR。 儘管 OracleCommand.Parameters 集合的參數會依名稱比對 REF CURSOR 輸出參數,但 OracleDataReader 會依照新增至 Parameters 集合的順序來存取它們。

例如,請考慮下列的 Oracle Package 和 Package 內容。

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;

下列程式碼將 OracleType.Cursor 的兩個參數加入 OracleCommand.Parameters 集合,藉此建立 OracleCommand,從上一個 Oracle Package 傳回 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;  

下列程式碼會使用 OracleDataReaderRead()NextResult() 方法傳回上一個命令的結果。 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 Package 的結果填入 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,建議您在 DataRow 中儲存值之前,也處理任何從 Oracle NUMBER 型別到有效 .NET Framework 型別的轉換。 您可以使用 FillError 事件判斷是否已發生 OverflowException。 如需 FillError 事件的詳細資訊,請參閱處理 DataAdapter 事件

另請參閱