Поделиться через


Получение данных с помощью DataReader

Чтобы получить данные с помощью DataReader, создайте экземпляр Command объекта, а затем создайте его DataReader , вызвав Command.ExecuteReader для получения строк из источника данных. DataReader предоставляет небуферизованный поток данных, что позволяет процедурной логике эффективно обрабатывать результаты из источника данных последовательно. DataReader — это хороший выбор при извлечении больших объемов данных, поскольку данные не сохраняются в кэше памяти.

В следующем примере показано использование DataReader, где представляет допустимый reader и 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. Вы не можете выполнить какие-либо команды для подключения, включая создание другого DataReader, пока исходный файл DataReader не будет закрыт.

Замечание

Не вызывайте или для Connection, DataReader, или любого другого управляемого объекта в методе вашего класса. В методе завершения следует только освобождать неуправляемые ресурсы, которыми ваш класс непосредственно владеет. Если класс не владеет неуправляемых ресурсов, не включите Finalize метод в определение класса. Дополнительные сведения см. в статье Сборка мусора.

Получение нескольких результирующих наборов с помощью NextResult

Если DataReader возвращает несколько результирующих наборов, вызовите метод NextResult для последовательной итерации через эти наборы. В следующем примере показана SqlDataReader обработка результатов двух инструкций SELECT с помощью ExecuteReader метода.

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

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 adChapter), можно получить с помощью .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 CURSORs

Поставщик данных .NET Framework для Oracle поддерживает использование oracle REF CURSORs для возврата результата запроса. Курсор Oracle REF возвращается в виде OracleDataReader.

Вы можете получить объект, представляющий Oracle REF CURSOR, с помощью метода OracleDataReaderExecuteReader. Можно также указать OracleCommand, который возвращает один или несколько Oracle REF CURSOR в качестве SelectCommand для OracleDataAdapter, используемого для заполнения DataSet.

Чтобы получить доступ к REF CURSOR, возвращенному из источника данных Oracle, создайте OracleCommand для вашего запроса и добавьте выходной параметр, который ссылается на REF CURSOR, в коллекцию Parameters вашего OracleCommand. Имя параметра должно соответствовать имени параметра REF CURSOR в запросе. Задайте для параметра тип OracleType.Cursor. Метод OracleCommand.ExecuteReader() вашего OracleCommand возвращает OracleDataReader для REF CURSOR.

Если ваш OracleCommand возвращает несколько курсоров REF, добавьте соответствующие выходные параметры. Вы можете получить доступ к различным REF CURSOR, вызвав метод OracleCommand.ExecuteReader(). Вызов ExecuteReader() возвращает объект OracleDataReader, ссылающийся на первый REF CURSOR. Затем можно вызвать метод OracleDataReader.NextResult(), чтобы получить доступ к последующим курсорам REF. Хотя параметры в вашей коллекции 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 , который возвращает REF CURSORs из предыдущего пакета Oracle, добавив два параметра типа OracleType.Cursor в коллекцию OracleCommand.Parameters .

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();

В следующем примере предыдущая команда используется для заполнения DataSet данными из пакета Oracle.

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".

См. также