Практическая работа по теме 1. "Новейшие направления в области создания технологий программирования" Использование инструментов Visual Studio.NET для создания баз данных и написания хранимых процедур. Основные объекты провайдера данных: объект Connection, объект Command. Применение объекта Command с параметрами и хранимыми процедурами. Метод выполнения команд ExecuteNonQuery. Метод ExecuteScalar. Метод ExecuteReader и объект DataReader. Транзакции к базе данных - объект Transaction. Оглавление Задание 1. Использование инструментов Visual Studio для создания базы данных Задание 2. Основные объекты провайдера данных: объект Connection Задание 3. Основные объекты провайдера данных: объект Command Задание 4. Применение объекта Command с параметрами и хранимыми процедурами Задание 5. Выполнение команд: метод ExecuteNonQuery Задание 6. Выполнение команд: метод ExecuteScalar Цель Задание 7. Выполнение команд: метод ExecuteReader и объект DataReader Задание 8. Другие объекты провайдеров данных: объект Transaction Задание 1. Использование инструментов Visual Studio для создания базы данных Цель Научиться использовать инструменты Visual Studio.NET для создания базы данных. Решение Использовать компонент Visual Studio.NET Server Explorer для создания новой базы данных, определения структуры таблиц, индексов и первичных ключей таблиц, создания схемы базы данных и установления отношений между таблицами. Обсуждение Существует несколько способов создания баз данных в SQL Server. С помощью набора инструментов SQL Enterprise Manager базы данных можно создавать графически или программно (с помощью команд на языке SQL). Кроме этого инструмента, существует множество других внешних инструментов для создания баз данных, например, Microsoft Visio. Среда Visual Studio.NET также содержит очень удобный инструмент для работы с базами данных MS SQL Server. Инструментарий входит в состав компонента Server Explorer, который предназначен для централизованного управления всеми видами серверного программного обеспечения. Рассмотрим создание базы данных, предназначенной для хранения следующей информации о каждом сотруднике компании: Название отдела, в котором работает служащий; Размер полученной сотрудником заработной платы; Размер иных доходов сотрудника; Перечень курсов, которые посещал служащий компании в целях повышения квалификации. Структура базы даны, включая отношения между таблицами, приведена на рис. 1.1. Рис. 1.1. Структура базы данных Чтобы создать новую базу данных выполните следующие действия: 1. Запустите интегрированную среду разработки Visual Studio.NET. 2. В левой части окна Visual Studio откройте окно Server Explorer, выбрав команду меню View - Server Explorer. 3. В этом окне раскройте узел Servers, найдите ваш компьютер, а затем раскройте узел SQL SERVER и найдите в нем экземпляр SQL Server, который установлен на вашем компьютере, как показано на рисунке. 4. Для создания новой базы данных щелкните правой кнопкой мыши на имени экземпляра SQL Server, который установлен на вашем компьютере. В контекстном меню выберите команду New Database (создать новую базу данных). 5. На экране появится диалоговое окно Create Database (создать базу данных). Введите имя новой базы данных и щелкните OK. 6. После этого в окне Server Explorer появится новая база данных. В нашем случае HumanResources. При раскрытии узла этой базы данных будут отображены следующие категории объектов базы данных: Database Diagrams (Диаграммы базы данных); Tables (Таблицы); Views (Представления); Stored Procedures (Хранимые процедуры); Functions (Функции). 7. Для создания таблиц нашей базы данных щелкните правой кнопкой мыши на узле Tables базы данных HumanResources, а затем из контекстного меню выберите команду New Tables (Создать таблицы). В соответствии с моделью на рис. 1.1. создайте таблицы средствами Visual Studio. На рис. 1.2 представлены поля таблицы Departmens. После ввода названия поля, следует указать его тип и размер. Если значения, вводимые в данное поле, будут уникальными, то необходимо указать, что поле является ключевым. Для этого щелкните на поле правой кнопкой мыши и выберите команду меню Diagram-Set Primary Key. Рис. 1.2. Создание структуры таблицы с помощью Visual Studio.NET Далее нужно указать, что ключевое поле (если это первичный ключ) используется в SQL Server в целях автоматической генерации идентификационных значений. Щелкните правой кнопкой мыши на окне с определением таблицы (можно в строку с первичным ключом таблицы) и выберите в контекстном меню Index/Keys (индексы/ключи)/. После этого на экране появится диалоговое окно Property Pages, имеющее вкладку Index/Keys. В списке Table Identify Column выберите ключевое поле вашей таблицы. После установления первичных ключей таблицу можно сохранить: File-Save Table1. В диалоговом окне Choose Name введите имя и нажмите OK. Обратите внимание, что ваша таблица появилась в окне компонента Server Explorer. Проделайте аналогичные процедуры создания для всех таблиц нашей модели. 8. Теперь, когда вы создали таблицы, осталось определить индексы (index). Индексы присваиваются полю, чтобы облегчить выборку данных на основе информации, хранимой в поле, например, в случае установления индекса для поля фамилия, поиск клиента по этому полю будет выполняться быстрее. Следует иметь в виду, что индексы увеличивают размер базы данных, то есть объем хранимой информации и объем занимаемой памяти, что приводит к снижению скорости работы компьютера. Злоупотреблять количеством индексов не следует. Для того чтобы добавить индекс к одному из полей таблицы следует выполнить перечисленные ниже действия: Щелкните правой кнопкой мыши на окне с определением таблицы, например, Employees и выберите в контекстном меню команду Index/Keys. После этого на экране появится страница свойств со списком существующих индексов, в котором уже присутствует индекс первичного ключа EmployeeId. Щелкните на кнопке New (Создать) для создания нового индекса для поля, например, FirstName. В списке полей выберите поле FirstName, как показано на рис.1.3, а затем щелкните на кнопке Close. Повторите действия из пп. 1-3, чтобы создать все остальные индексы. В нижней части диалогового окна Property Pages находится параметр Create UNIQUE (Создать уникальный индекс). Не устанавливайте флажок для этого параметра, потому что в таком случае в таблицу можно будет вводить только разные имена сотрудников. Уникальные индексы следует создавать только для того, чтобы гарантировать уникальность значений данного поля. 9. Для сохранения внесенных изменений в базу данных выберите команду меню File Save Employees. После успешного сохранения внесенных изменений закройте окно создания базы данных visual Studio.NET. Рис. 1.3. Диалоговое окно Property Pages после определения индекса для поля FirstName 10. Создание схемы базы данных. Схема базы данных (database diagram) - это визуальное представление таблиц в базе данных. Для создания таблиц и отношений между ними можно использовать инструменты SQL Server, но можно использовать средства среды Visual Studio.NET. Разверните узел нашей базы данных HumanResources в окне Server Explorer, щелкнув правой кнопкой мыши на узле Database Diagrams, и выберите в контекстном меню команду New Diagram (создать схему). В диалоговом окне Add Table (Добавить таблицу) будет приведен список таблиц вашей базы данных. Выберите все и/или нужные таблицы и щелкните на кнопке Add (Добавить), а затем на кнопке Close. 11. Для установления связей между таблицами следует установить связь (отношение) между ними следующим образом: щелкнуть на ключевом поле таблицы, например, Employees и перетащить его к полю DepartmentHeadId в таблице Departments. На экране появится диалоговое окно Create relationship (создать отношения), в котором можно указать свойства отношения между двумя таблицами. В результате будет создана новая схема базы данных (рис. 1.4). Рис. 1.4. Схема базы данных с установленными отношениями между таблицами 12. Для сохранения созданной схемы базы данных Database Diagram1 выберите команду File - Save DatabaseDiagram1. В диалоговом окне Save New Database Diagram (сохраните имя новой схемы базы данных) введите имя, например, Relationships для новой схемы базы данных. После создания базы данных можно приступить к написанию программного кода для нашего приложения. Сразу необходимо заметить, что приложение, которое мы с вами сейчас начнем разрабатывать, не будет полнофункциональным приложением. Целью изучаемого примера является всего лишь иллюстрация изученных ранее объектов для построения приложения по обработке информации. В приложении создадим единственную форму для работы с записями таблицы Employees. Форма под названием EmployeesDetails будет предназначена для добавления или удаления служащих компании, редактирования личной информации о служащих, включая принадлежность к тому или иному отделу, и текущий уровень заработной платы. Самостоятельно 1. Средствами Visual Studio.NET создайте базу данных Dogovor по модели, представленной на рис. 1.5. 2. Так как база данных Dogovor является основой для выполнения всех последующих заданий текущего практикума, внесите в нее данные (непосредственно в таблицы). Рис. 1.5. База данных Dogovor Задание 2. Основные объекты провайдера данных: объект Connection Цель Познакомиться с объектом Connection, обеспечивающим соединение с заданным источником данных. Решение Необходимо рассмотреть объекты OleDbConnection, SqlConnection и OdbcConnection, реализующие одинаковые интерфейсы, но имеющие различную реализацию. Обсуждение Объект Connection служит для установления соединения с заданным источником данных и с указанным в строке подключения учетным именем и паролем. Соединение можно настроить, редактируя нужным образом значения параметров строки подключения. Объекты OleDbConnection, SqlConnection и OdbcConnection реализуют одинаковые интерфейсы, но все же имеют разные реализации, то есть имеют разный формат строки подключения. В объектах OleDbConnection и OdbcConnection используется стандартный формат строки подключения OLEDB или ODBC с незначительными исключениями. Пример формирования строки подключения для провайдера OleDbConnection рассмотрен ниже. Объект OleDbConnection представляет уникальное подключение к источнику данных. Для клиент-серверных систем баз данных этот объект является эквивалентом сетевого подключения к серверу. В зависимости от функциональных возможностей, поддерживаемых собственным поставщиком OLE DB, некоторые методы или свойства объекта OleDbConnection могут оказаться недоступными. При создании экземпляра OleDbConnection для всех свойств задаются начальные значения. Если объект OleDbConnection выходит за границы области действия, он не закрывается. Поэтому необходимо явно закрыть подключение, вызвав методы объекта OleDbConnection Close или Dispose. Dim myConnection As New OleDbConnection(myConnectionString) myConnectionString = "Provider=SQLOLEDB;Data Source=localhost;Initial Catalog=Northwind;Integrated Security=SSPI;" В приведенной ниже таблице представлены параметры объекта ConnectionString. Имя Initial Database Описание Catalog| Имя базы данных Если имеет значение false, в подключении определяются идентификатор и пароль пользователя. Если имеет значение true, для Integrated Security| аутентификации используются текущие учетные Trusted_Connection данные Windows. По умолчанию false. Распознаваемые значения: true, false, yes, no и sspi (настоятельно рекомендуется), что эквивалентно true. Password| Pwd Пароль для подключения к SQL Server (не рекомендуется). Для поддержки наивысшего уровня безопасности настоятельно рекомендуется использовать вместо этого зарезервированные слова "Integrated Security" или "Trusted_Connection"). Persist Security Info Если задано значение false или no (настоятельно рекомендуется) сведения о безопасности (например, пароль) не возвращаются как часть подключения, если оно открыто или когда-либо находилось в открытом состоянии. По умолчанию false. Идентификатор пользователя Имя для подключения к SQL Server (не рекомендуется). Для поддержки наивысшего уровня безопасности рекомендуется использовать зарезервированные слова "Integrated Security" или "Trusted_Connection". Workstation ID Имя рабочей станции, подключающейся к SQL Server (по умолчаниюимя локального компьютера). При задании зарезервированных слов, требующих логических значений, можно использовать yes вместо true и no вместо false. Целочисленные значения предоставляются в виде строк. В объекте SqlConnection используется несколько иной формат строки подключения, который имеет отношение только к SQL Server версии 7.0 и выше. Синтаксис ConnectionString аналогичен, но не идентичен строке подключения OLE DB. В отличие от OLE DB, возвращаемая строка подключения совпадает с задаваемой пользователем ConnectionString, за исключением сведений о безопасности в случае, если Persist Security Info имеет значение false (значение по умолчанию). Провайдер данных .NET Framework для SQL Server не сохраняет и не возвращает пароль в строке подключения, если значение Persist Security Info отлично от true. Для подключения к базе данных можно использовать свойство ConnectionString. В следующем примере показана типичная строка подключения. Public Sub CreateSqlConnection() Dim myConnection As New SqlConnection() myConnection.ConnectionString = "Persist Security Info=False;Integrated Security=SSPI;database=northwind;server=mySQLServer;Connect Timeout=30" myConnection.Open() End Sub 'CreateSqlConnection Самостоятельно 1. Создайте проект, в форме которого поместите текстовое окно и командную кнопку (рис. 1.6). 2. Для командной кнопки введите программный код, реализующий: Создание экземпляра объекта Connection; Создание строки подключения; Отображение состояния подключения; Открытие подключения; Отображений состояния подключения. Пример кода, реализующего подключение к базе данных с помощью провайдера SqlClient, представлен ниже. 3. Самостоятельно напишите подключение к базам данных с использование других провайдеров: OleDb и ODBC. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim cnn As New SqlConnection cnn.ConnectionString = "Persist Security Info=False; Integrated Security=SSPI; database=Dogovor; server=abrzh" TextBox1.Clear() If (cnn.State = System.Data.ConnectionState.Open) Then TextBox1.Text = TextBox1.Text & "Connection is Open" Else TextBox1.Text = TextBox1.Text & "Connection is Closed" End If TextBox1.Text = TextBox1.Text & ControlChars.CrLf TextBox1.Text = TextBox1.Text & "Opening SQL connection.." & ControlChars.CrLf & ControlChars.CrLf cnn.Open() If (cnn.State = System.Data.ConnectionState.Open) Then TextBox1.Text = TextBox1.Text & "Connection is Open" Else TextBox1.Text = TextBox1.Text & "Connection is Closed" End If cnn.Close() End Sub 4. После запуска полученного приложения и щелчка по кнопке в текстовом поле появится строки о закрытии подключения, о состоянии подключения и повторном открытии подключения как показано на рис. 1.6. Рис. 1.6. Запуск приложения с подключением к базе данных Задание 3. Основные объекты провайдера данных: объект Command Цель Познакомиться с объектом Command, обеспечивающим выполнение команд по отношению к источнику данных, а также получение возвращенных данных или результатов выполнения команд. Решение Необходимо рассмотреть объекты OleDbCommand, SqlCommand и OdbcCommand, реализующие одинаковые интерфейсы, но имеющие различную реализацию. Обсуждение Объект Command служит для выполнения команд по отношению к источнику данных. Объект имеет следующие свойства: CommandText и CommandType для определения текста и типа фактической команды; Connection для указания подключения, используемого для выполнения команды; CommandTimeout для указания времени ожидания, по истечению которого команда отменяется и выдается сообщение об ошибке; Parameters для коллекции параметров команды; Transaction для указания транзакции, в которой используется данная команда. Все три версии объекта Command имеют идентичные свойства и методы, за исключением того, что объект SqlCommand имеет дополнительный метод, которого нет у двух других вариантов этого объекта, а именно ExecuteXMLReader. Он использует преимущества SQL Server для автоматического возвращения данных в формате XML (если в запрос SQL добавлено предложение FOR XML). Еще одно отличие между версиями объекта Command для разных провайдеров данных заключается в использовании свойства CommandType. Все они поддерживают значения Text и StoredProcedure, а объекты OleDbCommand поддерживают еще одно, третье возможное значение - TableDirect. Это позволяет эффективно загружать все содержимое таблицы за счет установки значения TableDirect для свойства CommandType и имени таблицы для свойства CommandText. Самостоятельно 1. Продолжим работу с проектом задания 2. Добавим в форму еще одну командную кнопку, которая будет выполнять подключение к базе данных и инициировать объект Command. 2. В обработчике события кнопки запишем следующий программный код. Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click Dim cnn As New SqlConnection cnn.ConnectionString = "Persist Security Info=False;Integrated Security=SSPI;database=Dogovor;server=abrzh" Dim cmd As New SqlCommand TextBox1.Clear() cnn.Open() cmd.Connection = cnn cmd.CommandType = CommandType.Text cmd.CommandText = "Select dogovor_Num, tovar from Dogovor" TextBox1.Text = "Command Strind:" & ControlChars.CrLf TextBox1.Text = TextBox1.Text & ControlChars.Tab & cmd.CommandText & ControlChars.CrLf cnn.Close() End Sub 3. Попробуйте запустить проект, нажмите кнопку Command. Измените содержимое строки CommandText и убедитесь, что вы записали ее верно (рис. 1.7). Рис. 1.7. Открытие подключения к базе данных и подготовка объекта Command Задание 4. Применение объекта Command с параметрами и хранимыми процедурами Цель Изучить способы использования объекта Command для выполнения запросов или хранимых процедур к источнику данных с передачей входных и выходных параметров. Решение Использовать свойство Parameters объекта Command. Обсуждение Теперь рассмотрим применение объекта Command с параметрами и хранимыми процедурами. При создании запросов или команд часто требуется передавать значения параметров действия (обновление, вставка или удаление данных) или хранимой процедуры. Для решения этой проблемы в объекте Command предусмотрено свойство Parameters, которое является объектом ParameterCollection и содержит коллекцию объектов-параметров Parameter. Способы программирования объекта SqlParameterCollection и использование объектов ODBCParameterCollection, OleParameterCollection имеют существенные отличия. Объекты OdbcParameterCollection и OleDbParameterCollection основаны на позиционных параметрах, а объект SqlParameterCollection - на именованных параметрах. Эти различия в значительной степени влияют на способ определения запросов и параметров. Создадим простой запрос с параметрами для извлечения из базы данных dogovor поставщиков заказа с заданным номером. С одной стороны, при использовании провайдера данных OLEDB или ODBC запрос будет иметь следующий вид: Select postavshik dogovor_Num=? FROM dogovor WHERE Здесь вопросительный знак заменяет один параметр, а для нескольких параметров можно использовать несколько вопросительных знаков. Порядок расположения параметров важен. С другой стороны, при использовании провайдера данных SqlClient запрос будет выглядеть следующим образом: Select postavshik FROM dogovor_Num=@MyParam dogovor WHERE Здесь заменителем параметра является его имя, а дополнительные параметры также обозначаются их именами, поэтому порядок расположения параметров не важен. Объект Parameter можно создавать явно, используя для этого конструктор New Parameter, или передавая нужные аргументы методу Add объекту ParameterCollection (то есть свойству parameters объекта Command). Помните, что оба метода (конструктор Parameter и метод Add) имеют перегруженные версии, то есть, вы можете пользоваться любой версией из списка предлагаемых .NET. Внесите изменения в код кнопки Command, как показано ниже. После запуска программы и щелчка по кнопке Command в текстовом поле появится текст запроса, а также имя и значение параметра (рис. 1.8). Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click Dim cnn As New SqlConnection("Persist Security Info=False;Integrated Security=SSPI;database=Dogovor;server=abrzh") Dim cmd As New SqlCommand Dim prm As New SqlParameter TextBox1.Clear() cnn.Open() cmd.Connection = cnn cmd.CommandType = CommandType.Text cmd.CommandText = "Select postavshik from Dogovor where tovar=@MyParam" cmd.Parameters.Add(New SqlParameter("@MyParam", SqlDbType.VarChar, 50)) cmd.Parameters("@MyParam").Value = "товар1" TextBox1.Text = "Command Strind:" & ControlChars.CrLf TextBox1.Text = TextBox1.Text & ControlChars.Tab & cmd.CommandText & ControlChars.CrLf TextBox1.Text = TextBox1.Text & "Command parameters:" & ControlChars.CrLf For Each prm In cmd.Parameters TextBox1.Text = TextBox1.Text & ControlChars.Tab & prm.ParameterName & " = " & prm.Value & ControlChars.CrLf Next cnn.Close() End Sub Рис. 1.8. Применение объекта Command с параметрами Аналогично вызываются хранимые процедуры, за исключением того, что вместо свойства CommandType.Text используется свойство CommandType.StoredProcedure, а имя хранимой процедуры присваивается свойству CommandText. Создание хранимой процедуры возможно с помощью программы SQL SERVER Enterprise Manager или программы SQL Query Analyzer. Но Visual Studio.NET также располагает средствами разработки таких процедур. 4. Для создания хранимой процедуры средствами Visual Studio.NET выберите на вкладке Server Explorer команду Stored Procedure - New Stored Procedure (рис. 1.9). Рис. 1.9. Шаблон для создания хранимой процедуры 5. В появившемся шаблоне следует описать входные и выходные параметры (если требуются), а для создания текста самой процедуры следует перейти по правой кнопке с помощью команды Insert SQL в дизайнер SQL и построить текст процедуры в графическом режиме (рис. 1.10). Рис. 1.10. Графический дизайнер SQL для построения хранимых процедур 6. Для выхода из дизайнера необходимо сохранить процедуру (File - Save Stored Procedure) и закрыть окно дизайнера, тогда вы снова окажетесь в окне кода хранимой процедуры (рис. 1.11). Рис. 1.11. Код хранимой процедуры 7. Для нашего примера создадим хранимую процедуру StoredProcedure4, выполняющую следующие команды: ALTER AS SELECT FROM GROUP HAVING RETURN Код вызова следующий PROCEDURE dbo.StoredProcedure4 BY (COUNT(dogovor_Num) хранимой процедуры postavshik Dogovor postavshik > 1) может Dim cnn As New SqlConnection("Persist Info=False;Integrated иметь вид: Security Security=SSPI;database=Dogovor;server=abrzh") cnn.Open() Dim cmd As New SqlCommand cmd.Connection = cnn cmd.CommandType = CommandType.StoredProcedure cmd.CommandText = "StoredProcedure4" cmd.CommandType = CommandType.StoredProcedure В случае вызова хранимой процедуры с параметрами необходимо описать создание параметра, добавление его в коллекцию Parameters и указать его значение. Рассмотрим, как изменится код самой хранимой процедуры и код вызова этой процедуры в проекте. 8. Текст хранимой процедуры для поиска поставщиков, имеющих некоторое (задаваемое пользователем) количество договоров поставки: ALTER @par AS SELECT FROM GROUP HAVING RETURN PROCEDURE BY (COUNT(dogovor_Num) StoredProcedure4 int postavshik Dogovor postavshik > @par) 9. При наличии входного параметра, код вызова хранимой процедуры несколько изменится: Dim cnn As New SqlConnection("Persist Security Info=False;Integrated Security=SSPI;database=Dogovor;server=abrzh") cnn.Open() Dim cmd As New SqlCommand Dim reader As SqlDataReader cmd.Connection = cnn cmd.CommandType = CommandType.StoredProcedure cmd.CommandText = "StoredProcedure4" cmd.CommandType = CommandType.StoredProcedure cmd.Parameters.Add(New SqlParameter("@par", SqlDbType.Int, 4)) cmd.Parameters("@par").Direction = ParameterDirection.Input cmd.Parameters("@par").Value = 1 Самостоятельно 1. Напишите хранимую процедуру с параметрами. 2. Опишите вызов этой процедуры с помощью свойств объекта Command. Для ввода значений входных параметров используйте элементы интерфейса (Textbox или InputBox). Задание 5. Выполнение команд: метод ExecuteNonQuery Цель Выполнить команды по отношению к источнику данных с помощью объекта Command и его методов. Решение Использовать один из методов объекта Command: метод ExecuteNonQuery. Обсуждение Теперь пришло время рассмотреть, каким образом команды выполняются. Для этого у объекта Command имеются методы: Метод ExecuteNonQuery - выполняет команду SQL и не возвращает значение. Метод ExecuteScalar - выполняет команду SQL и возвращает первое поле первой записи. Метод ExecuteReader - выполняет команду SQL и возвращает набор записей с помощью объекта DataReader. Метод ExecuteXMLReader (только для SqlCommand) - выполняет команду SQL и возвращает набор записей в формате XML с помощью объекта XMLReader. Метод ExecuteNonQuery возвращает только целочисленное значение, сообщающее об успешном или неудачном исходе выполнения команды. При удачном выполнении DDL-команд определения данных для изменения структуры баз данных возвращается значение -1, а при удачном выполнении DML-команд управления данными для их обновления, вставки или удаления возвращается количество строк, задействованных в команде. При неудачном выполнении обоих типов возвращается значение 0. 1. Продолжая работу с проектом, попробуем использовать объекты пространства имен SQLClient и базу данных Dogovor. Наша задача - изменить стоимость всех товаров на 10%. Введите для новой кнопки формы следующий код, выполняющий обновление таблицы базы данных с помощью команды UPDATE с параметром. Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click Dim cmd As New SqlCommand, cnn As New SqlConnection cnn.ConnectionString = "Persist Security Info=False;Integrated Security=SSPI;database=Dogovor;server=abrzh" TextBox1.Clear() cmd.Connection = cnn cmd.CommandText = "UPDATE Tovar SET cena=cena*@par" cmd.Parameters.Add(New SqlParameter("@par", SqlDbType.Float, 4)) cmd.Parameters("@par").Direction = ParameterDirection.Input cmd.Parameters("@par").Value = 1.1 cnn.Open() Dim i As Integer i = cmd.ExecuteNonQuery() If i <> 0 Then TextBox1.Text = "цена товара увеличена" Else TextBox1.Text = "не получилось" End If cnn.Close() End Sub 2. Теперь таблицу со стоимостью товаров в базе данных Dogovor можно обновить, запустив приложение. Значение параметра можно вводить из текстового окна . cmd.Parameters("@par").Value = val(Textbox1.Text ) 3. После запуска приложения и щелчка по кнопке Button3 вы должны увидеть в текстовом окне сообщение об успешном или неудачном исходе, а сами значения стоимости товаров можно посмотреть с помощью Server Explorer непосредственно в таблице Tovar (рис. 1.12). Если таблица Tovar была открыта, то изменения в столбце Tovar сразу не будут отображены. Необходимо или закрыть и открыть таблицу Tovar, или вызвать по правой кнопке мыши команду Refresh (Обновить) для этой таблицы. Рис. 1.12. Обновление таблицы базы данных с помощью объектов SQLClient Самостоятельно 4. Выполните обновление стоимости товаров с помощью хранимой процедуры, написанной самостоятельно. Использование хранимых процедур позволяет добиться более высокой производительности обработки баз данных и централизованно хранить код. 5. При создании хранимой процедуры обязательно укажите тип SQL команды: в нашем случае это команда на изменение записей. Тип укажите в раскрывающемся списке Change Type как показано на рис. 1.13. Примечание Из практического опыта организации ввода входных значений параметров хранимой процедуры: при вводе значений параметров из интерфейсных элементов, например, TextBox, необходимо очищать элемент или вводить значения параметров через окно стандартного диалога InputBox. Рис. 1.13. Указание типа команды SQL Задание 6. Выполнение команд: метод ExecuteScalar Цель Выполнить команды по отношению к источнику данных с помощью объекта Command и его методов. Решение Использовать один из методов объекта Command: метод ExecuteScalar. Обсуждение Метод ExecuteScalar - выполняет команду SQL и возвращает первое поле первой записи. В каких случаях используется этот метод? Иногда требуется выполнить команду, которая возвращает скалярное значение, то есть только одно значение. Типичными примерами являются команды SQL для вычисления суммы всех значений или общего количества записей (Sum или Count). Другими примерами являются справочные таблицы для подстановки одного значения или команды, возвращающие логические значения. Метод ExecuteScalar выполняет заданную команду и возвращает значение только первого поля первой записи, остальные записи игнорирует. 1. Напишем для нашей базы данных Dogovor хранимую процедуру для подсчета количества договоров на поставку товара определенного типа, например, 'Товар1'. ALTER AS SELECT PROCEDURE COUNT(*) dbo.StoredProcedure3 AS Expr1 FROM WHERE RETURN (tovar = Dogovor 'Товар1') 2. Для вызова хранимой процедуры с помощью провайдера данных SQLClient выполните следующие действия: Создайте дополнительную кнопку и назовите ее ExeciteScalar. Создайте код обработчика события щелчка по этой кнопке. Код приведен ниже. Запустите приложение и просмотрите результат. Измените содержимое таблицы Dogovor и снова запустите приложение. На рис. 1.14 представлен результат поиска записей в таблице базы данных и содержание самой таблицы. Private Sub Button5_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button5.Click Dim cnn As New SqlConnection("Persist Security Info=False;Integrated Security=SSPI;database=Dogovor;server=abrzh") Dim cmd As New SqlCommand cmd.Connection = cnn cmd.CommandType = CommandType.StoredProcedure cmd.CommandText = "StoredProcedure3" cnn.Open() Dim result As Integer result = cmd.ExecuteScalar TextBox1.Text = cnn.State.ToString TextBox1.Text = TextBox1.Text & ControlChars.CrLf & "Количество договоров " & result.ToString cnn.Close() TextBox1.Text = TextBox1.Text & cnn.State & ControlChars.CrLf End Sub Рис. 1.14. Результат поиска записей методом ExecuteScalar Самостоятельно 3. Напишите свой код извлечения скалярного значения из хранимой процедуры с помощью провайдера OleDb. Задание 7. Выполнение команд: метод ExecuteReader и объект DataReader Цель Выполнить команды по отношению к источнику данных с помощью объекта Command и его методов. Решение Использовать один из методов объекта Command: метод ExecuteReader. Обсуждение Метод ExecuteReader применяется для возвращения набора записей. В большинстве приложений для работы с базами данных именно этот метод используется чаще всего. Работа метода основана на объекте DataReader, с помощью которого записи обрабатываются последовательно одна за другой. Объект DataReader предназначен для чтения в прямом направлении небуферизуемого потока записей, полученных от метода ExecuteReader объекта Command. Объект DataReader предлагает наиболее быстрый способ доступа к источнику данных, но не предусматривает возможности прокрутки и обновления данных. Для перехода к следующей записи объекта DataReader нужно вызвать его метод Read. Доступ к полям каждой записи данных объекта DataReader можно выполнять следующими способами: 1. С помощью строго типизированных методов доступа. Эти методы извлекают значение поля по номеру поля; нумерация полей начинается с нуля, например: X=MyReader.GetInt(1) или X=MyReader.GetString(2). 2. С помощью коллекции Fields, в которой используются имена полей, что значительно упрощают чтение кода программы. X=MyReader("MyField"). Следует иметь в виду, что первый способ извлечения данных повышает производительность программы, так как используется номер поля, что исключает преобразование имени поля в его номер. Рассмотрим применение метода на практике. Для демонстрации способов использования метода ExecuteReader совместно с объектом DataReader добавим в проект еще одну кнопку и введем следующий код: Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click Dim cnn As New OleDbConnection("Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=Dogovor;Data Source=ABRZH;Workstation ID=ABRZH") Dim cmd As New OleDbCommand Dim reader As OleDbDataReader TextBox1.Clear() cmd.Connection = cnn cmd.CommandType = CommandType.TableDirect cmd.CommandText = "Dogovor" cnn.Open() reader = cmd.ExecuteReader While reader.Read TextBox1.Text = TextBox1.Text & reader("Postavshik") & ControlChars.Tab & reader("tovar") & ControlChars.CrLf End While reader.Close() cnn.Close() End Sub На рис. 1.15 представлен результат извлечения данных с помощью объекта DataReader и метода ExecuteReader. Обратите внимание, на использование значение TableDirect для свойства CommandType объекта Command. Значение TableDirect поддерживается только для провайдера OLE DB. Примечание Каждое обращение к данным с помощью объекта DataReader должно выполняться принудительно - вызовом метода Read(). 3. По окончании использования объекта DataReader следует вызывать метод Close(). Дело в том, что выходные данные или возвращаемые значения для объекта Command недоступны до тех пор, пока объект DataReader открыт. Объект остается открытым до тех пор, пока открыто само подключение или объект DataReader. Рис. 1.15. Извлечение значений полей с помощью метода ExecuteReader Самостоятельно 4. Примените объект DataReader и метод ExecuteReader для провайдера данных SQLClient. Задание 8. Другие объекты провайдеров данных: объект Transaction Цель Научиться выполнять транзакции к базе данных. Решение Провайдеры данных ADO.NET содержат объект Transaction, который имеет фундаментальные методы обработки транзакций. Обсуждение Транзакции используются для гарантированного успешного завершения сразу нескольких операций по принципу "все или ничего". Это значит, что-либо все операции транзакции успешно выполняются, либо они вообще не выполняются. Классическим примером транзакции является банковская операция перечисления денежных средств. Эта операция состоит из двух этапов: удержание денежной суммы с одного счета и зачисление ее на другой счет. При этом желательно избегать ситуаций, когда успешно выполняется только первый этап транзакции. Провайдеры данных ADO.NET содержат объект Transaction, который имеет фундаментальные методы обработки транзакций. Метод Commit фиксирует текущую транзакцию, а метод Rollback - откатывает (отменяет) текущую транзакцию. Выполнение транзакций и создание объекта Transaction осуществляется с помощью вызова метода BeginTransaction по отношению к открытому объекту Connection. Рассмотрим способ использования объекта Transaction на примере архивирования старых договоров по годам. После относительно длительного использования системы управления базами данных некоторые данные рекомендуется архивировать. В каждой рабочей системе операцию архивирования следует включить в состав обязательных и регулярно выполняемых операций резервного копирования. Архивируемые данные - это данные, которые нужны не для постоянного использования (в оперативном режиме), а только изредка. Удаление этих данных из основных оперативных таблиц базы данных может повысить производительность операций доступа к этим таблицам, так как при этом приходится обрабатывать и фильтровать меньше записей. Однако архивная таблица часто хранится в идентичном формате таблицы и доступ к ней в случае необходимости можно организовать аналогичным образом. Создадим простую форму для архивирования данных из таблицы Dogovor. Форма должна позволять выбирать и архивировать договоры по годам: Сначала в базе данных создается новая таблица DogovorXXXX, где XXXX обозначает тот год, записи о договорах которого будут архивироваться. Затем все записи о договорах за указанный год копируются из таблицы Dogovor в таблицу DogovorXXXX. Все скопированные записи о договорах за указанный год удаляются из таблицы Dogovor. Проблема здесь заключается в том, чтобы отменить всю транзакцию при неудачном выполнении какой-либо ее операции. Нам не нужна новая таблица, если в нее нельзя скопировать данные. Не нужно архивировать данные, если они не удаляются из основной таблицы. Аналогично, не нужно удалять никакие записи из основной таблицы, если они не скопированы в архивную таблицу. Для решения этой задачи можно использовать объект Transaction и методы этого объекта, позволяющие вернуть (откатить) базу данных в исходное состояние в случае сбоя каких-то операций. 1. Создайте форму, включающую поле со списком, кнопки Ok и Cancel (рис. 1.16). Не забудьте импортировать в код формы пространство имен System.Data.SQLClient. 2. Для кнопок введите код, представленный ниже. Обратите внимание, что процедура обработки события Form_Load инициирует список ListBox значениями, из которых можно выбрать год архивирования и устанавливает год по умолчанию. Рис. 1.16. Расположение элементов управления в форме Архивные договоры Imports System.Data.SqlClient Private Sub Form3_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load ListBox1.Items.Add("2001") ListBox1.Items.Add("2002") ListBox1.Items.Add("2003") ListBox1.Items.Add("2004") ListBox1.SelectedIndex = 0 End Sub Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click Me.Close() End Sub Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim sql As String Dim result As Integer Dim records As Integer Dim SelectedYear As String Dim cnn As New SqlConnection("Persist Security Info=False;Integrated Security=SSPI;database=Dogovor;server=abrzh") Dim cmd As New SqlCommand Dim trans As SqlTransaction SelectedYear = ListBox1.SelectedItem 'Открытие объекта Connection и запуск транзакции cnn.Open() trans = cnn.BeginTransaction 'Включение команды в транзакцию cmd.Connection = cnn cmd.Transaction = trans 'Указание команды SQL для вставки соответствующих записей в архивную таблицу sql = "SELECT * INTO Dogovor" & SelectedYear & _ " From Dogovor WHERE year(dogovorDate)= " & SelectedYear 'Передача текста команды SQL в транзакцию cmd.CommandText = sql result = cmd.ExecuteNonQuery 'Отображение результатов вставки записей в архивную таблицу If result > 0 Then records = result MsgBox(records & "записей вставлено в таблицу успешно за " & SelectedYear) Else MsgBox("Записи не вставлены в таблицу за " & SelectedYear) 'при отсутствиии записей транзакцию следует прекратить trans.Rollback() End If If records > 0 Then 'Удаление записей из исходной таблицы sql = "DELETE FROM Dogovor WHERE year(dogovorDate)=" & SelectedYear 'Эта команда находится в той же транзакции cmd.CommandText = sql result = cmd.ExecuteNonQuery 'Показать результаты удаления If result = records Then MsgBox(records & "записей удалено") 'Все действия усрешно выполнены. Фиксируем транзакцию trans.Commit() Else MsgBox("Записи не удалены") End If Else End If cnn.Close() End Sub End Class Поскольку транзакции определяются на уровне подключения, то сначала нужно открыть подключение, а затем создать объект Transaction с помощью вызова метода BeginTransaction для открытого подключения. Объекты Connection и Transaction присваиваются объекту Command, который будет использоваться для выполнения команд по отношению к базе данных. Первые два этапа создания архивной таблицы и копирования выбранных строк выполняются с помощью одной команды SELECT, которая содержит предложение INTO имя_таблицы. Указанная таким образом таблица создается автоматически, а если такая таблица уже существует, то генерируется исключительная ситуация. Выбранное значение года добавляется к имени таблицы Dogovor для создания имени новой архивной таблицы. Команда SELECT INTO не создает индекс, если он существует в исходной таблице. Для повышения производительности выполнения запросов по отношению к данной таблице, вероятно, придется создавать индексы по одному или нескольким полям. Для выполнения команды SQL вызывается метод ExecuteNonQuery(), который возвращает количество охваченных запросом записей. Если это возвращаемое значение больше нуля, следовательно, нужные записи найдены и вставлены в новую таблицу. В противном случае это значит, что таблица не может быть создана либо для копирования не найдено никаких записей. В любом из этих случаев транзакция откатывается, даже если таблица создана, чтобы база данных не наполнялась пустыми и бесполезными таблицами. Если хотя бы одна запись скопирована в таблицу, то ее прототип в исходной таблице Dogovor удаляется с помощью команды DELETE, содержащей предложение WHERE с заданным годом. В случае успешного выполнения этой команды, то есть, если количество скопированных и удаленных строк совпадает, транзакция считается успешно завершенной и фиксируется. В противном случае, то есть, если какая-то отдельная операция завершается неудачно (нарушится процесс удаления записей, будет отменено разрешение на удаление архивируемых данных или произойдет сбой сервера), вся транзакция будет отвергнута. Откат транзакции гарантирует, что при неудачной попытке удаления корректных записей из таблицы Dogovor архивная таблица DogovorXXXX будет удалена. Самостоятельно 3. Добавьте в таблицу Dogovor поле dogovor_Data для хранения даты договора и внесите в него информацию. Выполните все описанные выше действия и поэкспериментируйте с этим приложением, создавая архивы для записей разных лет. 4. Для проверки полученных результатов сопоставьте содержимое новой архивной и исходной таблиц до и после архивирования (рис. 1.17). Рис. 1.17. Содержание исходной таблицы до начала архивирования, исходной и архивной таблиц после завершения архивирования