В прошлой теме мы рассмотрели, как выполнять команды с помощью метода ExecuteNonOuery(), однако если мы хотим считывать данные, которые хранятся в таблице, то нам потребуется другой метод – ExecuteReader(). Этот метод возвращает объект SqlDataReader, который используется для чтения данных. Так, получим все данные из таблицы Users и выведем их на консоль:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
static void Main(string[] args) { string connectionString = @"Data Source=.\SQLEXPRESS;Initial Catalog=usersdb;Integrated Security=True"; string sqlExpression = "SELECT * FROM Users"; using (SqlConnection connection = new SqlConnection(connectionString)) { connection.Open(); SqlCommand command = new SqlCommand(sqlExpression, connection); SqlDataReader reader = command.ExecuteReader(); if(reader.HasRows) // если есть данные { // выводим названия столбцов Console.WriteLine("{0}\t{1}\t{2}", reader.GetName(0), reader.GetName(1), reader.GetName(2)); while (reader.Read()) // построчно считываем данные { object id = reader.GetValue(0); object name = reader.GetValue(1); object age = reader.GetValue(2); Console.WriteLine("{0} \t{1} \t{2}", id, name, age); } } reader.Close(); } Console.Read(); } |
Для выборки данных из БД используется sql-выражение SELECT. В данном случае мы выбираем все столбцы всех строк таблицы. Получив при выполнении запроса объект SqlDataReader, мы можем считать все полученные данные.
Но вначале мы проверяем, а есть ли вообще данные с помощью свойства HasRows. Если данные есть, то выводим заголовки таблицы с помощью методов reader.GetName(). Причем мы получаем столбцы в выборке именно в том порядке, в котором они определены в таблицы. То есть если вторым в таблицы идет столбец “Name”, то чтобы получить его столбец применяется метод GetName(1) (так как нумерация столбцов идет с нуля).
Далее считываем сами данные. С помощью метода reader.Read() ридер переходит к следующей строке и возвращает булевое значение, которое указывает, есть ли данные для считывания.
В цикле while (reader.Read()) в порядке следования столбов получаем данные с помощью метода GetValue(), который возвращает данные в виде объекта типа object. Например, столбец Id идет первым и представляет целое число, поэтому для его получения применяется метод reader.GetValue(0). А столбец Name идет вторым, поэтому его значения получаем с помощью reader.GetValue(1).
После завершения работы с SqlDataReader надо его закрыть методом Close(). И пока один SqlDataReader не закрыт, другой объект SqlDataReader для одного и того же подключения мы использовать не сможем.
В качестве альтернативы мы могли бы обращаться к данным через название параметра:
|
1 2 3 4 5 6 7 |
while (reader.Read()) { object id = reader["id"]; object name = reader["name"]; object age = reader["age"]; Console.WriteLine("{0} \t{1} \t{2}", id, name, age); } |
этом случае результат будет аналогичным.
Асинхронное чтение
Для асинхронного чтения, во-первых, применяется метод ExecuteReaderAsync() класса SqlCommand, и во-вторых, метод ReadAsync() класса SqlDataReader:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
static void Main(string[] args) { ReadDataAsync().GetAwaiter(); Console.Read(); } private static async Task ReadDataAsync() { string connectionString = @"Data Source=.\SQLEXPRESS;Initial Catalog=usersdb;Integrated Security=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) { // выводим названия столбцов Console.WriteLine("{0}\t{1}\t{2}", reader.GetName(0), reader.GetName(1), reader.GetName(2)); while (await reader.ReadAsync()) { object id = reader.GetValue(0); object name = reader.GetValue(1); object age = reader.GetValue(2); Console.WriteLine("{0} \t{1} \t{2}", id, name, age); } } reader.Close(); } } |
Типизация результатов
В прошлой теме для получения результатов SqlDataReader использовался метод GetValue, который возвращал значение определенного столбца в текущей ячейки в виде объекта типа object. Однако в ряде случаев такой способ не является оптимальным. Например, мы знаем, что в третьем столбце хранится возраст пользователя, который представляет целое число, и в программе мы хотели бы его использовать как целое число. Так как GetValue возвращает объект типа object, то, чтобы его использовать, к примеру, как число, нам надо его привести к типу int. Однако мы моем выбрать другой путь – использовать типизированные методы. Итак, изменим код программы следующим образом:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
static void Main(string[] args) { string connectionString = @"Data Source=.\SQLEXPRESS;Initial Catalog=usersdb;Integrated Security=True"; string sqlExpression = "SELECT * FROM Users"; using (SqlConnection connection = new SqlConnection(connectionString)) { connection.Open(); SqlCommand command = new SqlCommand(sqlExpression, connection); SqlDataReader reader = command.ExecuteReader(); if(reader.HasRows) // если есть данные { // выводим названия столбцов Console.WriteLine("{0}\t{1}\t{2}", reader.GetName(0), reader.GetName(1), reader.GetName(2)); while (reader.Read()) // построчно считываем данные { int id = reader.GetInt32(0); string name = reader.GetString(1); int age = reader.GetInt32(2); Console.WriteLine("{0} \t{1} \t{2}", id, name, age); } } reader.Close(); } Console.Read(); } |
Для получения данных здесь теперь используются методы GetInt32() и GetString(), которые возвращают объекты типа int и string соответственно. Причем поскольку мы знаем, что в столбце id хранится число, то мы можем получить его именно с помощью метода GetInt32, но никак не GetString. И также в этот метод передается номер столбца в таблице (нумерация опять же начинается с нуля).
Для получения данных каждого примитивного типа есть свой метод:
| Тип sql | Тип .NET | Метод |
| bigint | Int64 | GetInt64 |
| binary | Byte[] | GetBytes |
| bit | Boolean | GetBoolean |
| char | String и Char[] | GetString и GetChars |
| datetime | DateTime | GetDateTime |
| decimal | Decimal | GetDecimal |
| float | Double | GetDouble |
| image и long varbinary | Byte[] | GetBytes и GetStream |
| int | Int32 | GetInt32 |
| money | Decimal | GetDecimal |
| nchar | String и Char[] | GetString и GetChars |
| ntext | String и Char[] | GetString и GetChars |
| numeric | Decimal | GetDecimal |
| nvarchar | String и Char[] | GetString и GetChars |
| real | Single (float) | GetFloat |
| smalldatetime | DateTime | GetDateTime |
| smallint | Intl6 | GetIntl6 |
| smallmoney | Decimal | GetDecimal |
| sql variant | Object | GetValue |
| long varchar | String и Char[] | GetString и GetChars |
| timestamp | Byte[] | GetBytes |
| tinyint | Byte | GetByte |
| uniqueidentifier | Guid | GetGuid |
| varbinary | Byte[] | GetBytes |
| varchar | String и Char[] | GetString и GetChars |