DataReader によるデータの取得 (ADO.NET)

更新 : November 2007

DataReader を使用してデータを取得するには、データ ソースから行を取得するために Command.ExecuteReader を呼び出して、Command オブジェクトのインスタンスを作成し、次に DataReader を作成する必要があります。DataReader を使用する例を次に示します。ここで、reader は有効な DataReader を、command は有効な Command オブジェクトを表します。

reader = command.ExecuteReader();

クエリの結果から行を取得するには、DataReader オブジェクトの Read メソッドを使用します。返された行の各列にアクセスするには、その列の名前または序数参照を DataReader に渡します。一方で DataReader は、最適のパフォーマンスが得られるように、ネイティブのデータ型を使用して列の値にアクセスするための一連のメソッド (GetDateTimeGetDoubleGetGuidGetInt32 など) も提供します。データ プロバイダ固有の DataReaders の型指定されたアクセサ メソッドの一覧については、「OleDbDataReader」および「SqlDataReader」を参照してください。基になるデータ型がわかっている場合、型指定されたアクセサ メソッドを使用すると、列の値を取得するときに必要な型変換の量が少なくなります。

haa3afyz.alert_note(ja-jp,VS.90).gifメモ :

.NET Framework の Windows Server 2003 リリースには、DataReader の追加のプロパティである、HasRows が含まれているため、これを使用すると、読み取る前に DataReader が結果を返したかどうかを確認できます。

次に、DataReader オブジェクトを反復処理して各行から 2 つの列を返すコード サンプルを示します。

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 を作成することもできません。

haa3afyz.alert_note(ja-jp,VS.90).gifメモ :

クラスの Finalize メソッド内で ConnectionDataReader、またはその他のマネージ オブジェクトの Close または Dispose を呼び出さないでください。終了処理では、クラスに直接所有されているアンマネージ リソースだけを解放してください。クラスがアンマネージ リソースを所有していない場合は、クラス定義に Finalize メソッドを含めないでください。詳細については、「ガベージ コレクション」を参照してください。

NextResult による複数の結果セットの取得

複数の結果セットが返された場合は、DataReaderNextResult メソッドを提供してその結果セットを順に反復処理します。ExecuteReader メソッドを使用して、2 つの SELECT ステートメントの結果を処理する SqlDataReader の例を次に示します。

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 には結果セットの列ごとに 1 行が設定されます。スキーマ テーブル行の各列が、結果セットで返される列の 1 つのプロパティに割り当てられます。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 のチャプタの使用

階層構造の行セット、つまりチャプタ (OLE DB では DBTYPE_HCHAPTER 型、ADO では adChapter 型) は OleDbDataReader を使用して取得できます。チャプタを含むクエリが DataReader として返されるとき、チャプタはその DataReader の列として返され、DataReader オブジェクトとして公開されます。

ADO.NET の DataSet を使用することで、テーブル間の親子のリレーションシップを使用して階層構造の行セットを表現することもできます。詳細については、「DataSets、DataTables、および DataViews (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 による結果の取得

.NET Framework Data Provider for Oracle は、クエリ結果を返すために、Oracle REF CURSOR の使用をサポートしています。Oracle REF CURSOR は OracleDataReader として返されます。

Oracle REF CURSOR を表す OracleDataReader オブジェクトは、ExecuteReader メソッドで取得できます。また、DataSet にデータを格納するために使用する OracleDataAdapterSelectCommand として、1 つ以上の Oracle REF CURSOR を返す OracleCommand を指定することもできます。

Oracle データ ソースから返された REF CURSOR にアクセスするには、クエリ用の OracleCommand を作成し、その Parameters コレクションに、REF CURSOR を参照する出力パラメータを追加します。パラメータの名前は、クエリの REF CURSOR パラメータの名前と一致させる必要があります。パラメータの型を OracleType.Cursor に設定します。OracleCommandExecuteReader メソッドは 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; 

OracleType.Cursor 型の 2 つのパラメータを Parameters コレクションに追加して、上の Oracle パッケージから REF CURSOR を返す OracleCommand を作成するコードを次に示します。

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;

OracleDataReaderRead メソッドと 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 パッケージの結果を DataSet に設定する例を次に示します。

haa3afyz.alert_note(ja-jp,VS.90).gifメモ :

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)