使用 DataReader 检索数据包括创建 Command 对象的实例,然后通过调用 Command.ExecuteReader 创建一个 DataReader,以便从数据源检索行。下面的示例演示如何使用 DataReader,其中 reader 表示有效的 DataReader,而 command 表示有效的 Command 对象。
reader = command.ExecuteReader(); |
使用 DataReader 对象的 Read 方法可从查询结果中获取行。通过向 DataReader 传递列的名称或序号引用,可以访问返回行的每一列。不过,为了实现最佳性能,DataReader 提供了一系列方法,将使您能够访问其本机数据类型(GetDateTime、GetDouble、GetGuid、GetInt32 等)的列值。有关数据提供程序特定的 DataReaders 的类型化访问器方法列表,请参见 OleDbDataReader 和 SqlDataReader。假定基础数据类型为已知,如果使用类型化访问器方法,将减少在检索列值时所需的类型转换量。
![]() |
---|
.NET Framework 的 Windows Server 2003 版包含 DataReader 的附加属性 HasRows,该属性使您能够在读取 DataReader 之前就可确定它是否返回了任何结果。 |
以下代码示例循环访问一个 DataReader 对象,并从每个行中返回两个列。
Visual Basic
|
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 |
C#
|
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 是一种适合的选择。
![](http://i.msdn.microsoft.com/Platform/Controls/CollapsibleArea/resources/minus.gif)
每次使用完 DataReader 对象后都应调用 Close 方法。
如果 Command 包含输出参数或返回值,那么在 DataReader 关闭之前,将无法访问这些输出参数或返回值。
请注意,当 DataReader 打开时,该 DataReader 将以独占方式使用 Connection。在原始 DataReader 关闭之前,将无法对 Connection 执行任何命令(包括创建另一个 DataReader)。
![]() |
---|
不要在类的 Finalize 方法中对 Connection、DataReader 或任何其他托管对象调用 Close 或 Dispose。在终结器中,仅释放类直接拥有的非托管资源。如果类不拥有任何非托管资源,则不要在类定义中包含 Finalize 方法。有关更多信息,请参见 垃圾回收。 |
![](http://i.msdn.microsoft.com/Platform/Controls/CollapsibleArea/resources/minus.gif)
如果返回的是多个结果集,DataReader 会提供 NextResult 方法来按顺序循环访问这些结果集。以下示例显示 SqlDataReader 如何使用 ExecuteReader 方法处理两个 SELECT 语句的结果。
Visual Basic
|
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 |
C#
|
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(); } } } |
![](http://i.msdn.microsoft.com/Platform/Controls/CollapsibleArea/resources/minus.gif)
当 DataReader 打开时,可以使用 GetSchemaTable 方法检索有关当前结果集的架构信息。GetSchemaTable 将返回一个填充了行和列的 DataTable 对象,这些行和列包含当前结果集的架构信息。对于结果集的每一列,DataTable 都包含一行。架构表行的每一列都映射到在结果集中返回的列的属性,其中 ColumnName 是属性的名称,而列的值为属性的值。以下代码示例为 DataReader 编写架构信息。
Visual Basic
|
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 |
C#
|
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])); } } } } |
![](http://i.msdn.microsoft.com/Platform/Controls/CollapsibleArea/resources/minus.gif)
分层行集或章节(OLE DB 类型 DBTYPE_HCHAPTER、ADO 类型 adChapter)可以使用 OleDbDataReader 来检索。当以 DataReader 的形式返回包含某章节的查询时,该章节将以此 DataReader 中列的形式返回,并作为 DataReader 对象公开。
ADO.NET DataSet 也可用于通过表间的父子关系来表示分层行集。有关更多信息,请参见 DataSet、DataTable 和 DataView (ADO.NET)。
以下代码示例使用 MSDataShape 提供程序来为客户列表中的每个客户生成订单的章节列。
Visual Basic
|
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 |
C#
|
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(); } |
![](http://i.msdn.microsoft.com/Platform/Controls/CollapsibleArea/resources/minus.gif)
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 的 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 包和包正文。
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; |
以下代码将创建从先前的 Oracle 包返回 REF CURSOR 的 OracleCommand,方法是将类型 OracleType.Cursor 的两个参数添加到 Parameters 集合中。
Visual Basic
|
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 |
C#
|
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 参数按顺序返回。
Visual Basic
|
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() |
C#
|
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。
![]() |
---|
为了避免 OverflowException,建议您在将值存储在 DataRow 中之前,还应执行从 Oracle NUMBER 类型到有效的 .NET Framework 类型的任何转换。可以使用 FillError 事件来确定是否发生了 OverflowException。有关 FillError 事件的更多信息,请参见 处理 DataAdapter 事件 (ADO.NET)。 |
Visual Basic
|
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) |
C#
|
DataSet ds = new DataSet(); OracleDataAdapter adapter = new OracleDataAdapter(cursCmd); adapter.TableMappings.Add("Table", "Employees"); adapter.TableMappings.Add("Table1", "Departments"); adapter.Fill(ds); |