可以使用 ADO.NET DataReader 从数据库中检索只读、只进的数据流。因为每次在内存中始终只有一行,所以使用 DataReader 可提高应用程序的性能并减少系统开销。

当创建 Command 对象的实例后,可调用 Command.ExecuteReader 从数据源中检索行,从而创建一个 DataReader,如以下示例所示。

Dim myReader As SqlDataReader = myCommand.ExecuteReader()

SqlDataReader myReader = myCommand.ExecuteReader();

使用 DataReader 对象的 Read 方法可从查询结果中获取行。通过向 DataReader 传递列的名称或序号引用,可以访问返回行的每一列。不过,为了实现最佳性能,DataReader 提供了一系列方法,它们将使您能够访问其本机数据类型(GetDateTimeGetDoubleGetGuidGetInt32 等)形式的列值。有关类型化访问器方法的列表,请参阅 OleDbDataReader 类 SqlDataReader 类 。如果在基础数据类型未知时使用类型化访问器方法,将减少在检索列值时所需的类型转换量。

以下代码示例循环访问一个 DataReader 对象,并从每个行中返回两个列。

Do While myReader.Read()
  Console.WriteLine(vbTab & "{0}" & vbTab & "{1}", myReader.GetInt32(0), myReader.GetString(1))
Loop
myReader.Close()

while (myReader.Read())
  Console.WriteLine("\t{0}\t{1}", myReader.GetInt32(0), myReader.GetString(1));
myReader.Close();

DataReader 提供未缓冲的数据流,该数据流使过程逻辑可以有效地按顺序处理从数据源中返回的结果。由于数据不在内存中缓存,所以在检索大量数据时,DataReader 是一种适合的选择。

关闭 DataReader

每次使用完 DataReader 对象后都应调用 Close 方法。

如果 Command 包含输出参数或返回值,那么在 DataReader 关闭之前,将无法访问这些输出参数或返回值。

请注意,当 DataReader 打开时,该 DataReader 将以独占方式使用 Connection。在初始 DataReader 关闭之前,将无法对 Connection 执行任何命令(包括创建另一个 DataReader)。

多个结果集

如果返回的是多个结果集,DataReader 会提供 NextResult 方法来按顺序循环访问这些结果集,如以下代码示例所示。

Dim myCMD As SqlCommand = New SqlCommand("SELECT CategoryID, CategoryName FROM Categories;" & _
                                         "SELECT EmployeeID, LastName FROM Employees", nwindConn)
nwindConn.Open()

Dim myReader As SqlDataReader = myCMD.ExecuteReader()

Dim fNextResult As Boolean = True
Do Until Not fNextResult
  Console.WriteLine(vbTab & myReader.GetName(0) & vbTab & myReader.GetName(1))

  Do While myReader.Read()
    Console.WriteLine(vbTab & myReader.GetInt32(0) & vbTab & myReader.GetString(1))
  Loop

  fNextResult = myReader.NextResult()
Loop

myReader.Close()
nwindConn.Close()

SqlCommand myCMD = new SqlCommand("SELECT CategoryID, CategoryName FROM Categories;" +
                                  "SELECT EmployeeID, LastName FROM Employees", nwindConn);
nwindConn.Open();

SqlDataReader myReader = myCMD.ExecuteReader();

do
{
  Console.WriteLine("\t{0}\t{1}", myReader.GetName(0), myReader.GetName(1));

  while (myReader.Read())
    Console.WriteLine("\t{0}\t{1}", myReader.GetInt32(0), myReader.GetString(1));

} while (myReader.NextResult());

myReader.Close();
nwindConn.Close();

从 DataReader 中获取架构信息

DataReader 打开时,可以使用 GetSchemaTable 方法检索有关当前结果集的架构信息。GetSchemaTable 将返回一个填充了行和列的 DataTable 对象,这些行和列包含当前结果集的架构信息。对于结果集的每一列,DataTable 都将包含一行。架构表行的每一列都映射到在结果集中返回的列的属性,其中 ColumnName 是属性的名称,而列的值为属性的值。以下代码示例为 DataReader 写出架构信息。

Dim schemaTable As DataTable = myReader.GetSchemaTable()

Dim myRow As DataRow
Dim myCol As DataColumn

For Each myRow In schemaTable.Rows
  For Each myCol In schemaTable.Columns
    Console.WriteLine(myCol.ColumnName & " = " & myRow(myCol).ToString())
  Next
  Console.WriteLine()
Next

DataTable schemaTable = myReader.GetSchemaTable();

foreach (DataRow myRow in schemaTable.Rows)
{
  foreach (DataColumn myCol in schemaTable.Columns)
    Console.WriteLine(myCol.ColumnName + " = " + myRow[myCol]);
  Console.WriteLine();
}

OLE DB 章节

分层行集或章节(OLE DB 类型 DBTYPE_HCHAPTER、ADO 类型 adChapter)可以使用 OleDbDataReader 来检索。当以 DataReader 的形式返回包含某章节的查询时,该章节将以此 DataReader 中列的形式返回,并公开为 DataReader 对象。

ADO.NET DataSet 也可用于通过表间的父子关系来表示分层行集。有关的更多信息,请参阅创建和使用 DataSet

以下代码示例使用 MSDataShape 提供程序来为客户列表中的每个客户生成订单的章节列。

Dim nwindConn 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)", nwindConn)
nwindConn.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

custReader.Close()
nwindConn.Close()

OleDbConnection nwindConn = 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)", nwindConn);
nwindConn.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();
}

custReader.Close();
nwindConn.Close();