要使用 DataReader 取得資料,先建立物件 Command 的實例,然後 DataReader 呼叫 Command.ExecuteReader 從資料來源取得資料列。 它 DataReader 提供無緩衝的資料流,使程序邏輯能有效率地依序處理來自資料來源的結果。 當你需要擷取大量資料時,選擇使用 DataReader 是不錯的,因為資料不會被緩存在記憶體中。
下列範例說明如何使用 DataReader,其中 reader 代表有效的 DataReader,並 command 代表有效的 Command 物件。
reader = command.ExecuteReader();
reader = command.ExecuteReader()
使用 DataReader.Read 方法從查詢結果取得數據列。 您可以將資料行的名稱或序號傳遞至 DataReader,以存取傳回數據列的每個數據行。 不過,為了最佳效能,它 DataReader 提供了一系列方法,讓你能存取欄位值的原生資料型態(GetDateTime、 GetDouble、 GetGuid、 GetInt32 等等)。 如需數據提供者特定 DataReaders 的型別化存取方法清單,請參考 OleDbDataReader 和 SqlDataReader。 當您知道基礎數據類型時,使用類型化存取方法可減少擷取欄位值時所需的類型轉換需求。
以下範例會遍歷一個 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
使用完Close物件後,務必呼叫該DataReader方法。
如果 Command 包含輸出參數或回傳值,那麼在 DataReader 關閉之前,這些值無法取得。
雖然一個 DataReader 是開放的,但該 Connection 是由 DataReader 獨佔使用。 在原始連接關閉之前,你無法執行任何命令,包括建立另一個資料讀取器。
備註
不要在你的類別方法中呼叫 Close 或 Dispose 在 Connection、 DataReader 或其他受管理物件 Finalize 上。 在完成項中,只需釋放類別直接擁有的 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 取得架構資訊
當 a 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 章節
您可以使用 擷取階層式數據列集或章節(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,並將其作為SelectCommand,配合使用OracleDataAdapter來填入DataSet。
若要存取從 Oracle 數據源返回的 REF CURSOR,請為您的查詢建立 OracleCommand,並將引用 REF CURSOR 的輸出參數新增到您的 Parameters 的 OracleCommand 集合中。 參數的名稱必須符合查詢中的 REF CURSOR 參數名稱。 將參數的類型設定為 OracleType.Cursor。 OracleCommand.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 事件。
另請參閱
- DataAdapter 和 DataReader
- 命令和參數
- 擷取資料庫架構資訊
- ADO.NET 概觀