共用方式為


使用 DataReader 來擷取資料 (ADO.NET)

更新: November 2007

使用 DataReader 擷取資料時會建立 Command 物件的執行個體,再藉由呼叫 Command.ExecuteReader 擷取資料來源的資料列,建立 DataReader。下列範例將說明如何使用 DataReader,其中 reader 代表有效的 DataReader 而 command 代表有效的 Command 物件。

reader = command.ExecuteReader();

您使用 DataReader 物件的 Read 方法,從查詢結果取得資料列。您可以將資料行的名稱或循序參考傳遞給 DataReader,以存取傳回資料列的每個資料行。不過,為了達到最佳效能,DataReader 也提供了一系列方法,讓您以資料行的原生資料型別 (Native Data Type)(GetDateTimeGetDoubleGetGuidGetInt32 等等) 存取資料行的值。如需資料提供者特有 DataReaders 之具型別存取子方法的清單,請參閱 OleDbDataReaderSqlDataReader。假設已知基礎資料型別時,若使用具型別的存取子方法,可減少擷取資料行值時所需的型別轉換量。

注意事項:

.NET Framework 的 Windows Server 2003 版本包括 DataReaderHasRows 的額外屬性,可讓您在讀取 DataReader 傳回的結果前,先判斷是否已傳回該結果。

下列程式碼範例在 DataReader 物件中重複,並從每個資料列傳回兩個資料行。

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

DataReader 提供未緩衝的資料流,可使程序邏輯有效地循序處理來自資料來源的結果。需要擷取大量資料時,DataReader 是不錯的選擇,因為資料不會快取至記憶體。

關閉 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 陳述式的結果。

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

從 DataReader 取得結構描述資訊

DataReader 開啟期間,您可以使用 GetSchemaTable 方法擷取目前結果集的結構描述資訊。GetSchemaTable 傳回 DataTable 物件,其內填入內含目前結果集之結構描述資訊的資料列和資料行。DataTable 將針對結果集的每個資料行包含一個資料列。結構描述資料表資料列的每個資料行會對應至結果集內所傳回的資料行屬性,其中 ColumnName 等於屬性名稱,而資料行值則等於屬性的值。下列程式碼範例會寫出 DataReader 的結構描述資訊。

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

使用 OLE DB 章節

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

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

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

Using connection 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)", connection)
connection.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
' Make sure to always close readers and connections.
custReader.Close()
End Using
Using (OleDbConnection connection = 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)", connection);
connection.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();
}
// Make sure to always close readers and connections.
custReader.Close();
}

使用 Oracle REF CURSOR 來傳回結果

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

您可以擷取 OracleDataReader 物件 (其表示使用 ExecuteReader 方法的 Oracle REF CURSOR),也可以指定傳回一或多個 Oracle REF CURSOR 的 OracleCommand,做為讓 OracleDataAdapter 用來填入 DataSetSelectCommand

若要存取從 Oracle 資料來源傳回的 REF CURSOR,請為查詢建立一個 OracleCommand,並參考 REF CURSOR 的輸出參數,將它加入至 OracleCommandParameters 集合。參數的名稱必須符合查詢中 REF CURSOR 參數的名稱。將參數的型別設定為 OracleType.CursorOracleCommandExecuteReader 方法將傳回 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 型別的兩個參數加入 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;

下列程式碼使用 OracleDataReaderReadNextResult 方法,傳回上一個命令的結果。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

注意事項:

為了避免 OverflowException,建議您在 DataRow 中儲存值之前,也處理任何從 Oracle NUMBER 型別到有效 .NET Framework 型別的轉換。您可以使用 FillError 事件判斷是否已發生 OverflowException。如需 FillError 事件的詳細資訊,請參閱 處理 DataAdapter 事件 (ADO.NET)

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

請參閱

其他資源

DataReader (ADO.NET)

DataAdapter 和 DataReader (ADO.NET)

命令和參數 (ADO.NET)

擷取資料庫結構描述資訊 (ADO.NET)