Чтение результатов запроса и SqlDataReader

Последнее обновление: 09.02.2021

В прошлой теме мы рассмотрели, как выполнять команды с помощью метода ExecuteNonOuery()/ExecuteNonOueryAsync(), однако если мы хотим считывать данные, которые хранятся в таблице, то нам потребуется другой метод - ExecuteReader() / ExecuteReaderAsync(). Этот метод возвращает объект SqlDataReader, который используется для чтения данных.

Основные свойства класса SqlDataReader, которые мы можем использовать при получении данных:

  • FieldCount: количество столбцов в текущей строке

  • HasRows: указывает, содержит ли SqlDataReader как минимум одну строку

  • IsClosed: возвращает значение типа bool, которое указывает, закрыт ли данный экземпляр SqlDataReader

  • Item[Int32]: возвращает значение столбца, номер которого передается в квадратных скобках

  • Item[String]: возвращает значение столбца, название которого передается в квадратных скобках

Среди методов класса SqlDataReader, которые применяются при работе с данным классом, следует выделить следующие:

  • Close()/CloseAsync(): закрывает объект SqlDataReader

  • GetValue(Int32): возвращает значение столбца, номер которого передается в качестве параметра (нумерация начинается с нуля)

  • Read(): считывает следующую строку в полученном наборе

  • ReadAsync(): асинхронно считывает следующую строку из полученного набора, одна из версий данного метода в качестве параметра принимает токен отмены CancellationToken

Например, получим все данные из таблицы Users, созданной в прошлой теме, и выведем их на консоль:

using Microsoft.Data.SqlClient;
using System;
using System.Threading.Tasks;

namespace HelloApp
{
    class Program
    {
        static async Task Main(string[] args)
        {
            string connectionString = "Server=(localdb)\\mssqllocaldb;Database=adonetdb;Trusted_Connection=True;";

            string sqlExpression = "SELECT * FROM Users";

            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                await connection.OpenAsync();

                SqlCommand command = new SqlCommand(sqlExpression, connection);
                SqlDataReader reader = await command.ExecuteReaderAsync();

                if (reader.HasRows) // если есть данные
                {
                    // выводим названия столбцов
                    string columnName1 = reader.GetName(0);
                    string columnName2 = reader.GetName(1);
                    string columnName3 = reader.GetName(2);

                    Console.WriteLine($"{columnName1}\t{columnName3}\t{columnName2}");

                    while (await reader.ReadAsync()) // построчно считываем данные
                    {
                        object id = reader.GetValue(0);
                        object name = reader.GetValue(2);
                        object age = reader.GetValue(1);

                        Console.WriteLine($"{id} \t{name} \t{age}") ;
                    }
                }

                await reader.CloseAsync();
            }
            Console.Read();
        }
    }
}

Консольный вывод:

Id		Name		Age		
2		Alice		32		
3		Bob		28		
4		Alex		41		

Для выборки данных из БД используется sql-выражение SELECT. В данном случае мы выбираем все столбцы всех строк таблицы. Получив при выполнении запроса объект SqlDataReader, мы можем считать все полученные данные.

Но вначале мы проверяем, а есть ли вообще данные с помощью свойства HasRows. Если данные есть, то выводим заголовки таблицы с помощью методов reader.GetName(). Причем мы получаем столбцы в выборке именно в том порядке, в котором они определены в таблице.

CREATE TABLE Users (Id INT PRIMARY KEY IDENTITY, Age INT NOT NULL, Name NVARCHAR(20) NOT NULL)

Так, третим в таблице идет столбец "Name", то чтобы получить его столбец применяется метод GetName(2) (так как нумерация столбцов идет с нуля).

Далее считываем сами данные. С помощью метода reader.ReadAsync() ридер переходит к следующей строке и возвращает булевое значение, которое указывает, есть ли данные для считывания.

В цикле while (reader.Read()) в порядке следования столбов получаем данные с помощью метода GetValue(), который возвращает данные в виде объекта типа object. Например, столбец Id идет первым и представляет целое число, поэтому для его получения применяется метод reader.GetValue(0). А столбец Age идет вторым, поэтому его значения получаем с помощью reader.GetValue(1).

После завершения работы с SqlDataReader надо его закрыть методом Close()/CloseAsync(). И пока один SqlDataReader не закрыт, другой объект SqlDataReader для одного и того же подключения мы использовать не сможем.

В качестве альтернативы мы бы могли облачить SqlDataReader в конструкцию using:

using Microsoft.Data.SqlClient;
using System;
using System.Threading.Tasks;

namespace HelloApp
{
    class Program
    {
        static async Task Main(string[] args)
        {
            string connectionString = "Server=(localdb)\\mssqllocaldb;Database=adonetdb;Trusted_Connection=True;";

            string sqlExpression = "SELECT * FROM Users";

            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                await connection.OpenAsync();

                SqlCommand command = new SqlCommand(sqlExpression, connection);
                using (SqlDataReader reader = await command.ExecuteReaderAsync())
                {
                    if (reader.HasRows) // если есть данные
                    {
                        // выводим названия столбцов
                        string columnName1 = reader.GetName(0);
                        string columnName2 = reader.GetName(1);
                        string columnName3 = reader.GetName(2);

                        Console.WriteLine($"{columnName1}\t{columnName3}\t{columnName2}");

						while (await reader.ReadAsync()) // построчно считываем данные
						{
							object id = reader.GetValue(0);
							object name = reader.GetValue(2);
							object age = reader.GetValue(1);

							Console.WriteLine($"{id} \t{name} \t{age}") ;
						}
                    }
                }
            }
            Console.Read();
        }
    }
}

В примере выш используется асинхронное считывание, но также можно использовать синхронное считывание:

while (reader.Read()) // построчно считываем данные
{
	object id = reader.GetValue(0);
	object name = reader.GetValue(2);
	object age = reader.GetValue(1);

	Console.WriteLine($"{id} \t{name} \t{age}") ;
}

Извлечение данных

Выше для извлечения данных из строки использовался метод GetValue(), в который передавался номер столбца. В качестве альтернативы мы могли бы обращаться к данным через индексатор ридера:

while (await reader.ReadAsync())
{
	object id = reader[0];
	object name = reader[2];
	object age = reader[1];

	Console.WriteLine($"{id} \t{name} \t{age}");
}

Однако, мы можем сомневаться в порядке следования столбцов, и в этом случае можно передавать названия столбцов:

while (await reader.ReadAsync())
{
    object id = reader["id"];
    object name = reader["name"];
    object age = reader["age"];
    Console.WriteLine($"{id} \t{name} \t{age}");
}

В этом случае результат будет аналогичным.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850