使用 DataReader 擷取資料
若要使用 DataReader 擷取資料,請建立 Command 物件的執行個體,再藉由呼叫 Command.ExecuteReader 擷取資料來源的資料列,建立 DataReader。 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("{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 上呼叫 Close 或 Dispose、呼叫 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 做為用來填滿 DataSet 之 OracleDataAdapter 的 SelectCommand。
若要存取從 Oracle 資料來源傳回的 REF CURSOR,請為查詢建立 OracleCommand,並參考 REF CURSOR 的輸出參數,將它加入至 OracleCommand 的 Parameters 集合。 參數的名稱必須符合查詢中 REF CURSOR 參數的名稱。 將參數的型別設定為 OracleType.Cursor。 OracleCommand 的 OracleCommand.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;
下列程式碼會使用 OracleDataReader 的 Read() 和 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 事件。
另請參閱
- DataAdapter 和 DataReader
- 命令和參數
- 擷取資料庫結構描述資訊
- ADO.NET 概觀 \(部分機器翻譯\)