Читать в оригинале

<< ПредыдущаяОглавлениеСледующая >>


4.3.2. Объект Command

Создание и инициализация

 

При определении объектов ADO .NET DataAdapter был назван адаптером, преобразователем, предназначенным для взаимодействия с базой данных. Это действительно так, однако если рассматривать взаимодействие с базой данных более глубоко, то выясняется, что в ADO .NET есть специализированный объект для выполнения запросов, называемый Command. Под запросами понимается выполнение SQL-конструкций или запуск хранимых процедур. Этот объект среда создает неявным образом в методе InitializeComponent. Данный объект обладает следующими свойствами.

Connection - подключение к базе данных. Можно использовать как существующее подключение, так и создавать новое.

CommandType - тип команды (запроса), который будет направляться к базе данных. Возможны следующие значения:

         - Text. Текстовая команда состоит из SQL-конструкции, направляемой к базе данных. Это значение используется по умолчанию.

         - StoredProcedure. Текстовая команда состоит из названия хранимой процедуры.

         - TableDirect. Текстовая команда состоит из названия таблицы базы данных. В результате извлекается все содержимое таблицы. Эта команда аналогична текстовой команде SELECT * FROM Название_таблицы. Данная команда поддерживается только управляемым поставщиком OLE DB.

CommandText - собственно сам текст запроса.

Чтобы выполнить запрос, свойству Connection объекта Command следует задать объект - имя созданного объекта Connection:

// создание, инициализация и открытие объекта Connection OleDbConnection conn = new OleDbConnection();
conn.ConnectionString = ConnectionString; // содержание строки см. раздел 4.1
conn.Open();
 
// создание объекта Command и связывание его с объектом Connection
OleDbCommand myCommand = new OleDbCommand();
myCommand.Connection = conn;

Объект Connection также предоставляет метод CreateCommand, позволяющий упростить данный процесс - этот метод возвращает новый объект Command, уже инициализированный для использования созданного объекта Connection:

OleDbConnection conn = new OleDbConnection();
conn.ConnectionString = ConnectionString;
conn.Open();
OleDbCommand myCommand = conn.CreateCommand();

Эти два способа создания и инициализации объекта Command эквивалентны. Теперь следует определить SQL-запрос, который будет извлекать данные. Как и для строки соединения, будем использовать запрос из предыдущего приложения.

Для хранения текста команды объект myfommand имеет свойство CommandText. Чтобы избежать путаницы, изменим название переменной - CommandText на commandText (с маленькой буквы):

MyCommand.CommandText = commandText;
Создаем объект OleDbDataAdapter:
OleDbDataAdapter dataAdapter = new OleDbDataAdapter();

Объект dataAdapter имеет свойство SelectCommand, в котором мы и будем указывать объект myCommand:

dataAdapter.SelectCommand = myCommand;
Создаем объект DataSet:
DataSet ds = new DataSet();

Заполняем ds данными из dataAdapter:

dataAdapter.Fill(ds, "Туристы");

Указываем источник данных DataSource для dataGrid1:

dataGrid1.DataSource = ds.Tables["Туристы"].DefaultView;

Закрываем соединение явным образом:

conn.Close();

Теперь можно запускать приложение. Получим результат, что и на рисунке 60, но теперь управление всеми объектами, работающими с данными, происходит явно.

Полный листинг конструктора формы выглядит так:

public Form1() {
          InitializeComponent();
          string commandText = "SELECT Фамилия, Имя, Отчество FROM Туристы";
          string ConnectionString = @"Provider=MicrosoftJet.OLEDB.4.0; " +
                        "Data Source=D:\ВМИ\For ADO\BDTur_firm.mdb";
          OleDbConnection conn=new OleDbConnection(ConnectionString);
          conn.Open();
          OleDbCommand MyCommand=new OleDbCommand();
          MyCommand.Connection = conn;
          //или OleDbCommand MyCommand=conn.CreateCommand();
          MyCommand.CommandText = commandText;
          OleDbDataAdapter dataAdapter = new OleDbDataAdapter();
          dataAdapter.SelectCommand = MyCommand;
          DataSet ds = new DataSet();
          dataAdapter.Fill(ds, "Туристы");
          dataGridView1.DataSource = ds.Tables["Туристы"].DefaultView;
          conn.Close();
}

Работа с базой данных SQL аналогична рассмотренному примеру. Отличие заключается в использовании вместо классов OleDbConnection, OleDbCommand, OleDbDataAdapter классов SqlConnection, SqlCommand, SqlDataAdapter соответственно.

Отметим, что оба подхода (визуальный и ручной) к созданию и использованию объектов ADO обладают своими областями применения. Для упрощения разработки и сопровождения визуальных форм проще использовать визуальный подход. При создании приложений, от которых требуется надежность, гибкость или отсутствие экранных форм (например, Web-сервис) лучше создавать объекты ADO вручную.

При визуальном подходе наряду с представлением данных в виде таблицы часто приходится использовать традиционные элементы интерфейса, такие как TextBox, Label и пр. Привязка к элементам интерфейса и средство навигации по записям были рассмотрены выше (см. п. 3.1).

 

Свойства CommandType и CommandText

 

Для демонстрации свойств создадим новое Windows-приложение баз данных. Перетаскиваем на форму элемент управления DataGridView, его свойству Dock устанавливаем значение Fill. В классе формы создаем строки connection- String и commandText:

private string connectionString = @"Data Source = .\SQLEXPRESS; AttachDbFilename = " + @"D: \ВМИ\For ADO\BDTur_firmSQL2.mdf + "integrated Security=True; Connect Timeout=30; User Instance=True";
private string commandText = "SELECT * FROM Туры";

Конструктор формы будет иметь следующий вид:

private string connectionString = @"Data Source = .\SQLEXPRESS; AttachDbFilename = " + @"D:\ВМИ\For ADO\BDTur_firmSQL2.mdf" + ";Integrated Security=True;
Connect Timeout=30; User Instance=True";
private string commandText = "SELECT * FROM Туры";
public Form1() {
          InitializeComponent();
          SqlConnection conn = new SqlConnection(connectionString);
          conn.Open();
          SqlCommand myCommand = conn.CreateCommand();
          //myCommand.Connection = conn;
          myCommand.CommandText = commandText;
          //myCommand.ExecuteNonQuery();
          SqlDataAdapter dataAdapter = new SqlDataAdapter();
          dataAdapter.SelectCommand = myCommand;
          DataSet ds = new DataSet();
          dataAdapter.Fill(ds, "Туры");
          conn.Close();
          dataGridView1.DataSource = ds.Tables["Туры"].DefaultView;
}

Запустим приложение. На форму выводится содержимое таблицы «Туры». Здесь используется всего один объект DataAdapter, который сам открывает и закрывает соединение для получения данных. Конструктор объекта SqlCommand является перегруженным. Полное описание конструктора приведено в MSDN.

 

Метод ExecuteNonQuery

 

Для выполнения запросов на выборку простейших процедур достаточно просто указать тип и передать название запроса или процедуры. Однако этого явно недостаточно для серьезных приложений. Поэтому рассмотрим методы объекта Command.

Метод ExecuteNonQuery применяется для выполнения запросов, не возвращающих данные, таких как UPDATE, INSERT и DELETE - они вносят изменения в таблицу базы данных, не возвращая ничего назад в результате выполнения.

Создадим новое консольное приложение. Ниже приведен его полный листинг:

using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
namespace ConsoleApplication3 {
         class Program {
            static void Main(string[] args) {
                       string connectionString = @"Data Source=.\SQLEXPRESS;AttachDbFilename="+ @"D:\ВМИ\For ADO\BDTur_firmSQL2.mdf" + "integrated Security=True;Connect Timeout=30;User Instance=True"; string commandText = "SELECT * FROM Туры";
                       SqlConnection conn = new SqlConnection(connectionString);
                       conn.Open();
                       SqlCommand myCommand = conn.CreateCommand();
                       myCommand.CommandText = "UPDATE Туристы " + "SET Фамилия = 'Сергеева' WHERE [Код туриста] = 3";
                       myCommand.ExecuteNonQuery();
                       conn.Close();
            }
         }
}

В свойстве CommandText указывается непосредственно текст запроса, который устанавливает значение «Сергеева» поля «Фамилия» для записи с полем «Код Туриста» = 3. Для выполнения запроса просто вызываем метод ExecuteNonQuery:

myCommand.ExecuteNonQuery();

Запускаем приложение, нажимая F5. При успешном выполнении запроса консольное окно приложения появляется и тут же исчезает. Запускаем Management Studio, открываем таблицу «Туристы» и убеждаемся в том, что запись изменилась (рис. 77).

Рис. 77. Таблица «Туристы», изменение записи

Метод ExecuteNonQuery неявно возвращает результат выполнения запроса в виде количества измененных записей. Это значение может применяться для проверки правильности выполнения запроса, например, следующим образом:

int UspeshnoeIzmenenie = myCommand.ExecuteNonQuery();
if (UspeshnoeIzmenenie !=0) {
         Console.WriteLine ("Изменения внесены");
}
else {
         Console.WriteLine(“Не удалось внести изменения");
}

Отметим, что данный вариант текста не позволяет отличить обновление одной строки и нескольких строк.

Закомментируем имеющееся свойство CommandText и добавим новое:

myCommand.CommandText = "INSERT " +
         "INTO Туристы ([Код туриста], Фамилия, Имя, Отчество) " +
         "VALUES (6, 'Тихомиров', 'Андрей', 'Борисович')";

Запускаем приложение, затем переходим в SQL Server Enterprise Manager - запрос добавил новую запись (рис. 78).

Рис. 78. Таблица «Туристы», добавление записи

Снова закомментируем свойство CommandText, добавим теперь запрос на удаление записи:

myCommand.CommandText = "DELETE FROM Туристы WHERE [Код туриста] = 4";

Запускаем приложение - из таблицы удалена четвертая запись (рис. 79). Метод ExecuteNonQuery применяется также для выполнения запросов, относящихся к категории DDL языка SQL. Язык определения данных (Data Definition Language, DDL) позволяет создавать и изменять структуру объектов базы данных, например, создавать и удалять таблицы. Основными операторами этого языка являются CREATE, ALTER, DROP. В результате выполнения запросов DDL не возвращаются данные - именно поэтому можно применять метод ExecuteNonQuery.

Рис. 79. Таблица «Туристы», удаление записи

Закомментируем имеющееся свойство CommandText и напишем новое, создающее в базе BDTur_firm2 новую таблицу «Отзывы»:

myCommand.CommandText = "CREATE TABLE Отзывы (Кодотзыва INT NOT NULL, " +
                      "Кодтуриста INT NOT NULL, Комментарий VARCHAR(50))";

Запускаем приложение, затем переходим в Management Studio, нажимаем кнопку обновить на панели инструментов - в базе появляется новая таблица (рис. 80).

Рис. 80. Свойства таблицы "Отзывы"

Для добавления нового столбца «Отзыв туриста» строка CommandText должна иметь следующий вид:

myCommand.CommandText = "ALTER TABLE Отзывы ADD Отзывтуриста VARCHAR(50)";

В Management Studio в списке столбцов таблицы видим новое поле «Отзывтуриста» (рис. 81).

Рис. 81. База данных BDTur_firm2, новая таблица "Отзывы"

Для удаления таблицы «Отзывы» запускаем приложение, содержащее следующую строку CommandText:

myCommand.CommandText = "DROP TABLE Отзывы";

В Management Studio видно, что таблица полностью исчезла из базы данных. Если нужно лишь удалить данные из таблицы, сохранив структуру, то необходимо воспользоваться следующей командой:

myCommand.CommandText = "DELETE FROM Отзывы";

Объектами базы данных могут быть не только таблицы, но и хранимые процедуры, схемы, представления. В любом случае манипуляция с ними будет относиться к категории DDL.

Метод ExecuteNonQuery может применяться и для выполнения запросов, относящихся к категории DCL. Язык управления данными (Data Control Language, DCL) предназначен для управления доступом (определения полномочий) к объектам базы данных. Основными операторами этого языка являются GRANT, DENY, REVOKE. Данные запросы рассматриваться не будут - использование в данном случае объекта Commnad не отличается ничем от рассмотренного выше.

 

Метод ExecuteScalar

 

Метод ExecuteScalar объекта Command применяется для запросов, возвращающих одно значение. Такие запросы возникают при использовании агрегатных функций COUNT, MIN, MAX. Для демонстрации метода создадим новое консольное приложение. Полный листинг этого приложения:

using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
namespace ConsoleApplication3 {
          class Program {
          static void Main(string[] args) {
            string connectionString = @"Data Source=.\SQLEXPRESS;AttachDbFilename="+
                       @"D:\ВМИ\For ADO\BDTur_firmSQL2.mdf"+
                       "integrated Security=True;Connect Timeout=30;User Instance=True";
            string commandText = "SELECT * FROM Туры";
            SqlConnection conn = new SqlConnection(connectionString);
            conn.Open();
            SqlCommand myCommand = conn.CreateCommand();
            myCommand.CommandText = "SELECT COUNT (*) FROM Туры";
            string KolichestvoTurov = Convert.ToString(myCommand.ExecuteScalar());
            conn.Close();
            Console.WriteLine("Количество туров: " + KolichestvoTurov);
            Console.ReadKey();
          }
          }
}

Возвращаемый методом ExecuteScalar результат приводим к типу string для вывода в окно консоли. Запускаем приложение - запрос вернул число 5 (рис. 82).

Рис. 82. Вывод количества туров

Можно несколько раз применять этот метод.

myCommand.CommandText = "SELECT COUNT (*) FROM Туры";
string KolichestvoTurov = Convert.ToString(myCommand.ExecuteScalar());
myCommand.CommandText = "SELECT MAX (Цена) FROM Туры";
string MaxPrice = Convert.ToString(myCommand.ExecuteScalar());
myCommand.CommandText = "SELECT MIN (Цена) FROM Туры";
string MinPrice = Convert.ToString(myCommand.ExecuteScalar());
myCommand.CommandText = "SELECT AVG (Цена) FROM Туры";
string AvgPrice = Convert.ToString(myCommand.ExecuteScalar());
conn.Close();
Console.WriteLine("Количество туров: " + KolichestvoTurov +
                “\nСамый дорогой тур, цена в руб.: " +
                MaxPrice + “\nСамый дешевый тур, цена в руб.: " +
                MinPrice + "\nСредняя цена туров: " + AvgPrice);

Запускаем приложение и получаем несколько значений из базы данных (рис. 83).

Рис. 83. Вывод нескольких значений

Когда требуется получать подобные одиночные значения, всегда следует применять метод ExecuteScalar. Такое решение позволяет значительно повысить производительность.

 

 

Метод ExecuteReader

 

Теперь перейдем к рассмотрению следующего метода - ExecuteReader. Он применяется для получения набора записей из базы данных. Особенностью этого метода является то, что он возвращает специальный объект DataReader, с помощью которого просматриваются записи. Для хранения данных, полученных из базы, ранее использовался объект DataSet. Объект DataReader, в отличие от DataSet, требует наличия постоянного подключения для извлечения и просмотра данных, кроме того, он открывает данные только для чтения.

Создадим новое консольное приложение. Полный листинг этого приложения:

using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
namespace ConsoleApplication3 {
class Program {
          static void Main(string[] args) {
            string connectionString = @"Data Source=.\SQLEXPRESS;AttachDbFilename="+
                        ©”D:\ВМИ\For ADO\BDTur_firmSQL2.mdf"+ "integrated Security=True;Connect
                        Timeout=30;User Instance=True";
            string commandText = "SELECT * FROM Туры";
            SqlConnection conn = new SqlConnection(connectionString);
            conn.Open();
            SqlCommand myCommand = conn.CreateCommand();
            myCommand.CommandText = "SELECT * FROM Туристы";
            SqlDataReader dataReader = myCommand.ExecuteReader();
            while (dataReader.Read()) {
                        Console.WriteLine(dataReader["Фамилия"]);
            }
            dataReader.Close();
            conn.Close();
            Console.ReadKey();
          }
          }
}

Объект dataReader создается в результате вызова метода ExecuteReader объекта myCommand:

SqlDataReader dataReader = myCommand.ExecuteReader();

Перед считыванием первой записи происходит вызов метода Read объекта dataReader и вывод набора записей в консольное окно. Результат выполнения данного приложения представлен на рисунке 84.

Рис. 84. Вывод поля «Фамилия»

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

Console.WriteLine(dataReader[1]);

Перечислим несколько полей:

Console.WriteLine(dataReader[0]);
Console.WriteLine(dataReader[1]);
Console.WriteLine(dataReader[2]);
Console.WriteLine(dataReader[3]);

При выводе они будут располагаться в структурированном виде (рис. 85).

Рис. 85. Вывод содержимого всех полей

Поскольку мы имеем дело с объектами (тип данных object), для вывода записей в виде строк не применимо их простое объединение:

Console.WriteLine(dataReader[0] + dataReader[1] + dataReader[2] + dataReader[3]);

Преобразованные к типу string значения можно объединять:

Console.WriteLine(Convert.ToString(dataReader[0]) + " " +
                                               Convert.ToString(dataReader[1]) + " " +
                                               Convert.ToString(dataReader[2]) +" " +
                                               Convert.ToString(dataReader[3]));

Теперь записи выводятся в более привычном виде (рис. 86).

Рис. 86. Вывод содержимого всех полей в виде записей

 



<< ПредыдущаяОглавлениеСледующая >>