Технология Microsoft ADO .NET

Свойства и методы объекта Command

Разбить на страницы
Показывать лекцию целиком

Свойства и методы объекта Command

Объект Command. Свойства CommandType и CommandText

При определении объектов ADO .NET мы назвали DataAdapter адаптером, преобразователем, предназначенным для взаимодействия с базой данных. Это действительно так, однако если рассматривать взаимодействие с базой данных более глубоко, то выясняется, что в ADO .NET есть специализированный объект для выполнения запросов, называемый Command. Под запросами понимается выполнение SQL-конструкций или запуск хранимых процедур. С объектом Command мы познакомились еще во второй лекции, но у вас могло сложиться впечатление, что его введение достаточно формально - вполне можно обойтись одним объектом DataAdapter. Например, в приложении VisualDataAdapterSP мы создавали и выполняли хранимые процедуры, настраивая DataAdapter без всякого объекта Command! В действительности, среда создала его неявным образом - открываем метод InitializeComponent и видим объявление sqlSelectCommand1, а также описание нескольких его свойств:

private void InitializeComponent()
	{
...
this.sqlSelectCommand1 = new System.Data.SqlClient.SqlCommand();
		...
// 
// sqlSelectCommand1
// 
this.sqlSelectCommand1.CommandText = "[proc_da1]";
this.sqlSelectCommand1.CommandType = System.Data.CommandType.StoredProcedure;
this.sqlSelectCommand1.Connection = this.sqlConnection1;
this.sqlSelectCommand1.Parameters.Add(new System.Data.SqlClient.SqlParameter
 ("@RETURN_VALUE", System.Data.SqlDbType.Int, 4, 
 System.Data.ParameterDirection.ReturnValue, false, ((System.Byte)(0)),
 ((System.Byte)(0)), "", System.Data.DataRowVersion.Current, null));
...

Нам нужно научиться понимать это описание и создавать его программным образом. Среда Visual Studio .NET содержит инструменты для визуальной настройки объекта Command, и поскольку разобраться в них проще, чем в коде, начнем с них. Создайте новое Windows-приложение и назовите его "VisualCommand". Добавляем элемент управления DataGrid, его свойству Dock устанавливаем значение Fill. В окне ToolBox переходим на вкладку Data, перетаскиваем на форму объект SqlCommand (рис. 6.1):

(рис 6.1) Окно Toolbox, вкладка Data

Переходим в окно Properties появившегося объекта sqlCommand1, в свойстве "Connection" из выпадающего списка выбираем создание нового подключения (New) (рис. 6.2).

(рис 6.2) Свойство "Connection" объекта sqlCommand1

В окне "Свойства связи с данными" настраиваем подключение к базе данных BDTur_firm2 (см. рис. 5.18). На панели компонент появляется объект sqlConnection1 - если бы мы вначале создали и настроили подключение, то в свойстве Connection объекта sqlCommand1 можно было выбрать "Existing/sqlConnection1". Свойство CommandType указывает на тип команды (запроса), который будет направляться к базе данных (рис. 6.3):

(рис 6.3) Свойство CommandType объекта sqlCommand1

Возможны следующие значения:

  • Text. Текстовая команда состоит из SQL-конструкции, направляемой к базе данных. Это значение используется по умолчанию.
  • StoredProcedure. Текстовая команда состоит из названия хранимой процедуры.
  • TableDirect. Текстовая команда состоит из названия таблицы базы данных. В результате извлекается все содержимое таблицы. Эта команда аналогична текстовой команде SELECT * FROM Название_таблицы. Команда TableDirect поддерживается только управляемым поставщиком OLE DB.
  • Оставляем пока значение по умолчанию. В поле свойства (...) (рис. 6.4):

    (рис 6.4) Свойство CommandText объекта sqlCommand1

    В появившемся построителе выражений настраиваем извлечение содержимого таблицы "Туры". Обратите внимание на то, что в поле свойства CommandText появилась SQL-конструкция:

    SELECT Туры.* FROM Туры

    Снова в окне Toolbox переходим на вкладку Data, перетаскиваем на форму объект SqlDataAdapter. В мастере "Data Adapter Configuration Wizard" нажимаем кнопку "Cancel" - у нас уже есть подключение и настроенный объект sqlCommand1, достаточно указать в свойстве Select Command объект sqlCommand1 (рис. 6.5):

    (рис 6.5) Свойство SelectCommand объекта sqlDataAdapter1

    Выделяем sqlDataAdapter1, создаем DataSet, нажимая на ссылку "Generate Dataset". В окне "Generate Dataset" оставляем название "DataSet1", нажимаем "OK". В выпадающем списке свойства DataSource элемента управления DataGrid выбираем "datSet11" в качестве источника данных (рис. 6.6):

    (рис 6.6) Свойство DataSource элемента dataGrid1

    Наконец в конструкторе формы вызываем метод Fill объекта DataAdapter:

    public Form1()
    		{
    			InitializeComponent();
    			sqlDataAdapter1.Fill(dataSet11);
    		}

    Запускаем приложение. На форму выводится содержимое таблицы "Туры" (рис. 6.7):

    (рис 6.7) Приложение VisualCommand, вывод таблицы "Туры"

    Установим теперь в свойстве CommandType объекта Command значение "StoredProcedure", а в свойстве CommandText введем название имеющейся хранимой процедуры - "[proc10]" (рис. 6.8):

    (рис 6.8) Настройка свойств объекта sqlCommand1 для вывода процедуры [proc10]

    Чтобы избежать проблем со структурой DataSet, удалим имеющийся объект с панели компонент. Дополнительно в окне Solution Explorer удаляем файл DataSet1.xsd. Снова генерируем DataSet и определяем его в качестве источника данных для элемента DataGrid. Запускаем приложение - на форму выводится результат выполнения хранимой процедуры (рис. 6.9):

    (рис 6.9) Приложение VisualCommand, извлечение хранимой процедуры proc10

    Таким образом можно извлекать данные из таблиц при помощи SQL-конструкций и запускать хранимые процедуры таблицы 5.1. Поставщик данных SQL Server не поддерживает значение TableDirect свойства CommandType - при его выборе появляется диагностическое сообщение об ошибке. Это значение можно применять при подключении к базе данных Microsoft Access.

    В программном обеспечении к курсу вы найдете приложение Visual Command (Code\Glava3\ VisualCommand).

    Перейдем теперь к реализации аналогичного приложения без применения визуальных средств студии для объектов ADO .NET. Создайте новое Windows-приложение и назовите его "ProgrammCommand". Перетаскиваем на форму элемент управления DataGrid, его свойству Dock устанавливаем значение "Fill". В классе формы создаем строки connectionString и commandText:

    string connectionString = "integrated security=SSPI;
     data source=\".\"; persist security info=False;
     initial catalog=BDTur_firm2";
    string commandText = "SELECT * FROM Туры";

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

    public Form1()
    {		
    	InitializeComponent();
    	SqlConnection conn = new SqlConnection(connectionString);
    	SqlCommand myCommand = new SqlCommand();
    	myCommand.Connection = conn;
    	myCommand.CommandText = commandText;
    	SqlDataAdapter dataAdapter = new SqlDataAdapter();
    	dataAdapter.SelectCommand = myCommand;
    	DataSet ds = new DataSet();
    	conn.Open();
    	dataAdapter.Fill(ds, "Туры");
    	conn.Close();
    	dataGrid1.DataSource = ds;
    }

    Запускаем приложение. На форму выводится содержимое таблицы "Туры" (см. рис. 6.7). Здесь мы используем всего один объект DataAdapter, который сам открывает и закрывает соединение для получения данных. Поэтому можно не вызывать методы Open и Close объекта conn. Конструктор объекта SqlCommand может принимать значения, указанные в таблице 6.1.

    Конструктор объекта SqlCommandКонструктор объекта OleDbCommand в точности такой же.
    Вид
    1
    Пример Приводится только создание объекта SqlTransaction, без его методов, - см далее Лекцию 7, тему "Работа с транзакциями". Описание
    public Form1()
    {		
    InitializeComponent();
    SqlConnection  conn =
     new SqlConnection(connectionString);
    SqlTransaction trans = conn.BeginTransaction();
    SqlCommand myCommand =
     new SqlCommand(commandText, conn, trans);
    //SqlCommand  myCommand = 
    // new SqlCommand("SELECT * FROM Туры", conn, trans);
    SqlDataAdapter dataAdapter = new SqlDataAdapter();
    dataAdapter.SelectCommand = myCommand;
    DataSet ds = new DataSet();
    dataAdapter.Fill(ds, "Туры");
    dataGrid1.DataSource = ds;		
    }
    Первый параметр - строка commandText, причем можно сразу задавать SQL-конструкцию без создания строки в классе. Затем экземпляры объектов SqlConnection и SqlTransaction. Подробно о транзакциях см. далее
    Вид
    2
    Пример Описание
    public Form1()
    {		
    InitializeComponent();
    SqlConnection  conn =
     new SqlConnection(connectionString);
    SqlCommand  myCommand =
     new SqlCommand(commandText, conn);
    //SqlCommand  myCommand =
    // new SqlCommand("SELECT * FROM Туры", conn);
    SqlDataAdapter dataAdapter = new SqlDataAdapter();
    dataAdapter.SelectCommand = myCommand;
    DataSet ds = new DataSet();
    dataAdapter.Fill(ds, "Туры");
    dataGrid1.DataSource = ds;	
    }
    Вариант без экземпляра объекта SqlTransaction
    Вид
    3
    Пример Описание
    public Form1()
    {
    SqlConnection  conn =
     new SqlConnection(connectionString);
    SqlCommand  myCommand =
     new SqlCommand(commandText);
    //SqlCommand  myCommand =
    //  new SqlCommand("SELECT * FROM Туры");
    myCommand.Connection = conn;
    SqlDataAdapter dataAdapter =
     new SqlDataAdapter();
    dataAdapter.SelectCommand = myCommand;
    DataSet ds = new DataSet();
    dataAdapter.Fill(ds, "Туры");
    dataGrid1.DataSource = ds;	
    }
    Конструктор принимает в качестве параметра только строку commandText. Для указания соединений используем свойство Connection созданного экзмепляра myCommand
    Вид
    4
    Пример Описание
    public Form1()
    {
    SqlConnection  conn =
     new SqlConnection(connectionString);
    SqlCommand  myCommand = new SqlCommand();
    myCommand.Connection = conn;
    myCommand.CommandText = commandText;
    //Command.CommandText = "SELECT * FROM Туры";
    SqlDataAdapter dataAdapter = new SqlDataAdapter();
    dataAdapter.SelectCommand = myCommand;
    DataSet ds = new DataSet();
    dataAdapter.Fill(ds, "Туры");
    dataGrid1.DataSource = ds;
    }
    Конструктор не принимает параметров. Для указания соединения и строки commandText вызываем соответствующие свойства созданного экземпляра myCommand. Строке commandText можно передать SQL-конструкцию непосредственно - в этом случае создавать переменную в классе не нужно

    Первый вариант конструктора обеспечивает самую компактную форму записи. Его можно использовать при работе с одним соединением с базой данных. В случае работы с несколькими соединениями удобнее определить значения командных строк в классе формы (или вообще в отдельном классе) и использовать четвертый вариант. При изменении параметров подключения нужно будет исправить только сами строки, не затрагивая код, относящийся к объекту Command.

    Для вывода хранимой процедуры следует указать значение "Stored Procedure" свойства CommandType:

    public Form1()
    {
    SqlConnection conn =
     new SqlConnection(connectionString);
    SqlCommand myCommand =
     new SqlCommand();
    myCommand.Connection = conn;
    myCommand.CommandType =
     CommandType.StoredProcedure;
    myCommand.CommandText = "[proc10]";
    SqlDataAdapter dataAdapter =
     new SqlDataAdapter();
    dataAdapter.SelectCommand = myCommand;
    DataSet ds = new DataSet();
    dataAdapter.Fill(ds, "Туры");
    dataGrid1.DataSource = ds;
    }

    Название процедуры можно также присвоить переменной commandText, созданной в классе формы.

    В программном обеспечении к курсу вы найдете приложение Programm Command (Code\Glava3\ ProgrammCommand).

    Методы ExecuteNonQuery, ExecuteScalar и ExecuteReader объекта Command

    Мы рассмотрели всю возможную функциональность, которую можно получить без использования каких-либо методов объекта Command. Для выполнения запросов на выборку простейших процедур достаточно просто указать тип и передать название запроса или процедуры. Все работает, но этого явно недостаточно для серьезных приложений. Поэтому забудем пока про хранимые процедуры, другие объекты ADO .NET, и сосредоточим все внимание на методах объекта Command.

    Метод ExecuteNonQuery применяется для выполнения запросов, не возвращающих данные. Как же запросы, предназначенные именно для извлечения данных, могут не возвращать их? Речь идет о запросах типа UPDATE, INSERT и DELETE - в самом деле, они вносят изменения в таблицу базы данных, не возвращая ничего назад в результате выполнения. В самом языке SQL эти запросы образуют категорию DML (Data Manipulation Language, DML). Дословный перевод названия категории - "язык манипулирования данными", наличие слова "язык" вносит небольшую путаницу: в языке SQL есть язык DML. Но это именно так. После небольшого опыта подобные названия станут привычными.

    Создайте новое консольное приложение и назовите его "Example ExecuteNonQuery". Привожу его полный листинг:

    using System;
    using System.Data.SqlClient;
    
    namespace ExampleExecuteNonQuery
    {	
    	class Class1
    	{	
    		[STAThread]
    		static void Main(string[] args)
    		{
    			SqlConnection conn = new SqlConnection();
    			conn.ConnectionString = "integrated security=SSPI;
    			 data source=\".\"; 
    			 persist security info=False; initial catalog=BDTur_firm2";
    			conn.Open();
    			SqlCommand myCommand = conn.CreateCommand();
    			myCommand.CommandText = "UPDATE Туристы 
    			 SET Фамилия = 'Сергеева'
    			 WHERE Кодтуриста = 3";
    			myCommand.ExecuteNonQuery();
    			conn.Close();
    		}
    	}
    }

    Объект Command можно создавать, вызывая метод CreateCommand объекта Connection:

    SqlCommand myCommand = conn.CreateCommand();

    Ранее мы пользовались следующим способом:

    SqlCommand myCommand = new SqlCommand();
    myCommand.Connection = conn;

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

    myCommand.ExecuteNonQuery();

    Запускаем приложение, нажимая Ctrl+F5. При успешном выполнении запроса в консольном окне появляется надпись "Press any key to continue" (рис. 6.10):

    (рис 6.10) Выполнение приложения ExampleExecuteNonQuery

    Запускаем SQL Server Enterprise Manager, открываем таблицу "Туристы" и убеждаемся в том, что запись изменилась (рис. 6.11).

    (рис 6.11) Таблица "Туристы", изменение записи

    Метод ExecuteNonQuery все-таки что-то неявно возвращает - результат выполнения запроса в виде количества измененных записей; это может применяться для проверки (изменим фрагмент кода):

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

    Теперь, в зависимости от результата, будет появляться соответствующее сообщение (рис. 6.12).

    (рис 6.12) Результаты проверки выполнения запроса

    Неудачный результат можно получить, установив в тексте запроса значения поля "Код туриста", равное, скажем, 10.

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

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

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

    (рис 6.13) Таблица "Туристы", добавление записи

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

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

    Запускаем приложение - из таблицы удалена четвертая запись (рис. 6.14):

    (рис 6.14) Таблица "Туристы", удаление записи

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

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

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

    (рис 6.15) База данных BDTur_firm2, новая таблица "Отзывы"

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

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

    В SQL Server Enterprise Manager дважды щелкаем по названию таблицы - в появившемся окне "Table Properties" видим новое поле "Отзывтуриста" (рис. 6.16):

    (рис 6.16) Свойства таблицы "Отзывы"

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

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

    Переходим в SQL Server Enterprise Manager - таблица полностью исчезла из базы данных. Если бы нам нужно было лишь удалить данные из таблицы, сохранив структуру, мы бы воспользовались следующей командой:

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

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

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

    В программном обеспечении к курсу вы найдете приложение Example ExecuteNonQuery (Code\Glava3\ ExampleExecuteNonQuery).

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

    using System;
    using System.Data.SqlClient;
    
    namespace ExampleExecuteScalar
    {
    	class Class1
    	{
    		[STAThread]
    		static void Main(string[] args)
    		{
    			SqlConnection conn = new SqlConnection();
    			conn.ConnectionString = "integrated security=SSPI;data 
    			 source=\".\"; persist security info=False;
    			 initial catalog=BDTur_firm2";
    			conn.Open();
    			SqlCommand myCommand = conn.CreateCommand();
    			// SqlCommand myCommand = new SqlCommand();
    			// myCommand.Connection = conn;
    			myCommand.CommandText = "SELECT
    			 COUNT (*) FROM Туры";
    			string KolichestvoTurov = Convert.ToString(myCommand.ExecuteScalar());
    			conn.Close();
    			Console.WriteLine("Количество туров: " + 
    			 KolichestvoTurov);
    		}
    	}
    }

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

    (рис 6.17) Приложение ExampleExecuteScalar, вывод количества туров

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

    ...
    	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);
     }

    Запускаем приложениеСкорее всего, у вас будет измененное содержимое таблицы "Туры" - если, конечно, вы выполняли все действия, описанные в этой главе. Для получения исходной таблицы запустите SQL Server Enterprise Manager, удалите имеющуюся таблицу "Туры" и импортируйте ее снова из файла BDTur_firm2.mdb ((Code\Glava3\ BDTur_firm2.mdb). - получаем несколько значений из базы данных (рис. 6.18):

    (рис 6.18) Приложение ExampleExecuteScalar, вывод нескольких значений

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

    В программном обеспечении к курсу вы найдете приложение Example ExecuteScalar (Code\Glava3\ ExampleExecuteScalar).

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

    using System;
    using System.Data.SqlClient;
    
    namespace ExampleExecuteReader
    {
    	class Class1
    	{
    		[STAThread]
    		static void Main(string[] args)
    		{
    			SqlConnection conn = new SqlConnection();
    			conn.ConnectionString = "integrated security=SSPI;data 
    			 source=\".\"; persist security info=False;
    			 initial catalog=BDTur_firm2";
    			SqlCommand myCommand = conn.CreateCommand();
    			//SqlCommand myCommand = new SqlCommand();
    			//myCommand.Connection = conn;
    			myCommand.CommandText = "SELECT *
    			 FROM Туристы";
    			conn.Open();
    			SqlDataReader dataReader = myCommand.ExecuteReader();
    			
    			while (dataReader.Read())
    			{
    				Console.WriteLine(dataReader["Фамилия"]);
    			}
    			dataReader.Close();			
    			conn.Close();			
    		}
    	}
    }

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

    SqlDataReader dataReader = myCommand.ExecuteReader();

    Перед считыванием первой записи вызываем метод Read объекта dataReader и выводим набор записей в консольное окно. Запускаем приложениеТаблица "Туристы" была заменена на оригинальную из базы BDTur_firm2.mdb. (рис. 6.19).

    (рис 6.19) Приложение "ExampleExecuteReader". Вывод поля "Фамилия"

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

    Console.WriteLine(dataReader[1]);

    Это совершенно эквивалентная строка выводаИспользование индексов повышает производительность приложения..

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

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

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

    (рис 6.20) Приложение ExampleExecuteReader, вывод содержимого всех полей

    Поскольку мы имеем дело с объектами (тип данных 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]));

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

    (рис 6.21) Приложение ExampleExecuteReader. Вывод содержимого всех полей в виде записей

    В программном обеспечении к курсу вы найдете приложение "Example ExecuteReader" (Code\Glava3\ ExampleExecuteReader).

    Применение методов объекта Command в Windows-приложениях. Параметризированные запросы

    Мы рассмотрели основные методы объекта Command в консольных приложениях. Это дало нам возможность понять синтаксис самих методов, без привязки к какому-либо интерфейсу. Однако, после того как синтаксис стал ясен, возникает вопрос - как же использовать эти методы в реальных приложениях? Понятно, что простое копирование кода в конструктор формы, по сути, не изменит пример. Следовательно, мы должны привязывать вводимые значения, например, к текстовым полям. Но это означает, что параметры строки запроса будут неизвестны до тех пор, пока пользователь не введет соответствующие значения. Например, для метода ExecuteNonQuery строка commandText имела следующий вид:

    myCommand.CommandText = "UPDATE Туристы SET
     Фамилия = 'Сергеева' WHERE Кодтуриста = 3";

    Если мы создадим приложение, где пользователь будет вводить фамилию и код туриста, то мы не можем заранее указать, какие это будут значения. Логически запрос можно представить примерно так:

    myCommand.CommandText = "UPDATE Туристы SET
     Фамилия = 'Какая-то_фамилия,_которую_введет_пользователь'
     WHERE Кодтуриста = Какой-то_код,_который_введет_пользователь";

    Для решения таких задач, которые возникли еще в самом начале разработки языка SQL, были придуманы параметризированные запросы. В них неизвестные значения заменяются параметрами. Вот так:

    myCommand.CommandText = "UPDATE Туристы SET
     Фамилия = @Family WHERE Кодтуриста = @TouristID";

    Здесь @Family (обратите внимание, пишется без кавычек!) - параметр для неизвестного значения фамилии, @TouristID - параметр для неизвестного значения кода туриста. Теперь мы можем привязывать параметры к тексту, вводимому пользователем. Создайте новое Windows-приложение и назовите его "ExamWinExecuteNonQuery". Устанавливаем следующие свойства формы:

    Form1, форма, свойствоЗначение
    FormBorderStyle FixedSingle
    MaximizeBox False
    Size 620; 240

    Добавляем на форму элементы управления и устанавливаем их свойства:

    gropBox1, свойствоЗначение
    Location 16; 16
    Size 296; 112
    Text Пример UPDATE
    gropBox2, свойство Значение
    Location 320; 16
    Size 280; 176
    Text Пример INSERT
    gropBox3, свойство Значение
    Location 16; 136
    Size 296; 56
    Text Пример DELETE
    button1, свойство Значение
    Name btnUpdate
    Location 80; 80
    Text Обновить
    button2, свойство Значение
    Name btnInsert
    Location 88; 144
    Text Добавить
    button3, свойство Значение
    Name BtnDelete
    Location 208; 24
    Text Удалить
    textBox1, свойство Значение
    Name TxtTouristIDUpdate
    Location 16; 24
    Size 224; 20
    Text Введите код туриста
    textBox2, свойство Значение
    Name TxtFamilyUpdate
    Location 16; 56
    Size 224; 20
    Text Введите фамилию туриста
    textBox3, свойство Значение
    Name TxtTouristIDInsert
    Location 16; 24
    Size 224; 20
    Text Введите код туриста
    textBox4, свойство Значение
    Name TxtFamilyInsert
    Location 16; 56
    Size 224; 20
    Text Введите фамилию туриста
    textBox5, свойство Значение
    Name TxtFirstNameInsert
    Location 16; 88
    Size 224; 20
    Text Введите имя туриста
    textBox6, свойство Значение
    Name TxtMiddleNameInsert
    Location 16; 120
    Size 224; 20
    Text Введите отчество туриста
    textBox7, свойство Значение
    Name txtTouristIDDelete
    Location 8; 24
    Size 192; 20
    Text Введите код туриста для удаления

    Форма в режиме дизайна будет иметь следующий вид (рис. 6.22):

    (рис 6.22) Приложение ExamWinExecuteNonQuery, вид формы в режиме дизайна

    Подключаем пространство имен для работы с базой данных:

    using System.Data.SqlClient;

    В классе формы создаем экземпляр connМы это делаем для включения блока обработки исключений.:

    SqlConnection conn = null;

    Обработчик кнопки btnUpdate будет иметь следующий вид:

    private void btnUpdate_Click(object sender, System.EventArgs e)
    {
    	try
    	{
    		//Создаем переменную Family, в которую помещаем значение,
    		//введенное пользователем в поле txtFamilyUpdate:
    		string Family = Convert.ToString(this.txtFamilyUpdate.Text);
    		//Создаем переменную TouristID, в которую помещаем значение,
    		//введенное пользователем в поле txtTouristIDUpdate:
    		int TouristID = int.Parse(this.txtTouristIDUpdate.Text);
    		conn = new SqlConnection();
    		conn.ConnectionString = "integrated security=SSPI;data 
    		 source=\".\"; persist security info=False;
    		 initial catalog=BDTur_firm2";
    		conn.Open();
    		SqlCommand myCommand = conn.CreateCommand();
    		myCommand.CommandText = "UPDATE Туристы
    		 SET Фамилия = @Family WHERE Кодтуриста = @TouristID";
    		//Добавляем параметр @Family в коллекцию параметров 
    		//объекта myCommand
    		myCommand.Parameters.Add("@Family",
    		 SqlDbType.NVarChar, 50);
    		//Устанавливаем значение параметра @Family
    		//равным значению переменной Family
    		myCommand.Parameters["@Family"].Value = Family;
    		//Добавляем параметр @TouristID в коллекцию параметров 
    		//объекта myCommand
    		myCommand.Parameters.Add("@TouristID", SqlDbType.Int, 4);
    		//Устанавливаем значение параметра @TouristID 
    		//равным значению переменной TouristID
    		myCommand.Parameters["@TouristID"].Value = TouristID;
    		int UspeshnoeIzmenenie = myCommand.ExecuteNonQuery();
    		if (UspeshnoeIzmenenie !=0)
    		{
    			MessageBox.Show("Изменения внесены", 
    			 "Изменение записи");
    		}
    		else
    		{
    			MessageBox.Show("Не удалось внести изменения", 
    			 "Изменение записи");
    		}
    
    	}
    	catch(Exception ex)
    	{
    		MessageBox.Show(ex.ToString());
    	}
    	finally
    	{
    		conn.Close();
    	}
    }

    Обратите внимание - в блоке finally мы закрываем соединение, это нужно сделать в любом случае, независимо от результата выполнения команды. Значения, введенные пользователем в текстовые поля txtFamilyUpdate и txtTouristIDUpdate, помещаются в переменные Family и TouristID. В запросе к базе данных используются два параметра - @Family и @TouristID. Мы добавляем их в коллекцию объекта Command, используя метод Add свойства Parameters, а затем устанавливаем значения параметров равными переменным Family и TouristID. Конструктор метод Add перегружен, первый вариант принимает наибольшее количество свойствВообще-то, раньше свойства, входящие в какой-либо метод, мы называли параметрами. Здесь я не стал использовать это слово, чтобы не было путаницы с параметрами, входящими в коллекцию объекта Command. (рис. 6.23):

    (рис 6.23) Конструктор метода Add свойства Parameters объекта Command

    Описание некоторых свойств метода Add приводится в таблице 6.2.

    Свойства метода Add
    Свойство Описание
    parameterName Название параметра
    sqlDbType Тип данных передаваемого параметра
    size Размер параметра
    sourceColumn Название имени столбца объекта DataSet, на который ссылается данный параметр

    Конструктор метода Add свойства Parameters объекта Command для поставщика данных OLE DB имеет в точности такую же структуру (рис. 6.24):

    (рис 6.24) Конструктор метода Add свойства Parameters объекта Command поставщика данных OLE DB

    Добавляем обработчик кнопки btnInsert:

    private void btnInsert_Click(object sender, System.EventArgs e)
    {
    	try
    	{
    		int TouristID = int.Parse(this.txtTouristIDInsert.Text);
    		string Family = Convert.ToString(this.txtFamilyInsert.Text);
    		string FirstName = Convert.ToString(this.txtFirstNameInsert.Text);
    		string MiddleName = Convert.ToString(this.txtMiddleNameInsert.Text);
    		conn = new SqlConnection();
    		conn.ConnectionString = "integrated security=SSPI;data 
    		 source=\".\"; persist security info=False;
    		 initial catalog=BDTur_firm2";
    		conn.Open();
    		SqlCommand myCommand = conn.CreateCommand();
    		myCommand.CommandText =
    		 "INSERT INTO Туристы (Кодтуриста, Фамилия, Имя, Отчество)
    		 VALUES (@TouristID, @Family, @FirstName, @MiddleName)";
    		myCommand.Parameters.Add("@TouristID", SqlDbType.Int, 4);
    		myCommand.Parameters["@TouristID"].Value = TouristID;
    		myCommand.Parameters.Add("@Family", SqlDbType.NVarChar, 50);
    		myCommand.Parameters["@Family"].Value = Family;
    		myCommand.Parameters.Add("@FirstName",
    		 SqlDbType.NVarChar, 50);
    		myCommand.Parameters["@FirstName"].Value = FirstName;
    		myCommand.Parameters.Add("@MiddleName",
    		 SqlDbType.NVarChar, 50);
    		myCommand.Parameters["@MiddleName"].Value = MiddleName;
    		int UspeshnoeIzmenenie = myCommand.ExecuteNonQuery();
    		if (UspeshnoeIzmenenie !=0)
    		{
    			MessageBox.Show("Изменения внесены",
    			 "Изменение записи");
    		}
    		else
    		{
    			MessageBox.Show("Не удалось внести изменения",
    			 "Изменение записи");
    		}
    
    	}
    	catch(Exception ex)
    	{
    		MessageBox.Show(ex.ToString());
    	}
    	finally
    	{
    		conn.Close();
    	}
    }

    В запросе используются четыре параметра: @TouristID, @Family, @FirstName, @MiddleName. Тип данных создаваемых параметров соответствует типу данных полей таблицы "Туристы" в базе.

    Добавляем обработчик кнопки btnDelete:

    private void btnDelete_Click(object sender, System.EventArgs e)
    {
    	try
    	{
    		int TouristID = int.Parse(this.txtTouristIDDelete.Text);
    		conn = new SqlConnection();
    		conn.ConnectionString = "integrated security=SSPI;data
    		 source=\".\"; persist security info=False;
    		 initial catalog=BDTur_firm2";
    		conn.Open();
    		SqlCommand myCommand = conn.CreateCommand();
    		myCommand.CommandText = "DELETE FROM Туристы
    		 WHERE Кодтуриста = @TouristID";
    		myCommand.Parameters.Add("@TouristID", SqlDbType.Int, 4);
    		myCommand.Parameters["@TouristID"].Value = TouristID;
    		int UspeshnoeIzmenenie = myCommand.ExecuteNonQuery();
    		if (UspeshnoeIzmenenie !=0)
    		{
    			MessageBox.Show("Изменения внесены", 
    			 "Изменение записи");
    		}
    		else
    		{
    			MessageBox.Show("Не удалось внести изменения",
    			 "Изменение записи");
    		}
    
    	}
    	catch(Exception ex)
    	{
    		MessageBox.Show(ex.ToString());
    	}
    	finally
    	{
    		conn.Close();
    	}
    }

    Запускаем приложение. В каждой из групп заполняем поля, затем нажимаем на кнопки. Проверять результат можно, запуская SQL Server Enterprise Manager и просматривая каждый раз содержимое таблицы "Туристы" (рис. 6.25):

    (рис 6.25) Готовое приложение ExamWinExecuteNonQuery. Таблицы взяты из SQL Server Enterprise Manager

    В программном обеспечении к курсу вы найдете приложение Exam WinExecuteNonQuery (Code\Glava3\ ExamWinExecuteNonQuery).

    Применять метод ExecuteScalar объекта Command в Windows-приложениях очень легко - достаточно указать элемент управления (текстовое поле, надпись) для вывода одиночного значения.

    Рассмотрим теперь метод (...) (рис. 6.26). В редакторе "Column Header Collection Editor" добавляем следующие четыре элемента:

    NameText
    chTouristID Код туриста
    NameText
    chFamily Фамилия
    NameText
    chFirstName Имя
    NameText
    chMiddleName Отчество
    (рис 6.26) Свойство Columns элемента ListView и редактор "ColumnHeader Collection Editor"

    Для отображения созданных столбцов свойству View элемента устанавливаем значение "Details". Также включим режим отображения линий сетки - в свойстве GridLines выбираем значение "True". Сделанные изменения немедленно отобразятся на элементе.

    Подключаем пространство имен для работы с базой:

    using System.Data.SqlClient;

    В классе формы создаем объекты conn и dataReader:

    SqlConnection conn = null;
    SqlDataReader dataReader;

    В конструкторе формы добавляем код для заполнения данными элементов управления:

    public Form1()
    {
    	InitializeComponent();
    	try
    	{
    		conn = new SqlConnection();
    		conn.ConnectionString = "integrated security=SSPI;data 
    		 source=\".\"; persist security info=False; initial
    		 catalog=BDTur_firm2";
    		conn.Open();
    		SqlCommand myCommand = conn.CreateCommand();
    		myCommand.CommandText = "SELECT * FROM Туристы";
    		dataReader = myCommand.ExecuteReader();
    		while (dataReader.Read())
    		{
    			// Создаем переменные, получаем для них значения 
    			//из объекта dataReader, используя метод GetТипДанных
    			int TouristID = dataReader.GetInt32(0);
    			string Family = dataReader.GetString(1);
    			string FirstName = dataReader.GetString(2);
    			string MiddleName = dataReader.GetString(3);
    			//Выводим данные в элемент listBox1
    			listBox1.Items.Add("Код туриста: " + TouristID+
    			 " Фамилия: " + Family + " Имя: "+
    			 FirstName + " Отчество: " + MiddleName);
    			//Создаем экземпляр item класса ListViewItem 
    			//для записи в него данных из dataReader
    			ListViewItem item =
    			 new ListViewItem(new string[]{Convert.ToString(dataReader[0]), 
    			 Convert.ToString(dataReader[1]), Convert.ToString(dataReader[2]), 
    			 Convert.ToString(dataReader[3])});
    			listView1.Items.Add(item);				
    		}
    	}
    	catch(Exception ex)
    	{
    		MessageBox.Show(ex.ToString());
    	}
    	finally
    	{
    		dataReader.Close();			
    		conn.Close();
    	}
    }

    Метод "GetТипДанных" позволяет приводить значения, возвращаемые объектом DataReader, если заранее известен их тип данных. Запускаем приложение. На форму выводятся данные в виде списка в элементе ListBox и в виде таблицы в элементе ListView (рис. 6.27):

    (рис 6.27) Приложение ExamWinExecuteReader

    В программном обеспечении к курсу вы найдете приложение ExamWin ExecuteReader (Code\Glava3\ ExamWinExecuteReader).

    Вывод данных в элемент ListView приводит к достаточно удовлетворительному результату, однако более привычным для нас является вывод в элемент DataGrid. Раньше, при использовании объекта DataSet, мы указывали источник данных для элемента DataGrid так:

    dataGrid1.DataSource = dataset.Tables["Название_таблицы"].DefaultView;

    Или так:

    dataGrid1.DataSource = dataset;

    Объект DataReader не поддерживает аналогичного вывода - мы не можем связать объекты таким простым образом:

    dataGrid1.DataSource = datareader;

    Одним из способов вывода является применение дополнительных объектов DataTable. Объект DataTable предназначен для хранения таблицы, полученной из базы данных (в восьмой лекции мы рассмотрим подробно этот объект). Создайте новое приложение и назовите его "DataReaderToDataGrid". Перетаскиваем на форму элемент управления DataGrid, его свойству Dock устанавливаем значение "Fill". Подключаем пространство имен для работы с базой:

    using System.Data.SqlClient;

    В классе формы создаем следующие объекты:

    SqlConnection conn = null;
    //Создаем экземпляр FullDataTable, в который будут помещаться данные
    DataTable FullDataTable = new DataTable();
    //Создаем экземпляр ShemaDataTable для получения структуры таблицы из базы данных 
    DataTable ShemaDataTable = new DataTable();
    SqlDataReader dataReader;
    SqlCommand myCommand;
    //Создаем объект objectRow для получения информации о числе столбцов
    object[] objectRow;

    Основной код помещаем в конструктор формы:

    public Form1()
    {
    	InitializeComponent();
    	try
    	{
    		conn = new SqlConnection();
    		conn.ConnectionString = "integrated security=SSPI;data
    		 source=\".\"; persist security info=False;
    		 initial catalog=BDTur_firm2";
    		conn.Open();
    		myCommand = conn.CreateCommand();
    		myCommand.CommandText = "SELECT * FROM Туристы";
    		dataReader = myCommand.ExecuteReader();
    		//Вызываем метод GetSchemaTable, который получает схему таблицы из базы
    		//и передает ее объекту ShemaDataTable
    		ShemaDataTable = dataReader.GetSchemaTable();
    		//Свойство FieldCount возвращает число столбцов для текущей записи. 
    		//Передаем это значение объекту objectRow
    		objectRow = new object[dataReader.FieldCount];
    		//Определяем структуру объекта FullDataTable
    		for(int i =0; i <dataReader.FieldCount; i++)
    		{
    			FullDataTable.Columns.Add(ShemaDataTable.Rows[i] 
    			 ["ColumnName"].ToString(),
    			 ((System.Type)ShemaDataTable.Rows[i] ["DataType"]));
    		}
    		//Добавляем записи в объект FullDataTable
    		while(dataReader.Read())
    		{
    			dataReader.GetValues(objectRow);
    			myDataRow = FullDataTable.Rows.Add(objectRow);
    		}
    		//Определяем источник данных для элемента dataGrid1
    		dataGrid1.DataSource = FullDataTable;
    	}
    	catch (Exception ex)
    	{
    		MessageBox.Show(ex.ToString());
    	}
    	finally
    	{
    		dataReader.Close();
    		conn.Close();
    	}
    }

    Возможно, этот код показался вам сложным. Лучше будет к нему вернуться еще раз после изучения девятой лекции. Запускаем приложение (рис. 6.28):

    (рис 6.28) Приложение DataReaderToDataGrid

    В программном обеспечении к курсу вы найдете приложение Data ReaderToDataGrid (Code\Glava3\ DataReaderToDataGrid).

    Страницы:

    Свойства и методы объекта Command

    Объект Command. Свойства CommandType и CommandText

    При определении объектов ADO .NET мы назвали DataAdapter адаптером, преобразователем, предназначенным для взаимодействия с базой данных. Это действительно так, однако если рассматривать взаимодействие с базой данных более глубоко, то выясняется, что в ADO .NET есть специализированный объект для выполнения запросов, называемый Command. Под запросами понимается выполнение SQL-конструкций или запуск хранимых процедур. С объектом Command мы познакомились еще во второй лекции, но у вас могло сложиться впечатление, что его введение достаточно формально - вполне можно обойтись одним объектом DataAdapter. Например, в приложении VisualDataAdapterSP мы создавали и выполняли хранимые процедуры, настраивая DataAdapter без всякого объекта Command! В действительности, среда создала его неявным образом - открываем метод InitializeComponent и видим объявление sqlSelectCommand1, а также описание нескольких его свойств:

    private void InitializeComponent()
    	{
    ...
    this.sqlSelectCommand1 = new System.Data.SqlClient.SqlCommand();
    		...
    // 
    // sqlSelectCommand1
    // 
    this.sqlSelectCommand1.CommandText = "[proc_da1]";
    this.sqlSelectCommand1.CommandType = System.Data.CommandType.StoredProcedure;
    this.sqlSelectCommand1.Connection = this.sqlConnection1;
    this.sqlSelectCommand1.Parameters.Add(new System.Data.SqlClient.SqlParameter
     ("@RETURN_VALUE", System.Data.SqlDbType.Int, 4, 
     System.Data.ParameterDirection.ReturnValue, false, ((System.Byte)(0)),
     ((System.Byte)(0)), "", System.Data.DataRowVersion.Current, null));
    ...

    Нам нужно научиться понимать это описание и создавать его программным образом. Среда Visual Studio .NET содержит инструменты для визуальной настройки объекта Command, и поскольку разобраться в них проще, чем в коде, начнем с них. Создайте новое Windows-приложение и назовите его "VisualCommand". Добавляем элемент управления DataGrid, его свойству Dock устанавливаем значение Fill. В окне ToolBox переходим на вкладку Data, перетаскиваем на форму объект SqlCommand (рис. 6.1):

    (рис 6.1) Окно Toolbox, вкладка Data

    Переходим в окно Properties появившегося объекта sqlCommand1, в свойстве "Connection" из выпадающего списка выбираем создание нового подключения (New) (рис. 6.2).

    (рис 6.2) Свойство "Connection" объекта sqlCommand1

    В окне "Свойства связи с данными" настраиваем подключение к базе данных BDTur_firm2 (см. рис. 5.18). На панели компонент появляется объект sqlConnection1 - если бы мы вначале создали и настроили подключение, то в свойстве Connection объекта sqlCommand1 можно было выбрать "Existing/sqlConnection1". Свойство CommandType указывает на тип команды (запроса), который будет направляться к базе данных (рис. 6.3):

    (рис 6.3) Свойство CommandType объекта sqlCommand1

    Возможны следующие значения:

  • Text. Текстовая команда состоит из SQL-конструкции, направляемой к базе данных. Это значение используется по умолчанию.
  • StoredProcedure. Текстовая команда состоит из названия хранимой процедуры.
  • TableDirect. Текстовая команда состоит из названия таблицы базы данных. В результате извлекается все содержимое таблицы. Эта команда аналогична текстовой команде SELECT * FROM Название_таблицы. Команда TableDirect поддерживается только управляемым поставщиком OLE DB.
  • Оставляем пока значение по умолчанию. В поле свойства (...) (рис. 6.4):

    (рис 6.4) Свойство CommandText объекта sqlCommand1

    В появившемся построителе выражений настраиваем извлечение содержимого таблицы "Туры". Обратите внимание на то, что в поле свойства CommandText появилась SQL-конструкция:

    SELECT Туры.* FROM Туры

    Снова в окне Toolbox переходим на вкладку Data, перетаскиваем на форму объект SqlDataAdapter. В мастере "Data Adapter Configuration Wizard" нажимаем кнопку "Cancel" - у нас уже есть подключение и настроенный объект sqlCommand1, достаточно указать в свойстве Select Command объект sqlCommand1 (рис. 6.5):

    (рис 6.5) Свойство SelectCommand объекта sqlDataAdapter1

    Выделяем sqlDataAdapter1, создаем DataSet, нажимая на ссылку "Generate Dataset". В окне "Generate Dataset" оставляем название "DataSet1", нажимаем "OK". В выпадающем списке свойства DataSource элемента управления DataGrid выбираем "datSet11" в качестве источника данных (рис. 6.6):

    (рис 6.6) Свойство DataSource элемента dataGrid1

    Наконец в конструкторе формы вызываем метод Fill объекта DataAdapter:

    public Form1()
    		{
    			InitializeComponent();
    			sqlDataAdapter1.Fill(dataSet11);
    		}

    Запускаем приложение. На форму выводится содержимое таблицы "Туры" (рис. 6.7):

    (рис 6.7) Приложение VisualCommand, вывод таблицы "Туры"

    Установим теперь в свойстве CommandType объекта Command значение "StoredProcedure", а в свойстве CommandText введем название имеющейся хранимой процедуры - "[proc10]" (рис. 6.8):

    (рис 6.8) Настройка свойств объекта sqlCommand1 для вывода процедуры [proc10]

    Чтобы избежать проблем со структурой DataSet, удалим имеющийся объект с панели компонент. Дополнительно в окне Solution Explorer удаляем файл DataSet1.xsd. Снова генерируем DataSet и определяем его в качестве источника данных для элемента DataGrid. Запускаем приложение - на форму выводится результат выполнения хранимой процедуры (рис. 6.9):

    (рис 6.9) Приложение VisualCommand, извлечение хранимой процедуры proc10

    Таким образом можно извлекать данные из таблиц при помощи SQL-конструкций и запускать хранимые процедуры таблицы 5.1. Поставщик данных SQL Server не поддерживает значение TableDirect свойства CommandType - при его выборе появляется диагностическое сообщение об ошибке. Это значение можно применять при подключении к базе данных Microsoft Access.

    В программном обеспечении к курсу вы найдете приложение Visual Command (Code\Glava3\ VisualCommand).

    Перейдем теперь к реализации аналогичного приложения без применения визуальных средств студии для объектов ADO .NET. Создайте новое Windows-приложение и назовите его "ProgrammCommand". Перетаскиваем на форму элемент управления DataGrid, его свойству Dock устанавливаем значение "Fill". В классе формы создаем строки connectionString и commandText:

    string connectionString = "integrated security=SSPI;
     data source=\".\"; persist security info=False;
     initial catalog=BDTur_firm2";
    string commandText = "SELECT * FROM Туры";

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

    public Form1()
    {		
    	InitializeComponent();
    	SqlConnection conn = new SqlConnection(connectionString);
    	SqlCommand myCommand = new SqlCommand();
    	myCommand.Connection = conn;
    	myCommand.CommandText = commandText;
    	SqlDataAdapter dataAdapter = new SqlDataAdapter();
    	dataAdapter.SelectCommand = myCommand;
    	DataSet ds = new DataSet();
    	conn.Open();
    	dataAdapter.Fill(ds, "Туры");
    	conn.Close();
    	dataGrid1.DataSource = ds;
    }

    Запускаем приложение. На форму выводится содержимое таблицы "Туры" (см. рис. 6.7). Здесь мы используем всего один объект DataAdapter, который сам открывает и закрывает соединение для получения данных. Поэтому можно не вызывать методы Open и Close объекта conn. Конструктор объекта SqlCommand может принимать значения, указанные в таблице 6.1.

    Конструктор объекта SqlCommandКонструктор объекта OleDbCommand в точности такой же.
    Вид
    1
    Пример Приводится только создание объекта SqlTransaction, без его методов, - см далее Лекцию 7, тему "Работа с транзакциями". Описание
    public Form1()
    {		
    InitializeComponent();
    SqlConnection  conn =
     new SqlConnection(connectionString);
    SqlTransaction trans = conn.BeginTransaction();
    SqlCommand myCommand =
     new SqlCommand(commandText, conn, trans);
    //SqlCommand  myCommand = 
    // new SqlCommand("SELECT * FROM Туры", conn, trans);
    SqlDataAdapter dataAdapter = new SqlDataAdapter();
    dataAdapter.SelectCommand = myCommand;
    DataSet ds = new DataSet();
    dataAdapter.Fill(ds, "Туры");
    dataGrid1.DataSource = ds;		
    }
    Первый параметр - строка commandText, причем можно сразу задавать SQL-конструкцию без создания строки в классе. Затем экземпляры объектов SqlConnection и SqlTransaction. Подробно о транзакциях см. далее
    Вид
    2
    Пример Описание
    public Form1()
    {		
    InitializeComponent();
    SqlConnection  conn =
     new SqlConnection(connectionString);
    SqlCommand  myCommand =
     new SqlCommand(commandText, conn);
    //SqlCommand  myCommand =
    // new SqlCommand("SELECT * FROM Туры", conn);
    SqlDataAdapter dataAdapter = new SqlDataAdapter();
    dataAdapter.SelectCommand = myCommand;
    DataSet ds = new DataSet();
    dataAdapter.Fill(ds, "Туры");
    dataGrid1.DataSource = ds;	
    }
    Вариант без экземпляра объекта SqlTransaction
    Вид
    3
    Пример Описание
    public Form1()
    {
    SqlConnection  conn =
     new SqlConnection(connectionString);
    SqlCommand  myCommand =
     new SqlCommand(commandText);
    //SqlCommand  myCommand =
    //  new SqlCommand("SELECT * FROM Туры");
    myCommand.Connection = conn;
    SqlDataAdapter dataAdapter =
     new SqlDataAdapter();
    dataAdapter.SelectCommand = myCommand;
    DataSet ds = new DataSet();
    dataAdapter.Fill(ds, "Туры");
    dataGrid1.DataSource = ds;	
    }
    Конструктор принимает в качестве параметра только строку commandText. Для указания соединений используем свойство Connection созданного экзмепляра myCommand
    Вид
    4
    Пример Описание
    public Form1()
    {
    SqlConnection  conn =
     new SqlConnection(connectionString);
    SqlCommand  myCommand = new SqlCommand();
    myCommand.Connection = conn;
    myCommand.CommandText = commandText;
    //Command.CommandText = "SELECT * FROM Туры";
    SqlDataAdapter dataAdapter = new SqlDataAdapter();
    dataAdapter.SelectCommand = myCommand;
    DataSet ds = new DataSet();
    dataAdapter.Fill(ds, "Туры");
    dataGrid1.DataSource = ds;
    }
    Конструктор не принимает параметров. Для указания соединения и строки commandText вызываем соответствующие свойства созданного экземпляра myCommand. Строке commandText можно передать SQL-конструкцию непосредственно - в этом случае создавать переменную в классе не нужно

    Первый вариант конструктора обеспечивает самую компактную форму записи. Его можно использовать при работе с одним соединением с базой данных. В случае работы с несколькими соединениями удобнее определить значения командных строк в классе формы (или вообще в отдельном классе) и использовать четвертый вариант. При изменении параметров подключения нужно будет исправить только сами строки, не затрагивая код, относящийся к объекту Command.

    Для вывода хранимой процедуры следует указать значение "Stored Procedure" свойства CommandType:

    public Form1()
    {
    SqlConnection conn =
     new SqlConnection(connectionString);
    SqlCommand myCommand =
     new SqlCommand();
    myCommand.Connection = conn;
    myCommand.CommandType =
     CommandType.StoredProcedure;
    myCommand.CommandText = "[proc10]";
    SqlDataAdapter dataAdapter =
     new SqlDataAdapter();
    dataAdapter.SelectCommand = myCommand;
    DataSet ds = new DataSet();
    dataAdapter.Fill(ds, "Туры");
    dataGrid1.DataSource = ds;
    }

    Название процедуры можно также присвоить переменной commandText, созданной в классе формы.

    В программном обеспечении к курсу вы найдете приложение Programm Command (Code\Glava3\ ProgrammCommand).

    Методы ExecuteNonQuery, ExecuteScalar и ExecuteReader объекта Command

    Мы рассмотрели всю возможную функциональность, которую можно получить без использования каких-либо методов объекта Command. Для выполнения запросов на выборку простейших процедур достаточно просто указать тип и передать название запроса или процедуры. Все работает, но этого явно недостаточно для серьезных приложений. Поэтому забудем пока про хранимые процедуры, другие объекты ADO .NET, и сосредоточим все внимание на методах объекта Command.

    Метод ExecuteNonQuery применяется для выполнения запросов, не возвращающих данные. Как же запросы, предназначенные именно для извлечения данных, могут не возвращать их? Речь идет о запросах типа UPDATE, INSERT и DELETE - в самом деле, они вносят изменения в таблицу базы данных, не возвращая ничего назад в результате выполнения. В самом языке SQL эти запросы образуют категорию DML (Data Manipulation Language, DML). Дословный перевод названия категории - "язык манипулирования данными", наличие слова "язык" вносит небольшую путаницу: в языке SQL есть язык DML. Но это именно так. После небольшого опыта подобные названия станут привычными.

    Создайте новое консольное приложение и назовите его "Example ExecuteNonQuery". Привожу его полный листинг:

    using System;
    using System.Data.SqlClient;
    
    namespace ExampleExecuteNonQuery
    {	
    	class Class1
    	{	
    		[STAThread]
    		static void Main(string[] args)
    		{
    			SqlConnection conn = new SqlConnection();
    			conn.ConnectionString = "integrated security=SSPI;
    			 data source=\".\"; 
    			 persist security info=False; initial catalog=BDTur_firm2";
    			conn.Open();
    			SqlCommand myCommand = conn.CreateCommand();
    			myCommand.CommandText = "UPDATE Туристы 
    			 SET Фамилия = 'Сергеева'
    			 WHERE Кодтуриста = 3";
    			myCommand.ExecuteNonQuery();
    			conn.Close();
    		}
    	}
    }

    Объект Command можно создавать, вызывая метод CreateCommand объекта Connection:

    SqlCommand myCommand = conn.CreateCommand();

    Ранее мы пользовались следующим способом:

    SqlCommand myCommand = new SqlCommand();
    myCommand.Connection = conn;

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

    myCommand.ExecuteNonQuery();

    Запускаем приложение, нажимая Ctrl+F5. При успешном выполнении запроса в консольном окне появляется надпись "Press any key to continue" (рис. 6.10):

    (рис 6.10) Выполнение приложения ExampleExecuteNonQuery

    Запускаем SQL Server Enterprise Manager, открываем таблицу "Туристы" и убеждаемся в том, что запись изменилась (рис. 6.11).

    (рис 6.11) Таблица "Туристы", изменение записи

    Метод ExecuteNonQuery все-таки что-то неявно возвращает - результат выполнения запроса в виде количества измененных записей; это может применяться для проверки (изменим фрагмент кода):

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

    Теперь, в зависимости от результата, будет появляться соответствующее сообщение (рис. 6.12).

    (рис 6.12) Результаты проверки выполнения запроса

    Неудачный результат можно получить, установив в тексте запроса значения поля "Код туриста", равное, скажем, 10.

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

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

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

    (рис 6.13) Таблица "Туристы", добавление записи

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

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

    Запускаем приложение - из таблицы удалена четвертая запись (рис. 6.14):

    (рис 6.14) Таблица "Туристы", удаление записи

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

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

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

    (рис 6.15) База данных BDTur_firm2, новая таблица "Отзывы"

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

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

    В SQL Server Enterprise Manager дважды щелкаем по названию таблицы - в появившемся окне "Table Properties" видим новое поле "Отзывтуриста" (рис. 6.16):

    (рис 6.16) Свойства таблицы "Отзывы"

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

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

    Переходим в SQL Server Enterprise Manager - таблица полностью исчезла из базы данных. Если бы нам нужно было лишь удалить данные из таблицы, сохранив структуру, мы бы воспользовались следующей командой:

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

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

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

    В программном обеспечении к курсу вы найдете приложение Example ExecuteNonQuery (Code\Glava3\ ExampleExecuteNonQuery).

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

    using System;
    using System.Data.SqlClient;
    
    namespace ExampleExecuteScalar
    {
    	class Class1
    	{
    		[STAThread]
    		static void Main(string[] args)
    		{
    			SqlConnection conn = new SqlConnection();
    			conn.ConnectionString = "integrated security=SSPI;data 
    			 source=\".\"; persist security info=False;
    			 initial catalog=BDTur_firm2";
    			conn.Open();
    			SqlCommand myCommand = conn.CreateCommand();
    			// SqlCommand myCommand = new SqlCommand();
    			// myCommand.Connection = conn;
    			myCommand.CommandText = "SELECT
    			 COUNT (*) FROM Туры";
    			string KolichestvoTurov = Convert.ToString(myCommand.ExecuteScalar());
    			conn.Close();
    			Console.WriteLine("Количество туров: " + 
    			 KolichestvoTurov);
    		}
    	}
    }

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

    (рис 6.17) Приложение ExampleExecuteScalar, вывод количества туров

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

    ...
    	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);
     }

    Запускаем приложениеСкорее всего, у вас будет измененное содержимое таблицы "Туры" - если, конечно, вы выполняли все действия, описанные в этой главе. Для получения исходной таблицы запустите SQL Server Enterprise Manager, удалите имеющуюся таблицу "Туры" и импортируйте ее снова из файла BDTur_firm2.mdb ((Code\Glava3\ BDTur_firm2.mdb). - получаем несколько значений из базы данных (рис. 6.18):

    (рис 6.18) Приложение ExampleExecuteScalar, вывод нескольких значений

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

    В программном обеспечении к курсу вы найдете приложение Example ExecuteScalar (Code\Glava3\ ExampleExecuteScalar).

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

    using System;
    using System.Data.SqlClient;
    
    namespace ExampleExecuteReader
    {
    	class Class1
    	{
    		[STAThread]
    		static void Main(string[] args)
    		{
    			SqlConnection conn = new SqlConnection();
    			conn.ConnectionString = "integrated security=SSPI;data 
    			 source=\".\"; persist security info=False;
    			 initial catalog=BDTur_firm2";
    			SqlCommand myCommand = conn.CreateCommand();
    			//SqlCommand myCommand = new SqlCommand();
    			//myCommand.Connection = conn;
    			myCommand.CommandText = "SELECT *
    			 FROM Туристы";
    			conn.Open();
    			SqlDataReader dataReader = myCommand.ExecuteReader();
    			
    			while (dataReader.Read())
    			{
    				Console.WriteLine(dataReader["Фамилия"]);
    			}
    			dataReader.Close();			
    			conn.Close();			
    		}
    	}
    }

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

    SqlDataReader dataReader = myCommand.ExecuteReader();

    Перед считыванием первой записи вызываем метод Read объекта dataReader и выводим набор записей в консольное окно. Запускаем приложениеТаблица "Туристы" была заменена на оригинальную из базы BDTur_firm2.mdb. (рис. 6.19).

    (рис 6.19) Приложение "ExampleExecuteReader". Вывод поля "Фамилия"

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

    Console.WriteLine(dataReader[1]);

    Это совершенно эквивалентная строка выводаИспользование индексов повышает производительность приложения..

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

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

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

    (рис 6.20) Приложение ExampleExecuteReader, вывод содержимого всех полей

    Поскольку мы имеем дело с объектами (тип данных 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]));

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

    (рис 6.21) Приложение ExampleExecuteReader. Вывод содержимого всех полей в виде записей

    В программном обеспечении к курсу вы найдете приложение "Example ExecuteReader" (Code\Glava3\ ExampleExecuteReader).

    Применение методов объекта Command в Windows-приложениях. Параметризированные запросы

    Мы рассмотрели основные методы объекта Command в консольных приложениях. Это дало нам возможность понять синтаксис самих методов, без привязки к какому-либо интерфейсу. Однако, после того как синтаксис стал ясен, возникает вопрос - как же использовать эти методы в реальных приложениях? Понятно, что простое копирование кода в конструктор формы, по сути, не изменит пример. Следовательно, мы должны привязывать вводимые значения, например, к текстовым полям. Но это означает, что параметры строки запроса будут неизвестны до тех пор, пока пользователь не введет соответствующие значения. Например, для метода ExecuteNonQuery строка commandText имела следующий вид:

    myCommand.CommandText = "UPDATE Туристы SET
     Фамилия = 'Сергеева' WHERE Кодтуриста = 3";

    Если мы создадим приложение, где пользователь будет вводить фамилию и код туриста, то мы не можем заранее указать, какие это будут значения. Логически запрос можно представить примерно так:

    myCommand.CommandText = "UPDATE Туристы SET
     Фамилия = 'Какая-то_фамилия,_которую_введет_пользователь'
     WHERE Кодтуриста = Какой-то_код,_который_введет_пользователь";

    Для решения таких задач, которые возникли еще в самом начале разработки языка SQL, были придуманы параметризированные запросы. В них неизвестные значения заменяются параметрами. Вот так:

    myCommand.CommandText = "UPDATE Туристы SET
     Фамилия = @Family WHERE Кодтуриста = @TouristID";

    Здесь @Family (обратите внимание, пишется без кавычек!) - параметр для неизвестного значения фамилии, @TouristID - параметр для неизвестного значения кода туриста. Теперь мы можем привязывать параметры к тексту, вводимому пользователем. Создайте новое Windows-приложение и назовите его "ExamWinExecuteNonQuery". Устанавливаем следующие свойства формы:

    Form1, форма, свойствоЗначение
    FormBorderStyle FixedSingle
    MaximizeBox False
    Size 620; 240

    Добавляем на форму элементы управления и устанавливаем их свойства:

    gropBox1, свойствоЗначение
    Location 16; 16
    Size 296; 112
    Text Пример UPDATE
    gropBox2, свойство Значение
    Location 320; 16
    Size 280; 176
    Text Пример INSERT
    gropBox3, свойство Значение
    Location 16; 136
    Size 296; 56
    Text Пример DELETE
    button1, свойство Значение
    Name btnUpdate
    Location 80; 80
    Text Обновить
    button2, свойство Значение
    Name btnInsert
    Location 88; 144
    Text Добавить
    button3, свойство Значение
    Name BtnDelete
    Location 208; 24
    Text Удалить
    textBox1, свойство Значение
    Name TxtTouristIDUpdate
    Location 16; 24
    Size 224; 20
    Text Введите код туриста
    textBox2, свойство Значение
    Name TxtFamilyUpdate
    Location 16; 56
    Size 224; 20
    Text Введите фамилию туриста
    textBox3, свойство Значение
    Name TxtTouristIDInsert
    Location 16; 24
    Size 224; 20
    Text Введите код туриста
    textBox4, свойство Значение
    Name TxtFamilyInsert
    Location 16; 56
    Size 224; 20
    Text Введите фамилию туриста
    textBox5, свойство Значение
    Name TxtFirstNameInsert
    Location 16; 88
    Size 224; 20
    Text Введите имя туриста
    textBox6, свойство Значение
    Name TxtMiddleNameInsert
    Location 16; 120
    Size 224; 20
    Text Введите отчество туриста
    textBox7, свойство Значение
    Name txtTouristIDDelete
    Location 8; 24
    Size 192; 20
    Text Введите код туриста для удаления

    Форма в режиме дизайна будет иметь следующий вид (рис. 6.22):

    (рис 6.22) Приложение ExamWinExecuteNonQuery, вид формы в режиме дизайна

    Подключаем пространство имен для работы с базой данных:

    using System.Data.SqlClient;

    В классе формы создаем экземпляр connМы это делаем для включения блока обработки исключений.:

    SqlConnection conn = null;

    Обработчик кнопки btnUpdate будет иметь следующий вид:

    private void btnUpdate_Click(object sender, System.EventArgs e)
    {
    	try
    	{
    		//Создаем переменную Family, в которую помещаем значение,
    		//введенное пользователем в поле txtFamilyUpdate:
    		string Family = Convert.ToString(this.txtFamilyUpdate.Text);
    		//Создаем переменную TouristID, в которую помещаем значение,
    		//введенное пользователем в поле txtTouristIDUpdate:
    		int TouristID = int.Parse(this.txtTouristIDUpdate.Text);
    		conn = new SqlConnection();
    		conn.ConnectionString = "integrated security=SSPI;data 
    		 source=\".\"; persist security info=False;
    		 initial catalog=BDTur_firm2";
    		conn.Open();
    		SqlCommand myCommand = conn.CreateCommand();
    		myCommand.CommandText = "UPDATE Туристы
    		 SET Фамилия = @Family WHERE Кодтуриста = @TouristID";
    		//Добавляем параметр @Family в коллекцию параметров 
    		//объекта myCommand
    		myCommand.Parameters.Add("@Family",
    		 SqlDbType.NVarChar, 50);
    		//Устанавливаем значение параметра @Family
    		//равным значению переменной Family
    		myCommand.Parameters["@Family"].Value = Family;
    		//Добавляем параметр @TouristID в коллекцию параметров 
    		//объекта myCommand
    		myCommand.Parameters.Add("@TouristID", SqlDbType.Int, 4);
    		//Устанавливаем значение параметра @TouristID 
    		//равным значению переменной TouristID
    		myCommand.Parameters["@TouristID"].Value = TouristID;
    		int UspeshnoeIzmenenie = myCommand.ExecuteNonQuery();
    		if (UspeshnoeIzmenenie !=0)
    		{
    			MessageBox.Show("Изменения внесены", 
    			 "Изменение записи");
    		}
    		else
    		{
    			MessageBox.Show("Не удалось внести изменения", 
    			 "Изменение записи");
    		}
    
    	}
    	catch(Exception ex)
    	{
    		MessageBox.Show(ex.ToString());
    	}
    	finally
    	{
    		conn.Close();
    	}
    }

    Обратите внимание - в блоке finally мы закрываем соединение, это нужно сделать в любом случае, независимо от результата выполнения команды. Значения, введенные пользователем в текстовые поля txtFamilyUpdate и txtTouristIDUpdate, помещаются в переменные Family и TouristID. В запросе к базе данных используются два параметра - @Family и @TouristID. Мы добавляем их в коллекцию объекта Command, используя метод Add свойства Parameters, а затем устанавливаем значения параметров равными переменным Family и TouristID. Конструктор метод Add перегружен, первый вариант принимает наибольшее количество свойствВообще-то, раньше свойства, входящие в какой-либо метод, мы называли параметрами. Здесь я не стал использовать это слово, чтобы не было путаницы с параметрами, входящими в коллекцию объекта Command. (рис. 6.23):

    (рис 6.23) Конструктор метода Add свойства Parameters объекта Command

    Описание некоторых свойств метода Add приводится в таблице 6.2.

    Свойства метода Add
    Свойство Описание
    parameterName Название параметра
    sqlDbType Тип данных передаваемого параметра
    size Размер параметра
    sourceColumn Название имени столбца объекта DataSet, на который ссылается данный параметр

    Конструктор метода Add свойства Parameters объекта Command для поставщика данных OLE DB имеет в точности такую же структуру (рис. 6.24):

    (рис 6.24) Конструктор метода Add свойства Parameters объекта Command поставщика данных OLE DB

    Добавляем обработчик кнопки btnInsert:

    private void btnInsert_Click(object sender, System.EventArgs e)
    {
    	try
    	{
    		int TouristID = int.Parse(this.txtTouristIDInsert.Text);
    		string Family = Convert.ToString(this.txtFamilyInsert.Text);
    		string FirstName = Convert.ToString(this.txtFirstNameInsert.Text);
    		string MiddleName = Convert.ToString(this.txtMiddleNameInsert.Text);
    		conn = new SqlConnection();
    		conn.ConnectionString = "integrated security=SSPI;data 
    		 source=\".\"; persist security info=False;
    		 initial catalog=BDTur_firm2";
    		conn.Open();
    		SqlCommand myCommand = conn.CreateCommand();
    		myCommand.CommandText =
    		 "INSERT INTO Туристы (Кодтуриста, Фамилия, Имя, Отчество)
    		 VALUES (@TouristID, @Family, @FirstName, @MiddleName)";
    		myCommand.Parameters.Add("@TouristID", SqlDbType.Int, 4);
    		myCommand.Parameters["@TouristID"].Value = TouristID;
    		myCommand.Parameters.Add("@Family", SqlDbType.NVarChar, 50);
    		myCommand.Parameters["@Family"].Value = Family;
    		myCommand.Parameters.Add("@FirstName",
    		 SqlDbType.NVarChar, 50);
    		myCommand.Parameters["@FirstName"].Value = FirstName;
    		myCommand.Parameters.Add("@MiddleName",
    		 SqlDbType.NVarChar, 50);
    		myCommand.Parameters["@MiddleName"].Value = MiddleName;
    		int UspeshnoeIzmenenie = myCommand.ExecuteNonQuery();
    		if (UspeshnoeIzmenenie !=0)
    		{
    			MessageBox.Show("Изменения внесены",
    			 "Изменение записи");
    		}
    		else
    		{
    			MessageBox.Show("Не удалось внести изменения",
    			 "Изменение записи");
    		}
    
    	}
    	catch(Exception ex)
    	{
    		MessageBox.Show(ex.ToString());
    	}
    	finally
    	{
    		conn.Close();
    	}
    }

    В запросе используются четыре параметра: @TouristID, @Family, @FirstName, @MiddleName. Тип данных создаваемых параметров соответствует типу данных полей таблицы "Туристы" в базе.

    Добавляем обработчик кнопки btnDelete:

    private void btnDelete_Click(object sender, System.EventArgs e)
    {
    	try
    	{
    		int TouristID = int.Parse(this.txtTouristIDDelete.Text);
    		conn = new SqlConnection();
    		conn.ConnectionString = "integrated security=SSPI;data
    		 source=\".\"; persist security info=False;
    		 initial catalog=BDTur_firm2";
    		conn.Open();
    		SqlCommand myCommand = conn.CreateCommand();
    		myCommand.CommandText = "DELETE FROM Туристы
    		 WHERE Кодтуриста = @TouristID";
    		myCommand.Parameters.Add("@TouristID", SqlDbType.Int, 4);
    		myCommand.Parameters["@TouristID"].Value = TouristID;
    		int UspeshnoeIzmenenie = myCommand.ExecuteNonQuery();
    		if (UspeshnoeIzmenenie !=0)
    		{
    			MessageBox.Show("Изменения внесены", 
    			 "Изменение записи");
    		}
    		else
    		{
    			MessageBox.Show("Не удалось внести изменения",
    			 "Изменение записи");
    		}
    
    	}
    	catch(Exception ex)
    	{
    		MessageBox.Show(ex.ToString());
    	}
    	finally
    	{
    		conn.Close();
    	}
    }

    Запускаем приложение. В каждой из групп заполняем поля, затем нажимаем на кнопки. Проверять результат можно, запуская SQL Server Enterprise Manager и просматривая каждый раз содержимое таблицы "Туристы" (рис. 6.25):

    (рис 6.25) Готовое приложение ExamWinExecuteNonQuery. Таблицы взяты из SQL Server Enterprise Manager

    В программном обеспечении к курсу вы найдете приложение Exam WinExecuteNonQuery (Code\Glava3\ ExamWinExecuteNonQuery).

    Применять метод ExecuteScalar объекта Command в Windows-приложениях очень легко - достаточно указать элемент управления (текстовое поле, надпись) для вывода одиночного значения.

    Рассмотрим теперь метод (...) (рис. 6.26). В редакторе "Column Header Collection Editor" добавляем следующие четыре элемента:

    NameText
    chTouristID Код туриста
    NameText
    chFamily Фамилия
    NameText
    chFirstName Имя
    NameText
    chMiddleName Отчество
    (рис 6.26) Свойство Columns элемента ListView и редактор "ColumnHeader Collection Editor"

    Для отображения созданных столбцов свойству View элемента устанавливаем значение "Details". Также включим режим отображения линий сетки - в свойстве GridLines выбираем значение "True". Сделанные изменения немедленно отобразятся на элементе.

    Подключаем пространство имен для работы с базой:

    using System.Data.SqlClient;

    В классе формы создаем объекты conn и dataReader:

    SqlConnection conn = null;
    SqlDataReader dataReader;

    В конструкторе формы добавляем код для заполнения данными элементов управления:

    public Form1()
    {
    	InitializeComponent();
    	try
    	{
    		conn = new SqlConnection();
    		conn.ConnectionString = "integrated security=SSPI;data 
    		 source=\".\"; persist security info=False; initial
    		 catalog=BDTur_firm2";
    		conn.Open();
    		SqlCommand myCommand = conn.CreateCommand();
    		myCommand.CommandText = "SELECT * FROM Туристы";
    		dataReader = myCommand.ExecuteReader();
    		while (dataReader.Read())
    		{
    			// Создаем переменные, получаем для них значения 
    			//из объекта dataReader, используя метод GetТипДанных
    			int TouristID = dataReader.GetInt32(0);
    			string Family = dataReader.GetString(1);
    			string FirstName = dataReader.GetString(2);
    			string MiddleName = dataReader.GetString(3);
    			//Выводим данные в элемент listBox1
    			listBox1.Items.Add("Код туриста: " + TouristID+
    			 " Фамилия: " + Family + " Имя: "+
    			 FirstName + " Отчество: " + MiddleName);
    			//Создаем экземпляр item класса ListViewItem 
    			//для записи в него данных из dataReader
    			ListViewItem item =
    			 new ListViewItem(new string[]{Convert.ToString(dataReader[0]), 
    			 Convert.ToString(dataReader[1]), Convert.ToString(dataReader[2]), 
    			 Convert.ToString(dataReader[3])});
    			listView1.Items.Add(item);				
    		}
    	}
    	catch(Exception ex)
    	{
    		MessageBox.Show(ex.ToString());
    	}
    	finally
    	{
    		dataReader.Close();			
    		conn.Close();
    	}
    }

    Метод "GetТипДанных" позволяет приводить значения, возвращаемые объектом DataReader, если заранее известен их тип данных. Запускаем приложение. На форму выводятся данные в виде списка в элементе ListBox и в виде таблицы в элементе ListView (рис. 6.27):

    (рис 6.27) Приложение ExamWinExecuteReader

    В программном обеспечении к курсу вы найдете приложение ExamWin ExecuteReader (Code\Glava3\ ExamWinExecuteReader).

    Вывод данных в элемент ListView приводит к достаточно удовлетворительному результату, однако более привычным для нас является вывод в элемент DataGrid. Раньше, при использовании объекта DataSet, мы указывали источник данных для элемента DataGrid так:

    dataGrid1.DataSource = dataset.Tables["Название_таблицы"].DefaultView;

    Или так:

    dataGrid1.DataSource = dataset;

    Объект DataReader не поддерживает аналогичного вывода - мы не можем связать объекты таким простым образом:

    dataGrid1.DataSource = datareader;

    Одним из способов вывода является применение дополнительных объектов DataTable. Объект DataTable предназначен для хранения таблицы, полученной из базы данных (в восьмой лекции мы рассмотрим подробно этот объект). Создайте новое приложение и назовите его "DataReaderToDataGrid". Перетаскиваем на форму элемент управления DataGrid, его свойству Dock устанавливаем значение "Fill". Подключаем пространство имен для работы с базой:

    using System.Data.SqlClient;

    В классе формы создаем следующие объекты:

    SqlConnection conn = null;
    //Создаем экземпляр FullDataTable, в который будут помещаться данные
    DataTable FullDataTable = new DataTable();
    //Создаем экземпляр ShemaDataTable для получения структуры таблицы из базы данных 
    DataTable ShemaDataTable = new DataTable();
    SqlDataReader dataReader;
    SqlCommand myCommand;
    //Создаем объект objectRow для получения информации о числе столбцов
    object[] objectRow;

    Основной код помещаем в конструктор формы:

    public Form1()
    {
    	InitializeComponent();
    	try
    	{
    		conn = new SqlConnection();
    		conn.ConnectionString = "integrated security=SSPI;data
    		 source=\".\"; persist security info=False;
    		 initial catalog=BDTur_firm2";
    		conn.Open();
    		myCommand = conn.CreateCommand();
    		myCommand.CommandText = "SELECT * FROM Туристы";
    		dataReader = myCommand.ExecuteReader();
    		//Вызываем метод GetSchemaTable, который получает схему таблицы из базы
    		//и передает ее объекту ShemaDataTable
    		ShemaDataTable = dataReader.GetSchemaTable();
    		//Свойство FieldCount возвращает число столбцов для текущей записи. 
    		//Передаем это значение объекту objectRow
    		objectRow = new object[dataReader.FieldCount];
    		//Определяем структуру объекта FullDataTable
    		for(int i =0; i <dataReader.FieldCount; i++)
    		{
    			FullDataTable.Columns.Add(ShemaDataTable.Rows[i] 
    			 ["ColumnName"].ToString(),
    			 ((System.Type)ShemaDataTable.Rows[i] ["DataType"]));
    		}
    		//Добавляем записи в объект FullDataTable
    		while(dataReader.Read())
    		{
    			dataReader.GetValues(objectRow);
    			myDataRow = FullDataTable.Rows.Add(objectRow);
    		}
    		//Определяем источник данных для элемента dataGrid1
    		dataGrid1.DataSource = FullDataTable;
    	}
    	catch (Exception ex)
    	{
    		MessageBox.Show(ex.ToString());
    	}
    	finally
    	{
    		dataReader.Close();
    		conn.Close();
    	}
    }

    Возможно, этот код показался вам сложным. Лучше будет к нему вернуться еще раз после изучения девятой лекции. Запускаем приложение (рис. 6.28):

    (рис 6.28) Приложение DataReaderToDataGrid

    В программном обеспечении к курсу вы найдете приложение Data ReaderToDataGrid (Code\Glava3\ DataReaderToDataGrid).

    Вернуться к учебному плану