Что такое ado. Использование библиотеки ADO (Microsoft ActiveX Data Object). Провайдер данных Odbc

Тесты, проделанные нами на прошлом семинаре, как правило, проводятся не вручную. Для целей тестирования пишут специальную программу - тестовый драйвер, который и проводит тестирование. Более того, такие программы часто пишутся на другом языке, нежели тестируемая программа, или создаются автоматически, с помощью специальных утилит.

На этом семинаре мы сами напишем простой тестовый драйвер на C# для тестирования функций "Калькулятора", используя спецификацию второго семинара.

Замечание . Код программы слегка изменен для упрощения компиляции отдельных модулей. Так, исключена работа с переменной Program.res , а класс CalcClass объявлен как public .

Для начала рассмотрим функцию деления. Тест-требования к ней мы уже составили. Для простоты будем пользоваться лишь четырьмя общими тест- требованиями.

  1. Оба входных параметра принадлежат допустимой области , и выходное значение принадлежит допустимой области .
  2. Первый входной параметр принадлежит допустимой области , второй не принадлежит допустимой области
  3. Первый входной параметр не принадлежит допустимой области , второй принадлежит допустимой области
  4. Оба входных параметров принадлежат допустимой области , а значение функции не принадлежит допустимой области .

Составим программу:

Private void buttonStartDel_Click(object sender, EventArgs e) { try { richTextBox1.Text = ""; richTextBox1.Text += "Test Case 1\n"; richTextBox1.Text += "Входные данные: a= 78508, b = -304\n"; richTextBox1.Text += "Ожидаемый результат: res = 78204 && error = \"\""+"\n"; int res = CalcClass.Add(78508, -304); string error = CalcClass.lastError; richTextBox1.Text += "Код ошибки: " + error + "\n"; richTextBox1.Text += "Получившийся результат: " +"res = "+ res.ToString() +" error = "+error.ToString() +"\n"; if (res == 78204 && error == "") { richTextBox1.Text += "Тест пройден\n\n"; } else { richTextBox1.Text += "Тест не пройден\n\n"; } } catch (Exception ex) { richTextBox1.Text += "Перехвачено исключение: " + ex.ToString() + "\nТест не пройден.\n"; } try { richTextBox1.Text += "Test Case 2\n"; richTextBox1.Text += "Входные данные: a= -2850800078, b = 3000000000\n"; richTextBox1.Text += "Ожидаемый результат: res = 0 && error = \"Error 06\"\n"; int res = CalcClass.Add(-2850800078, 3000000000); string error = CalcClass.lastError; richTextBox1.Text += "Код ошибки: " + error + "\n"; richTextBox1.Text += "Получившийся результат: " + "res = " + res.ToString() + " error = " + error.ToString() + "\n"; if (res == 0 && error == "Error 06") { richTextBox1.Text += "Тест пройден\n\n"; } else { richTextBox1.Text += "Тест не пройден\n\n"; } } catch (Exception ex) { richTextBox1.Text += "Перехвачено исключение: " + ex.ToString() + "\nТест не пройден.\n"; } try { richTextBox1.Text += "Test Case 3\n"; richTextBox1.Text += "Входные данные: a= 3000000000, b = - 2850800078\n"; richTextBox1.Text += "Ожидаемый результат: res = 0 && error = \"Error 06\"\n"; int res = CalcClass.Add(3000000000, -2850800078); string error = CalcClass.lastError; richTextBox1.Text += "Код ошибки: " + error+"\n"; richTextBox1.Text += "Получившийся результат: " + "res = " + res.ToString() + " error = " + error.ToString() + "\n"; if (res == 0 && error == "Error 06") { richTextBox1.Text += "Тест пройден\n\n"; } else { richTextBox1.Text += "Тест не пройден\n\n"; } } catch (Exception ex) { richTextBox1.Text += "Перехвачено исключение: " + ex.ToString() + "\nТест не пройден.\n"; } try { richTextBox1.Text += "Test Case 4\n"; richTextBox1.Text += "Входные данные: a= 2000000000, b = 2000000000\n"; richTextBox1.Text += "Ожидаемый результат: res = 0 && error = \"Error 06\"\n"; int res = CalcClass.Add(2000000000, 2000000000); string error = CalcClass.lastError; richTextBox1.Text += "Код ошибки: " + error +"\n"; richTextBox1.Text += "Получившийся результат: " + "res = " + res.ToString() + " error = " + error.ToString() + "\n"; if (res == 0 && error == "Error 06") { richTextBox1.Text += "Тест пройден\n\n"; } else { richTextBox1.Text += "Тест не пройден\n\n"; } } catch (Exception ex) { richTextBox1.Text += "Перехвачено исключение: " + ex.ToString() + "\nТест не пройден.\n"; } } Листинг 7.1. Текст программы

Каждый тестовый пример находится внутри блока try-catch для того, чтобы перехватить любое сгенерированное исключение внутри методов Add() .

При этом файл CalcClass.dll , в котором и реализованы все математические методы, необходимо добавить в References проекта.

Проведем тестирование и получим следующий результат:

Test Case 1 Входные данные: a= 78508, b = -304 Ожидаемый результат: res = 78204 && error = "" Код ошибки: Получившийся результат: res = 78204 error = Тест пройден Test Case 2 Входные данные: a= -2850800078, b = 3000000000 Ожидаемый результат: res = 0 && error = "Error 06" Код ошибки: Error 06 Получившийся результат: res = 0 error = Error 06 Тест пройден Test Case 3 Входные данные: a= 3000000000, b = -2850800078 Ожидаемый результат: res = 0 && error = "Error 06" Код ошибки: Error 06 Получившийся результат: res = 0 error = Error 06 Тест пройден Test Case 4 Входные данные: a= 2000000000, b = 2000000000 Ожидаемый результат: res = 0 && error = "Error 06" Код ошибки: Error 06 Получившийся результат: res = 0 error = Error 06 Тест пройден

Точно такой же результат мы бы получили и при ручном тестировании , если бы выявленные ошибки были исправлены. Заметим, что при таком подходе к тестированию нам удается локализовать ошибки. Если что-то работает не так, как надо, то можно с уверенностью утверждать, что ошибка содержится именно в функции деления, в то время, как на прошлом семинаре мы не могли сказать, где именно она произошла.

Замечание . Мы считаем, что тестовый драйвер сам не содержит ошибок. Тестирование тестового драйвера выходит за пределы изучаемой темы.

7.4. Раздаточный материал

7.4.1. Программа

Будут выданы .dll файлы, которые нужно протестировать методом "черного ящика", и пример тестового драйвера.

7.5. Домашнее задание

Составить тест-план и провести модульное тестирование следующих методов:

    Нахождение остатка.

Если вы PHP-разработчик, и по разным обстоятельствам тесты для своих приложений не пишете, то эта статья для вас. В ней я постараюсь вкратце показать с чего начать и что делать, чтобы написание тестов приносило вам радость, а вашему приложению стабильность.

Итак, первый совет. Забудьте всё что вы знаете о юнит-тестах. Швырните табуреткой в человека, который сказал вам, что без них не обойтись. Попробуем разобраться, в каких случаях нужно их использовать, а в каких - нецелесообразно.

Я абсолютно уверен, что PHP-программисты редко пишут тесты, потому что начинают не с того конца. Все знают, что тесты это хорошо и клево. Но открыв сайт того же PHPUnit , и прочитав красочный пример о тестировании калькулятора умеющего выполнять операцию a + b, они спрашивают себя: какое отношение этот калькулятор имеет к моему приложению? Ответ: никакого. Ровно как все похожие примеры, на сайтах других фреймворков тестирования. Будто бы все забыли, что PHP прежде всего для веба. Будто бы все пишут калькуляторы, а не сайты на основе MVC-парадигмы.

Положим, вы создаете сайт или разрабатываете веб-приложение. На нем уже есть некоторые страницы, формы, возможно даже интерактивные элементы. Как вы (или, допустим, ваш заказчик) проверяете что сайт работает? Наверняка вы заходите на сайт, кликаете по ссылкам, заполняете формы, смотрите на результат. И по-хорошему, все эти рутинные процессы кликанья и заполнения форм, стоит автоматизировать в первую очередь.

И потому мы поговорим о функциональных тестах (или приемочных).

Функциональное тестирование - это тестирование ПО в целях проверки реализуемости функциональных требований, то есть способности ПО в определённых условиях решать задачи, нужные пользователям. Функциональные требования определяют, что именно делает ПО, какие задачи оно решает.

Википедия

Почему написание тестов стоит начать именно с них? Да просто, чтобы быть уверенным, что ваш сайт функционирует, и всё там работает. Приемочные тесты одинаково хорошо подойдут как для солидного веб-приложения, так и простенького сайта, склепанного за ночь на коленке. А потому начать стоит с них.

Что у нас есть для функционального тестирования? Из всего известного мне, это Codeception и, например, PHPUnit + Mink или прямое использование Selenium. Я хотел включить в этот печерень и Behat , но его автор попросил не использовать Behat для функционального тестирования .

Если бы тестировать с Selenium или PHPUnit + Mink было просто, вы бы уже наверняка их использовали. Потому остановимся на Codeception.

Тест в нем описывается так:

wantTo("see my site is working"); $I->amOnPage("/"); $I->see("Welcome, on my site!"); ?>

Всего в несколько строчек вы проверяете, что как минимум, главная страница вашего сайта открывается. За длительной работой, сломать её, не так и сложно.
Точно так же, вы можете описывать дальнейшие действия:

click("Feedback"); $I->see("Submit your feedback"); $I->fillField("Body","Your site is great!"); $I->click("Submit"); $I->see("Thanks for your feedback!"); ?>

Вот тут мы уже проверяем, что форма отзывов на вашем сайте работает. По крайней мере, пользователю, показывается, что его мнение было учтено.

А теперь попробуем определиться, с unit-тестами.

Модульное тестирование, или юнит-тестирование (англ. unit testing) - процесс в программировании, позволяющий проверить на корректность отдельные модули исходного кода программы.

Идея состоит в том, чтобы писать тесты для каждой нетривиальной функции или метода. Это позволяет достаточно быстро проверить, не привело ли очередное изменение кода к регрессии, то есть к появлению ошибок в уже оттестированных местах программы, а также облегчает обнаружение и устранение таких ошибок.


Википедия .

Беда их использования в том, что обычно веб-приложения основываются на CMS или фреймворках. И порой не всегда получается выделить конкретные модули для тестирования. Особенно неудобно получается, когда логика приложения раскидана по различным классам, а каждый конкретный юнит по сути не делает ничего. Процесс тестирования ещё будет усложнен тем, что все модули как-то взаимосвязаны, имеют много унаследованых методов, конфигураций, и выделить где код ваш, а где код библиотеки порой очень трудно.

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

Например, проверьте, не позволяет ли ваш код создавать двух пользователей с одинаковыми именами. Проверьте, что при попытке создать дубликат приложение не упадет, и не покажет белый экран смерти, а перехватит ошибку и покажет её текст пользователю. Согласитесь, пользователь, может натворить, гораздо больше, чем вы того хотите. А потому предусмотреть все возможные сценарии и покрыти их функциональными тестами порой невозможно.

Или ещё один пример: у вас на сайте акция - каждый 100ый зарегистрированый пользователь получает подарок. Как бы так проверить, что подарки выдаются, и не создавать при этом 100 лишних пользователей? Вот тут уже пишите юнит-тесты. Без них, скорее всего, никак.

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

И ещё: если вы создаете сайт или приложение на основе своего фреймворка, CMS, или каких-то своих модулей к этим фреймворкам и CMS - обязательно пишите к ним модульные тесты. Тут без вариантов. Если же вы используете сторонние популярные решения, а не изобретаете велосипеды, то скорее всего их код уже протестирован автором и зацикливаться на их тестировании не стоит.

Для юнит-тестирования фреймворков много.

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

Сегодня большое значение имеет работа с данными. Для хранения данных используются различные системы управления базами данных: MS SQL Server, Oracle, MySQL и так далее. И большинство крупных приложений так или иначе используют для хранения данных эти системы управления базами данных. Однако чтобы осуществлять связь между базой данных и приложением на C# необходим посредник. И именно таким посредником является технология ADO.NET.

ADO.NET предоставляет собой технологию работы с данными, которая основана на платформе.NET Framework. Эта технология представляет нам набор классов, через которые мы можем отправлять запросы к базам данных, устанавливать подключения, получать ответ от базы данных и производить ряд других операций.

Причем важно отметить, что систем управления баз данных может быть множество. В своей сущности они могут различаться. MS SQL Server, например, для создания запросов использует язык T-SQL, а MySQL и Oracle применяют язык PL-SQL. Разные системы баз данных могут иметь разные типы данных. Также могут различаться какие-то другие моменты. Однако функционал ADO.NET построен таким образом, чтобы предоставить разработчикам унифицированный интерфейс для работы с самыми различными СУБД.

Основу интерфейса взаимодействия с базами данных в ADO.NET представляет ограниченный круг объектов: Connection, Command, DataReader, DataSet и DataAdapter. С помощью объекта Connection происходит установка подключения к источнику данных. Объект Command позволяет выполнять операции с данными из БД. Объект DataReader считывает полученные в результате запроса данные. Объект DataSet предназначен для хранения данных из БД и позволяет работать с ними независимо от БД. И объект DataAdapter является посредником между DataSet и источником данных. Главным образом, через эти объекты и будет идти работа с базой данных.

Однако чтобы использовать один и тот же набор объектов для разных источников данных, необходим соответствующий провайдер данных . Собственно через провайдер данных в ADO.NET и осуществляется взаимодействие с базой данных. Причем для каждого источника данных в ADO.NET может быть свой провайдер, который собственно и определяет конкретную реализацию вышеуказанных классов.

По умолчанию в ADO.NET имеются следующие встроенные провайдеры:

    Провайдер для MS SQL Server

    Провайдер для OLE DB (Предоставляет доступ к некоторым старым версиям MS SQL Server, а также к БД Access, DB2, MySQL и Oracle)

    Провайдер для ODBC (Провайдер для тех источников данных, для которых нет своих провайдеров)

    Провайдер для Oracle

    Провайдер EntityClient. Провайдер данных для технологии ORM Entity Framework

    Провайдер для сервера SQL Server Compact 4.0

Кроме этих провайдеров, которые являются встроенными, существует также множество других, предназначенных для различных баз данных, например, для MySQL.

Основные пространства имен, которые используются в ADO.NET:

    System.Data: определяет классы, интерфейсы, делегаты, которые реализуют архитектуру ADO.NET

    System.Data.Common: содержит классы, общие для всех провайдеров ADO.NET

    System.Data.Design: определяет классы, которые используются для создания своих собственных наборов данных

    System.Data.Odbc: определяет функциональность провайдера данных для ODBC

    System.Data.OleDb: определяет функциональность провайдера данных для OLE DB

    System.Data.Sql: хранит классы, которые поддерживают специфичную для SQL Server функциональность

    System.Data.OracleClient: определяет функциональность провайдера для баз данных Oracle

    System.Data.SqlClient: определяет функциональность провайдера для баз данных MS SQL Server

    System.Data.SqlServerCe: определяет функциональность провайдера для SQL Server Compact 4.0

    System.Data.SqlTypes: содержит классы для типов данных MS SQL Servera

    Microsoft.SqlServer.Server: хранит компоненты для взаимодействия SQL Server и среды CLR

Схематично архитектуру ADO.NET можно представить следующим образом:

Функционально классы ADO.NET можно разбить на два уровня: подключенный и отключенный. Каждый провайдер данных.NET реализует свои версии объектов Connection, Command, DataReader, DataAdapter и ряда других, который составляют подключенный уровень. То есть с помощью них устанавливается подключение к БД и выполняется с ней взаимодействие. Как правило, реализации этих объектов для каждого конкретного провайдера в своем названии имеют префикс, который указывает на провайдер:

Другие классы, такие как DataSet, DataTable, DataRow, DataColumn и ряд других составляют отключенный уровень, так как после извлечения данных в DataSet мы можем работать с этими данными независимо от того, установлено ли подключение или нет. То есть после получения данных из БД приложение может быть отключено от источника данных.

1. Введение

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

2. Обзор ADO

Технология ADO была впервые применена в Microsoft Internet Information Server как интерфейс доступа к БД. Использование ADO позволяет минимизировать сетевой траффик в ключевых Internet-сценариях и уменьшить количество промежуточных уровней между клиентским приложением и источником данных. ADO легко использовать, так как он (или они (объекты) или она (технология)) применяет привычную систему вызовов - интерфейс Автоматизации OLE, доступный сегодня в большинстве средств разработки приложений. Из-за легкости применения и изучения популярность ADO будет расти и в итоге ADO вытеснит технологии RDO и DAO, которые в настоящее время применяются очень широко. Технология ADO во многом подобна RDO и DAO, например, она использует те же соглашения языка. ADO также поддерживает аналогичную семантику и поэтому может быть легко освоена разработчиками ПО.
ADO является интерфейсом программного уровня к OLE DB, новейшей и мощнейшей (сильно сказано) парадигме доступа к данным от MS. OLE DB обеспечивает высокопроизводительный доступ ко многим источникам данных. ADO и OLE DB вместе представляют собой основу стратегии Универсального доступа к данным (Universal Data Access). OLE DB дает возможность универсального доступа ко многим данным и представляет разработчикам возможность сделать это достаточно легко. Так как ADO находится на вершине OLE DB, то применение ADO имеет все преемущества Универсального доступа к данным, которое обеспечивает OLE DB.

3. Обзор OLE DB

OLE DB - это открытая спецификация, разработанная на основе успеха спецификации ODBC и обеспечивает открытый стандарт доступа ко всем видам данным в системах масштаба предприятия. OLE DB - это ядро технологии поддерживающей Универсальный доступ к данным. В отличие от технологии ODBC, которая была создана для доступа к реляционным БД, технология OLE DB разработана для реляционных и нереляционных источников данных, таких как хранилища почты (mail stores), текстов и графики для Web, службы каталогов (directory services), IMS и VSAM хранилищ данных на мэйнфреймах.
Компоненты OLE DB состоят из провайдеров данных (data providers), которые представляют свои данные, потребителей данных (data consumers), которые используют данные, и сервисных компонент (service components), которые обрабатывают и транспортируют данные (например, процессор запросов и механизм курсоров). OLE DB включает в себя мост с ODBC, чтобы дать возможность разработчикам использовать ODBC-драйвера реляционных БД, широко распространенные в настоящее время.
OLE DB провайдеры
Существует два типа OLE DB приложений: потребители и провайдеры. Потребителями могут быть любые приложения, которые используют OLE DB интерфейсы. Например, Visual C++ приложение, которое использует OLE DB интерфейсы для связи с сервером БД - это OLE DB потребитель. Объектная модель ADO, которое использует OLE DB интерфейсы, - это тоже OLE DB потребитель. Любое приложение, которое использует ADO, косвенно использует OLE DB интерфейсы через объекты ADO.
OLE DB провайдер осуществляет OLE DB интерфейсы, поэтому, OLE DB провайдер дает возможность потребителям иметь доступ к данным единообразным способом через ряд документированных интерфейсов. В этом смысле OLE DB провайдер подобен ODBC драйверу, который обеспечивает универсальный механизм доступа к реляционным БД, но только для нереляционных типов данных. Более того, OLE DB провайдер встроен в вершину OLE COM интерфейсов, что придает ему большую гибкость, а ODBC драйвер встроен в вершину C API спецификации.
Microsoft OLE DB SDK version 1.1 поставляет два OLE DB провайдера: ODBC провайдер и провайдер текстов. Провайдер текстов служит примером, который демонстрирует подробную реализацию OLE DB провайдера. ODBC провайдер - это OLE DB провайдер для ODBC драйверов. Этот провайдер предоставляет механизм для потребителей, чтобы использовать существующие ODBC драйверы без необходимости срочной замены существующих ODBC драйверов на новые OLE DB провайдеры. Больше информации про OLE DB и OLE DB провайдеры можно посмотреть на https://www.microsoft.com/data в разделе OLE DB.
ODBC Провайдеры
ODBC провадер устанавливает соответствие между OLE DB интерфейсами и ODBC API. С ODBC провадером OLE DB потребители могут связываться с сервером БД через существующие ODBC драйверы. Потребитель вызывает OLE DB интерфейс через ODBC провайдера. ODBC провайдер вазывает соответствующие ODBC API иннтерфейсы и посылает запросы к ODBC драйверу. Целью разработки ODBC провайдера является осуществление всей функциональности менеджера ODBC драйвера. Поэтому теперь нет необходимости в менеджере ODBC драйвера. Однако при использовании ODBC провайдером версии 1.1 менеджер ODBC драйвера все еще требуется для поддержки связи с ODBC приложениями.

4. Объектная модель ADO

Объектная модель ADO определяет набор (коллекцию) программируемых объектов, которые могут использоваться с Visual Basic, Visual C++, VBScripting, Java на любой платформе, которая поддерживает COM и Автоматизацию OLE. Объектная модель ADO разработана для выполнения большинства особенностей OLE DB.
ADO содержит семь объектов:
* Connection
* Command
* Parameter
* Recordset
* Field
* Property
* Eror
и четыре набора объектов (коллекции):
* Fields
* Properties
* Parameters
* Errors

Коллекция Properties и объект Property доступны через объекты Connection, Recordset и Command. Коллекция Properties и объект Property содержат свойства, которые могут быть доступны только для чтения или для чтения-записи.
Объекты Connection, Recordset и Command являются ключевыми объектами в объектной модели ADO. ADO приложение может использовать объект Connection для установки соединения с сервером БД, объект Command - для выдаче команды к БД, таких как запросы, обновления и т.п. и объект Recordset - для просмотра и манипулирования данными. Командный язык, используемый с объектом Command, зависит от провайдера для БД. В случае реляционных баз данных в качестве командного языка выступает SQL.
Объект Command может не использоваться в случае, если OLE DB провайдер не обеспечивает выполнение интерфейса Icommand. Как как OLE DB провайдер может находиться на вершине реляционных или нереляционных БД, то традиционные SQL-операторы для запроса данных могут быть недоступны для нереляционной БД и поэтому объект Command не потребуется. Если объект Command содержит параметры, то информацию о них можно просмотреть или определить через набор объектов Parameters и объект Parameter. Объект Parameter описывает информацию о параметрах для объекта Command.
В ADO версий 1.0 и 1.5 все объекты могут быть созданы, за исключением объектов Error, Field и Property. К набору объектов Errors и объекту Error можно получить доступ через объект Connection после того как случилась ошибка провайдера. К набору объектов Fields и объекту Field можно получить доступ через объект Recordset после того как в объекте Recordset появятся какие-либо данные. Информация о метаданных объекта Recordset может быть просмотрена через набор объектов Fields и объект Field.

5. Объект Connection

Объект Connection позволяет установливать сеансы связи с источниками данных. Объект Connection обеспечивает механизм для инициализации и установления соединения, выполнения запросов и использования транкзаций.
Основной OLE DB провайдер, используемый для соединения, не ограничивает использование других ODBC провайдеров. Другие провайдеры так же могут использоваться для соединения. Провайдер определяется посредством установки свойства Provider. Если это свойство не определено, то по умолчанию будет использован провайдер MSDASQL.
Метод Open объекта Connection используется для установки соединения. С ODBC провайдером, приложение ADO может использовать механизм ODBC соединения для подключения к серверу БД. ODBC позволяет приложениям устанавливать соединение через источники данных ODBC или явно определять источник данных (DSN-Less connection).
Перед соединением приложение может установить строку соединения, тайм-аут соединения, БД по умолчанию и атрибуты соединения. Объект Connection так же позволяет установить свойство CommandTimeout для всех командных объектов, связвнных с данным соединением. Запросы могу выполняться с использованием метода Execute.
Через объект Connection можно управлять транкзациями. Для этого у него есть методы BeginTrans, CommitTrans и RollbackTrans.
Следующий пример показывает использование ODBC провайдера - по умолчанию OLE DB провайдера в ADO, для подсоединения к SQL Server:

Cn.ConnectionTimeout = 100
" DSN connection
#Cn.Open "pubs", "sa"
" DSN-Less connection for SQL Server
" Cn.Open "Driver={SQL Server};Server=Server1;Uid=sa;Pwd=;Database=pubs"
Cn.Close

В примере сначала устанавливается тайм-аут в 100 сек, затем открываетсясоединение используя ODBC источник данных - pubs, который указывает на SQL Server. Для SQL Server требуется указать идентификатор пользователя (user ID), поэтому sa - это второй параметр метода Open. Пароля нет, поэтому и третьего параметра тоже нет.
Этот пример так же содержит закоментированную строку, которая показывает как присоединиться к SQL Server без источника данных ODBC. Для связи с SQL Server, называемом Server1, используется ODBC драйвер для SQL Server {SQL Server}. Идентификатор пользователя - sa и пароля нет. БД по умолчанию для этого соединения - pubs.
Следующий пример показывает использование свойства Provider для определения альтернативного OLE DB провайдера.
Dim Cn As New ADODB.Connection
Dim rs As ADODB.Recordset
Cn.Provider = "sampprov"
Cn.Open "data source=c:\sdks\oledbsdk\samples\sampclnt\"
Set rs = Cn.Execute("customer.csv")

В этом примере определяется OLE DB провайдер - sampprov. Вместе с ODBC провайдером OLE DB SDK поставляется с текстовым провайдером. Провайдер текста позволяет приложению получать данные из текстового файла. В этом примере соединение устанавливается определением каталога источника данных c:\sdks\oledbsdk\samples\sampleclnt\ и данными в файле customer.csv, получаемыми в результате выполнения метода Execute.
Следующий пример демонстрирует использование методов BeginTrans, CommitTrans и RollbackTrans:
Dim Cn As New ADODB.Connection

" Open connection.
Cn.Open "pubs", "sa"
" Open titles table.
rs.Open "Select * From titles", Cn, adOpenDynamic, adLockPessimistic
Cn.BeginTrans
"
Cn.CommitTrans
" or rollback
" cn.RollbackTrans
Cn.Close

После установки соединения этот пример начинает транкзацию. Измененние данных в этой транкзации может быть подтверждено или произведен откат.
Набор объектов Errors объект Error
Набор объектов Errors и объект Error позволяют получить информацию о случившейся ошибке провайдера. Ошибки могут генерироваться вызовами методов или свойств объектов Connection, Command или Recordset, но всегда получаются из объекта Connection. Набор объектов Errors не существует сам по себе. Он зависит от объекта Connection и ошибок, лежащих ниже ADO (OLE DB провайдеры, ODBC драйверы и источники данных), которые помещаются в набор объектов Errors. Недопустимые значения свойств или ADO интерфейсов не добавляют объекты Error в набор объектов Errors.
В наборе объектов Errors также запоминаются предупреждения (warnings). Предупреждения, в отличие от ошибок, не вызывают остановки выполнения кода. Объект Error позволяет получить описание и источник ошибки. При работе с ODBC провайдером из Error так же доступна информация об SQLSTATE и собственных ошибках БД (database-native error).
Следующий пример показывает получение SQLSTATE, информации о собственной ошибке и описание ошибки при использовании ODBC провайдера.
Dim Cn As New ADODB.Connection
Dim Errs1 As ADODB.Errors
Dim rs As New ADODB.Recordset
Dim i As Integer
Dim StrTmp
On Error GoTo AdoError
Cn.Open "pubs", "sa"
Set rs = Cn.Execute("Select * From TableDoesnotExist")
Done:
" Close all open objects.
Cn.Close
" Destroy anything not destroyed yet.
Set Cn = Nothing
" We"re outta here.
Exit Sub
AdoError:
Dim errLoop As Error
Dim strError As String

" each Error object.
Set Errs1 = Cn.Errors
For Each errLoop In Errs1
Debug.Print errLoop.SQLState


Next
GoTo Done
Соединение с SQL Server устанавливается через источник данных - pubs. Запрос select * from TableDoesnotExist выполняется с помощью метода Execute объекта Connection. Так как в запросе выполняется попытка получить записи из несуществующей таблицы, то возникает ошибка. После возникновения ошибки выполнение программы переходит на метку AdoError. Далее из объекта Connection получается набор объектов Errors и связывается с набором объектов Errs1. Затем в цикле в окно отладчика выводится информация об ошибке.

6. Объект Command

Объект Command позволяет передавать команды к БД. Эти команды могут готовить строки запросов и связянные с запросами параметры, но не ограничиваются только запросами. Командный язык и его особенности зависят от конкретного провайдера БД. Здесь содержится информация и примеры для ODBC провайдера от Microsoft, который поддерживает достаточно широкий диапазон реляционных БД. Более подробно про OLE DB и OLE DB провайдеров можно почитать на сайте https://www.microsoft.com/data в разделе OLE DB.
Объект Command может как открывать новое соединение, так и использовать уже существующее соединение для выполнения запросов, в зависимости от установки свойства ActiveConnection. Если свойство ActiveConnection установлено с ссылкой на объект Connection, то объект Command будет использовать существующее соединение из объекта Connection. Если свойство ActiveConnection определяется строкой соединения, то для объекта Command будет установлено новое соединение. Для однго объекта Connection могут использоваться несколько объектов Command.
Ваполнение запросов может генерировать набор записей, множественные наборы записей или не генерировать никаких записей. Например, выполнение запросов языка описания данных (data definition language - DDL) не генерирует наборов записей. Выполнение оператора SELECT может генерировать набор записей и выполнение пакета SELECT операторов или хранимой процедуры генерирует более чем один набор записей.
Строка запроса определяется свойством CommandText. Строка запроса может быть определена на стандартном языке манипулирования данными (data manipulation language - DML) с использованием SELECT, INSERT, DELETE, или UPDATE операторов или на языке описания данных, например CREATE или DROP. В строке запроса может также буть имя хранимой процедуры или таблицы.
Тип строки запроса определяется свойством CommandType. Значения свойства CommandType могут быть следующими: adCmdText, adCmdTable, adCmdStoreProc и adCmdUnknown. Когда строкой запроса является SQL оператор, свойство CommandType должно определяться как adCmdText. Значения adCmdStoreProc или adCmdTable применяются, если строка запроса определяется как хранимая процудура или имя таблицы.
Если определяется значение adCmdStoredProc, то объект Command выполняет строку запроса с синтаксисом {call procedure=name}. Если определяется значение adCmdTable, то объект Command выполняет строку запроса с синтаксисом select * from tablename. Если определяется значение adCmdUnknown, то объект Command должен выполнить дополнительные действаия по определению типа запроса, что уменьшает производительность системы.
Вы также можете определить, будете или нет готовить строку запроса с помощью свойства Prepared. Установка свойства Prepared позволяет планировать запрос перед первым выполнением. Подготовленный таким образом запрос затем используется при последующих выполнениях для улучшения производительности. Строку запроса следует подготавливать только когда запрос будет выполняться более чем один раз, так как это потребует больше времени, чем на непосредственное выполнение запроса. Таким образом производительность может увеличиться только при выполнение такого запроса во второй, третий и т.д. раз.
Свойство Prepared также может быть полезным при неоднократном выполнении запросов с параметрами. Разные значения параметров будут подставляться при каждом выполнении запроса, вместо полной перестройки строки запроса. Объект Parameter может быть создан с помощью метода CreateParameter. Более подробно об этом написано в разделе "Использование подготовленных операторов".
В следующем примере показано выполнение оператора SELECT, которое возвращает объект Recordset.
Dim Cmd As New ADODB.Command
Dim rs As New ADODB.Recordset


Cmd.CommandText = "select * from titles"
Cmd.CommandTimeout = 15
Cmd.CommandType = adCmdText
Set rs = Cmd.Execute()
rs.Close
Свойство ActiveConnection определяется как строка ODBC соединения - DSN=pubs;UID=sa ODBC. Оператор select * from titles определяется в свойстве CommandText и тек как это SQL оператор, то свойство CommandType устанавливается в adCmdText. Затем устанавливается время ожидания в 15 сек. В результате выполнения запроса возвращается объект Recordset, который связывается с объектом rs.
Следующий пример показывает выполнение хранимой процедуры, которая не возвращает набор записей.
Синтаксис хранимой процедуры следующий:.
drop proc myADOProc
go
create proc myADOProc as
create table #tmpADO (id int not NULL, name char(10) NOT NULL)
insert into #tmpADO values(1, "test")

Хранимая процедура myADOProc создает временную таблицу tmpADO и вставляет строку в эту таблицу. Операторы CREATE и INSERT не генерируют результатов и поэтому при выполнение хранимой процедуры myADOProc не возвращается никаких результатов.
Visual Basic код, вызывающий процедуру myADOProc, имеет следующий вид:
Dim Cmd As New ADODB.Command
" Use a connection string or a Connection object.
Cmd.ActiveConnection = "DSN=pubs;UID=sa"
Cmd.CommandText = "myADOProc"
Cmd.CommandTimeout = 15

Cmd.Execute

Хранимая процедура myADOProc определяется свойством CommandText. Свойство CommandType устанавливается в adCmdStoredProc для определения типа выполняемого объекта и в результате генерируется оператор {call myADOProc}. Так как в результате выполнения хранимой процедуры не возвращается никаких данных, то и нет привязки результатов выполнения процедуры к объекту Recordset.
Набор объектов Parameters и объект Parameter
Набор объектов Parameters обеспечивает объект Command информацией о параметрах и данных. Набор объектов Parameters состоит из объектов Parameter. И набор объектов Parameters и объект Parameter требуются только в том случае, если строка запроса в объекте Command требует параметров. Информация об индивидуальных параметрах (например, размер, тип данных, направление и значение) может быть считана или записана для каждого объекта Parameter. Существует четыре типа параметров направления: входной (input), выходной (output), входной и выходной (input and output) и возвращенное значение (return value). Объект Parameter может служить как входной параметр, как выходной параметр, который содержит данные, и как возвращенное значение хранимой процедуры. Применение метода Refresh коллекции параметров может заставить провайдера обновить информацию о параметрах, однако эта процедура потребует дополнительного времени.
При работе с значительными по размеру типами данных для записи части данным можно использовать метод AppendChunk. Дополнительная информация об этом содержится в разделе "Использование больших типов данных"
Следующий пример демонстрирует создание параметров для хранимой процедуры. Хранимая процедура имеет следующий синтаксис:
drop proc myADOParaProc
go
create proc myADOParaProc
@type char(12)
as
select * from titles where type = @type

Процедура myADOParaProc берет один @type входной параметр и возвращает данные, которые соответствуют определенному типу. Типом данных для параметра @type является тип character, который имеет длину 12.
Код Visual Basic, вызывающий эту хранимую процедуру, имеет следующий вид:
Dim cmd As New ADODB.Command
Dim rs As New ADODB.Recordset
Dim prm As ADODB.Parameter
" Define a Command object for a stored procedure.
cmd.ActiveConnection = "DSN=pubs;uid=sa"
cmd.CommandText = "myADOParaProc"
cmd.CommandType = adCmdStoredProc
cmd.CommandTimeout = 15
" Set up new parameter for the stored procedure.
Set prm = Cmd.CreateParameter("Type", adChar, adParamInput, 12, "Business")
Cmd.Parameters.Append prm
" Create a record set by executing the command.
Set rs = Cmd.Execute
While (Not rs.EOF)
Debug.Print rs(0)
rs.MoveNext
Wend

Свойства ActiveConnection, CommandText, CommandType, и CommandTimeout определяются так же, как и в предыдущем примере. Процедура myADOParaPro ожидает входного параметра, который имеет тип character и размер 12. Метод CreateParameter используется для создания объекта Parameter с соответствующими характеристиками: тип данных - adChar для символьных данных, тип параметра - adParamInput для входного параметра и длина - 12. Объект Parameter так же определяется именем Type и так как это входной параметр, то определяеся значение Business. После того, как параметр определен, с помощью метода Append он добавляется к набору объектов Parameters. Затем выполняется хранимая процедура и результат возвращается в объект Recordset.

7. Объект Recordset

Объект Recordset обеспечивает методы для манипулирования наборами данных. Объект Recordset позволяет добавлять, удалять, обновлять записи и перемещаться по записям внаборе данных. С помощью набора объектов Fields и объекта Field можно получить доступ к любой конкретной записи. Обновление объекта Recordset может быть сделано немедленно или в пакетном режиме. При создании объекта Recordset автоматически открывается курсор.
Объект Recordset позволяет определить тип курсора иего расположение для выбора результирующего набора данных. Используя свойство CursorType можно определить тип курсора: forward-only, static, keyset-driven, или dynamic. Тип курсора определяет поведение объекта Recordset при прокрутке записей вперед/назад или при обновлении записей. Тип курсора также влияет на видимость измененных записей.
По умолчанию тип курсора устанавливается в forward-only только для чтения. Если необходимо только читать данные в однонаправленном режиме (forward), то изменять тип курсора не следует. С другой стороны, можно определить тип курсора в зависимости от решаемой задачи.
С помощью свойства CursorLocation можно определить будет ли курсор серверным или клиентским. Расположение курсора играет большую роль при использовании несвязанных наборов записей. Подробнее об этом написано в разделе "Использование серверных курсоров". Объект Recordset может быть создан при выполнении метода Execute объекта Connection или Command.
Следующий пример показывает использование объекта для открытия соединения и получения результирующего набора данных:
Dim rs As New ADODB.Recordset

While (Not rs.EOF)
Debug.Print rs(0)
rs.MoveNext
Wend
rs.Close

В этом примере открывается соединение, создается набор записей, затем в цикле печатается содержимое первого поля каждой строки набора записей.
Набор объектов Fields и объект Field
Набор объектов Fields и объект Field позволяют получить доступ к данным любой колонки в текущей записи. К набору объектов Fields можно получить доступ через объект Recordset. К объекту Field можно получить доступ через набор объектов Fields используя индекс. Объект Field можно использовать для составления новой записи или для изменения уже существующих данных и затем использовать методы AddNew, Update или UpdateBatch объекта Recordset для вставки новых или изменения существующих данных.
В отличи от RDO, в ADO нет метода Edit. Обновление объекта Field приводит к изменению данных и поэтому не требуется явных методов для редактирования данных.
Следующий пример показывает использование объекта Field для возвращения имени, типа и значения каждого поля данных в текущей записи:
Dim rs As New ADODB.Recordset
Dim fld As ADODB.Field
rs.Open "select * from titles", "DSN=pubs;UID=sa"
Set Flds = rs.Fields
Dim TotalCount As Integer
TotalCount = Flds.Count
i = 0
For Each fld In Flds
Debug.Print fld.Name
Debug.Print fld.Type
Debug.Print fld.Value
Next
rs.Close

После создания объекта Recordset в результате выполнения запроса select * from titles, возвращается набор объектов Fields. Затем в цикле для каждого объекта Field из набора объектов Fields выводятся на печать свойства Name, Type и Value.

Набор объектов Properties и объект Property
Набор объектов Properties и объект Property обеспечивают информацию о параметрах объектов Connection, Command, Recordset и Field. К набору объектов Properties можно получить доступ через объекты Connection, Command, Recordset и Field. К объекту Property можно получить доступ через набор объектов Properties используя индекс.
Набор объектов Properties состоит из объектов Property. Кроме значения и типа свойства объект Property позволяет получить доступ и к атрибутам свойства. Атрибуты описывают такие вещи, как будет или нет поддерживаться определенное свойство объекта или оно может быть доступно только для чтения или записи. Например, свойство ConnectionTimeout обеспечивает данные о том, сколько секунд будет длиться ожидание при установке соединения перед тем как будет сгенерирована ошибка завершения времени.
Следующий пример демонстрирует возвращение свойств ConnectionTimeout, CommandTimeout и Updatability:
Dim Cn As New ADODB.Connection
Dim Cmd As New ADODB.Command
Dim rs As New ADODB.Recordset
Cn.Open "pubs", "sa"
" Find out ConnectionTimeout property.
Debug.Print Cn.Properties("Connect Timeout")

Cmd.CommandText = "titles"
Cmd.CommandType = adCmdTable
Set rs = Cmd.Execute()
" find out CommandTimeout property.
Debug.Print Cmd.Properties("Command Time out")
Debug.Print rs.Properties("Updatability")

В этом примере набор объектов Properties возвращается из объектов Connection, Command и Recordset. Свойство ConnectionTimeout объекта Connection выводится на печать. Затем аналогичные шаги выполняются для объектов Command и Recordset.

8. Продвинутые возможности ADO

8.1. Использование языка определения данных (Data Definition Language - DDL)
Язык определения данных - это такие SQL операторы, которые поддерживают определения или объявления объектов БД, например CREATE TABLE, DROP TABLE или ALTER TABLE. Выполнение запросов DDL не генерирует никаких данных и, поэтому, нет необходимости использовать объект Recordset. Для выполнения запросов DDL идеальным является объект Command. Чтобы отличить запросы DDL от имен хранимых процедур или таблиц, свойство CommandType объекта Command должно быть установлено в adCmdText.
SQL Server обеспечивает ряд опций выполнения запросов, которые могут быть установлены оператором SET. Эти SET опции не генерируют никаких результирующих наборов данных и, поэтому они могут рассматриваться в обном ряду с запросами DDL.
Следующий пример показывает использование объекта Command для отключения опции SET NOCOUNT оператора SET SQL Server Transact-SQL - SET:
Dim Cn As New ADODB.Connection
Dim Cmd As New ADODB.Command
" If the ADOTestTable does not exist
On Error GoTo AdoError
Cn.Open "pubs", "sa"
Set Cmd.ActiveConnection = Cn
Cmd.CommandText = "drop table ADOTestTable"
Cmd.CommandType = adCmdText
Cmd.Execute
Done:
Cmd.CommandText = "set nocount on"
Cmd.Execute
Cmd.CommandText = "create table ADOTestTable (id int, name char(100))"
Cmd.Execute
Cmd.CommandText = "insert into ADOTestTable values(1, "Jane Doe")"
Cmd.Execute
Cn.Close
Exit Sub
AdoError:
Dim errLoop As Error
Dim strError As String
" Enumerate Errors collection and display properties of
" each Error object.
Set Errs1 = Cn.Errors
For Each errLoop In Errs1
Debug.Print errLoop.SQLState
Debug.Print errLoop.NativeError
Debug.Print errLoop.Description
Next
GoTo Done
End Sub
Этот пример сбрасывает таблицу, открывает таблицу и вставляет данные в таблицу с использованием метода Execute объекта Command. Для этого типа запросов не создаются объекты Recordset. Таблица ADOTestTable может не существовать в БД и поэтому выполнение drop table ADOTestTable может генерировать ошибку, которая покажет, что таблица не существует. Для обработки такой ситуации предусмотрена обработка ошибок. Затем выполняется установка опции set - set nocount on.
8.2. Использование подготовленных (Prepared) операторов
Запросы могут быть подготовлены перед их выполнением или могут быть непосредственно выполнены. Свойство Prepared объекта Command позволяет определить будет запрос подготавливаться или нет.
Если свойство Prepared установлено в TRUE, то строка запроса будет подвергнута разбору и оптимизации при первом выполнении. При любм последующем выполнении этого запроса будет использоваться "откомпилированная" версия запроса. Это потребует некоторого времени при первом выполнении запроса, но зато потом, при последующих выполнениях, должен наблюдаться заметный рост производительности. Если предполагается, что запрос будет выполняться один раз, то он должен быть выполнен без предварительной подготовки.
Свойство Prepared может также использоваться при выполнении запросов со многими параметрами. Приложение может выполнять запросы с параметрами более чкм один раз с подстановкой разных наборов параметров при каждом выполнении запроса вместо полной перестройки строки запроса всякий раз при изменении параметров. Однако, если запрос с параметрами выполняется один раз, то нет необходимости в его предварительной подготовке.
SQL Server не поддерживает непосредственно модель Prepare/Execute для ODBC. Когда оператор подготавливается, ODBC драйвер SQL server создает временную хранимую процедуру для этого оператора. Эта временная хранимая процедура существует в tempdb и не сбрасывается до тех пор, пока не закроются объекты Recordset или Connection.
Эта опция может быть отключена в диалоге SQL Server ODBC Data Source Setup если для подсоединения к SQL Server используется источник данных ODBC. Если эта опция отключена, то каждый раз при выполнении запроса SQL оператор сначала запоминается и затем посылается к серверу на выполнение.
Следующий пример показывает использование подготовленного оператора для обновления запроса и динамической сборке запроса с различными наборами параметров во время выполнения:
Dim Cn As New ADODB.Connection
Dim Cmd As New ADODB.Command
Dim prm1 As New ADODB.Parameter
Dim prm2 As New ADODB.Parameter
Cn.Open "DSN=pubs", "sa"
Set Cmd.ActiveConnection = Cn
Cmd.CommandText = "update titles set type=? where title_id=?"
Cmd.CommandType = adCmdText
Cmd.Prepared = True
Set prm1 = Cmd.CreateParameter("Type", adChar, adParamInput, 12, "New Bus")
Cmd.Parameters.Append prm1
Set prm2 = Cmd.CreateParameter("Title_id", adChar, adParamInput, 6, "BU7832")
Cmd.Parameters.Append prm2
Cmd.Execute
Cmd("Type") = "New Cook"
Cmd("title_id") = "TC7777"
Cmd.Execute
Cmd("Type") = "Cook"
Cmd("title_id") = "TC7778"
Cmd.Execute
Cn.Close
Этот пример обновляетданные в таблице titles с использованием разных значений параметров. Строка запроса подготавливается так, чтобы при его выполнении могли применяться разные наборы параметров. Для операции обновления требуются два параметра: type и title_id. Они создаются с помощью метода CreateParameters и добавляются к набору объектов Parameters с помощью метода Append.
Первый набор параметров имеет значения New Bus и BU7832. Другие значения параметров могут использоваться перед выполнением метода Execute без перестройки строки запроса, так как свойство Prepared установлено в TRUE.
8.3. Выполнение хранимых процедур
Выполнение хранимых процедур очень похоже на выполнение подготовленных запросов, за исключением того, что хранимая процедура существует как объект в БД даже когда выполнение запроса закончено. Хранимая процедура может также использоваться для сокрытия из приложения сложных SQL операторов.
Когда в объекте Command выполняется хранимая процедура, свойство CommandType должно быть определено как adCmdStoredProc. При таком определениии свойства CommandType генерируется соответствующий оператор SQL для основного провайдера. Для ODBC провайдера генерируются ODBC escape-последовательности для вызовов процедур {[?=]call procedure-name[([,]...)]} и ODBC драйвер SQL Server оптимизирует запрос, чтобы использовать все преемущества этих последовательностей.
Следующий пример показывает выполнение хранимой процедуры sp_who:
Dim Cmd As New ADODB.Command
Dim rs As New ADODB.Recordset
Cmd.ActiveConnection = "DSN=pubs;uid=sa"
Cmd.CommandText = "sp_who"
Cmd.CommandType = adCmdStoredProc
Set rs = Cmd.Execute()
Debug.Print rs(0)
rs.Close
Для оптимальной производительности приложение никогда не должно подготавливать хранимые процедуры SQL Server. Иначе возникнут дополнительные накладные расходы при создании временных хранимых процедур.

Коды возврата и выходные параметры хранимых процедур
Хранимые процедуры могут содержать входные и выходные параметры и возвращать значения. Например, следующая хранимая процедура myProc содержит выходной параметр @ioparm и возвращает значение 99.
CREATE PROCEDURE myProc @ioparm int OUTPUT AS
SELECT name FROM sysusers WHERE uid SELECT @ioparm = 88
RETURN 99
Входной параметр для хранимой процедуры можно определить через объект Parameter. Выходной параметр и возвращенное значение могут также быть определены через объект Parameter, но действительные значения обоих этих параметров не будут возвращены до тех пор, пока объект Recordset не будет полностью выбран (в смысле выборки данных) или пока объект Recordset не будет закрыт.
ADO следующий:
Dim Cmd As New ADODB.Command
Dim rs As New ADODB.Recordset
Dim param As Parameter
Cmd.ActiveConnection = "DSN=pubs;UID=sa"
Cmd.CommandText = "myproc"
Cmd.CommandType = adCmdStoredProc
" Set up parameters.
Set param = Cmd.CreateParameter("Return", adInteger, adParamReturnValue, 0)
Cmd.Parameters.Append param
Set param = Cmd.CreateParameter("Output", adInteger, adParamOutput, 0)
Cmd.Parameters.Append param
Set rs = Cmd.Execute
If Not rs.EOF And Not rs.BOF Then
Debug.Print rs(0)
rs.Close
End If
Debug.Print Cmd(0) " The return code
Debug.Print Cmd(1) " The Output parameter
Для хранимой процедуры myProc требуются два параметра: выходной параметр для сохранения возвращенного значения и выходной параметр @ioparam. В этом примере сначала создаются два параметра: Return и Output. Параметр Return создается как тип adParamReturnValue и имеет тип данных - adInteger, что соответствует integer. Параметр Output - adParamReturnValue для выходного параметра и тоже имеет тип данных integer. Так как оба параметра имеют тип integer, то нет необходимости определять длину данных.
После добавления параметров и выполнения запроса создается набор записей. Затем объект Recordset закрывается, чтобы получить код возврата и выходной параметр.
8.4. Использование пакетного обновления (Batch Updating)
Метод Update объекта Recordset позволяет обновить текущую запись. Метод UpdateBatch позволяет применить все ожидающие новые, обновленные и удаленные записи к объекту Recordset. Используя LockType adLockBatchOptimistic, метод UpdateBatch позволяет выполнить все изменения, находящиеся в состоянии ожидания, на клиентском компьютере и одновременно послать все эти изменения серверу БД. Изменения, находящиеся в состоянии ожидания, могут быть отменены с помощью метода CancelBatch.
Только если все изменения в БД при выполнении метода UpdateBatch будут неудачными, будет возвращена ошибка. Если же только некоторые изменения будут неудачными, то будет возвращено предупреждение.
В SQL Server метод UpdateBatch является допустимым только, когда свойство LockType установлено в adLockBatchOptimistic и тип курсора - либо keyset-driven, либо static. Курсор типа keyset-driven может открываться с таблицами, имеющими уникальные индексы.
Следующий пример демонстрирует использование метода UpdateBatch:
Dim rs As New ADODB.Recordset
rs.CursorType = adOpenKeyset
rs.LockType = adLockBatchOptimistic
rs.Open "select * from titles", "DSN=pubs;uid=sa"
" Change the type for a specified title.
While (Not rs.EOF)
If Trim(rs("Type")) = "trad_cook" Then
rs("Type") = "Cook"
End If
rs.MoveNext
Wend
rs.UpdateBatch
rs.Close
В этом примере сохдается набор записей используя курсор типа keyset-driven со свойством LockType установленным в adLockBatchOptimistic. После создания объекта Recordset, тип trad-cook изменяется на Cook, новый тип для всех записей в таблице title. После выполнения всех изменений, сделанные изменения данных подтверждаются применением метода UpdateBatch.
8.5. Генерация нескольких наборов записей
Вместо выполнения одного запроса несколько раз, SQL Server позволяет выполнять пакеты запросов. В результате выполнении пакета запросов может генерироваться более чем один набор записей. Кроме пакетных запросов, множественные наборы записей могут также генерироваться SQL операторами, включающими предложения COMPUTE BY и COMPUTE, или хранимыми процедурами, которые содержат более одного оператора SELECT.
Когда генерируются множественные наборы записей, важно иметь возможность последовательно выбирать наборы записей до тех пор, пока наборы записей являются доступными. Метод NextRecordset объекта Recordset позволяет выбрать любые последующие наборы записей. Если нет больше доступных наборов записей, то возвращенный объект Recordset будет установлен в Nothing.
Следующий пример показывает использование метода NextRecordset для выборки нескольких наборов записей.
Синтаксис хранимой процедуры следующий:
drop proc myNextproc
go
create proc myNextproc as
select * from titles
select * from publishers
Эта хранимая процедура генерирует два набора записей: один - в результате выполнения запроса select * from titles, другой - в результате выполнения запроса select * from publishers.
ADO код имеет следующий вид:
Dim cmd As New ADODB.Command
Dim rs As ADODB.Recordset
Cmd.ActiveConnection = "DSN=pubs;UID=sa"
Cmd.CommandText = "myNextProc"
Cmd.CommandType = adCmdStoredProc
Set rs = Cmd.Execute()
While Not rs Is Nothing
If (Not rs.EOF) Then
Debug.Print rs(0)
End If
Set rs = rs.NextRecordset()
Wend
После выполнения хранимой процедуры myNextProc создается объект Recordset. Так как в результате выполнения процедуры myNextProc создается два набора записей, каждые объект Recordset может быть получен при помощи метода NextRecordset.
8.6. Использование серверных курсоров
SQL Server обеспечивает целый ряд типов серверных курсоров для использования в приложениях. По умолчанию ADO приложение не использует серверных курсоров когда вместе с SQL Server используется ODBC провайдер. По умолчанию ADO приложение использует несерверный курсор типа forward и только для чтения.
Серверные курсоры полезны при обновлении, вставке или удалении записей. Серверные курсоры также позволяют иметь множественные активные операторы (active statements) при одном соединении. SQL Server намеренно не позволяет множественных активных операторов на соединение, пока используются серверные курсоры. Активный оператор предназначен для того, чтобы в обработке оператора существовало несколько незавершенных результатов (pending results). Если серверные курсоры не используются и приложение пытается иметь более одного активного оператора, то приложение получит сообщение об ошибке "Connection Busy with Another Active Statement".
Чтобы в приложении использовать серверный курсор необходимо сделать следующее:
* Установить тип курсора отличный от устанавливаемого по умолчанию при помощи метода RecordsetOpen. По умолчанию тип курсора устанавливается в adOpenForwardOnly и изменение типа на adOpenKeyset, adOpenDynamic или adOpenStatic приведет к использованию серверного курсора.
* Установить LockType отличный от устанавливаемого по умолчанию при помощи метода RecordsetOpen. По умолчанию LockType установлен в adLockReadOnly и любое изменение его на adLockPessimistic, adLockOptimistic или adLockBatchOptimistic приведет к использованию серверного курсора.
* Установить значение свойства CacheSize на любое другое, отличное от устанавливаемого по умолчанию (по умолчанию устанавливается 1).
Серверные курсоры:
* Создаются только для операторов, начинающахся с SELECT, EXEC procedure_name или {call procedures_name}. Даже если приложение явно потребует создания серверного курсора, серверный курсор не будет создан для операторов таких как INSERT.
* Не могут использоваться с операторами, которые генерируют белее одного набора записей. Это ограниечение применимо ко всем операторам, описанным в разделе "Генерация множественных наборов записей". Если серверный курсор используется с любым оператором, генерирующим множественные наборы записей, то приложение может получить следующие ошибки:
* "Cannot open a cursor on a stored procedure that has anything other than a single select statement in it."
* "sp_cursoropen. The statement parameter can only be a single select or a single stored procedure."
Следующий пример демонстрирует открытие динамического (dynamic) серверного курсора:
Dim rs As New ADODB.Recordset
rs.Open "select * from titles", "DSN=pubs;UID=sa", adOpenDynamic, adLockOptimistic
rs.Close
8.7. Использование объемных типов данных
Объемными типами данных в SQL server являются типы text и image. Тексты и рисунки иногда могут поместиться в памяти, но они также могут оказаться такими большими, что не смогут быть возвращены за одну операцию или целиком разместиться в памяти. Если объемные данные помещаются в памяти, то для получения всех данных за одну операцию может использоваться свойство Value объекта Field. Если данные слишком велики, чтобы поместиться в памяти, то операции с такими данными должны проводиться по частям. Существует два способа манипулирования данными большого размера. Первый - через объект Field и второй - через объект Parameter. Оба объекта Field и Parameter поддерживают метод AppendChunk, а объект Field, кроме того, обеспечивает метод GetChunk для операций с данными большого размера.
Объект Field позволяет записать или прочитать данные через объект Recordset. Метод AppendChunk объекта Field позволяет добавить данные к концу текущих данных когда запрос уже выполнен. Метод GetChunk позволяет читать данные по частям.
Объект Parameter обрабатывает данные большого размера подобным образом. Только у объекта Parameter нет метода GetChunk и нет объекта Recordset при работе с данными большого размера в режиме выполнения. С объектом Parameter данные большого размера свянаны в режиме выполнения и выполняются с объектом Command.
Существует несколько ограничений при работе с данными большого размера при использовании ODBC провайдера. Если не используется серверный курсор, то все колонки с объемными данными должны располагаться справа от всех остальных колонок (с обычными данными). Если есть несколько колонок с данными большого размера, то доступ к ним должен производиться в порядке слева-направо.
Следующий пример демонстрирует использование методов AppendChunk и GetChunk для чтения и записи данных большого размера:
Структура таблицы-приемника следующая:
drop table myBLOB
go
create table myBLOB(id int unique, info text)
go
insert into myBLOB values(1, "test")
go
Таблица myBLOB - это таблица-приемник, в которую будут вставлены данные большого размера:
ADO код имеет следующий вид:
Dim Cn As New ADODB.Connection
Dim rsRead As New ADODB.Recordset
Dim rsWrite As New ADODB.Recordset
Dim strChunk As String
Dim Offset As Long
Dim Totalsize As Long
Dim ChunkSize As Long
Cn.Open "pubs", "sa"
rsRead.CursorType = adOpenStatic
rsRead.Open "select pr_info from pub_info", Cn
rsWrite.CursorType = adOpenKeyset
rsWrite.LockType = adLockBatchOptimistic
rsWrite.Open "select * from myBLOB", Cn
ChunkSize = 1000
Totalsize = rsRead("pr_info").ActualSize
Do While Offset strChunk = rsRead("pr_info").GetChunk(ChunkSize)
Offset = Offset + ChunkSize
rsWrite("info").AppendChunk strChunk
Loop
rsWrite.UpdateBatch
rsWrite.Close
rsRead.Close
End Sub
В этом примере данные pr_info считываются из таблицы pub_info и вставляются в таблицу myBLOB. После создания наборов записей rsRead и rsWrite, размер данных запоминаетсяв переменной Totalsize. Затем в цикле WHILE данные вставляются частями по 1000 байт. Выход из цикла предусмотрен, когда размер вставленных данных превысит размер первоначальный размер данных. После вставки для подтверждения сделанных изменений применяется метод UpdateBatch.

9. Сервисы удаленных данных (Remote Data Services)

Remote Data Services (ранее называемые Advanced Data Connector) разработан, чтобы устранить статичность Web страниц. Традиционная Web технология доступа к БД позволяет получить данные с сервера БД в виде HTML страницы и после этого данные становятся статичными и ими нельзя манипулировать без установления нового соединения с сервером БД.
Remote Data Services устраняет это ограничение, предоставляя дистанционный набор записей на клиентском компьютере, которым можно манипулировать. Remote Data Services позволяет тспользовать набор записей ADO, полученный с удаленного сервера. Такой набор записей может размещаться на клиентском компьютере без продолжения активного соединения с сервером БД.
Remote Data Services интегрирован в модель объектов ADO. Advanced Data Connector, распространаемый с OLE DB SDK version 1.1, обеспечивает механизмы для несвязанных наборов записей (disconnected record sets) и кэширования на стороне клиента, что дает возможность создавать динамические Web приложения с доступом к БД.
Другим подходом, предлагаемым с Remote Data Services, является использование прикладных объектов или объектов промежуточного уровня (business objects). Remote Data Services также позволяет отделить business-правила от данных. Вместо того, чтобы выкладывать всю программную или business логику на Web страницу клиента, можно разместить всю программную или business логику в приккладном объекте.
Прикладные объекты (Business Object) и ADO
Прикладные объекты - это программные модули OLE DLL, которые можно создавать с помощью Visual Basic, Visual C++ или Microsoft Visual J++. Основная цель прикладных объектов - разделить логику приложения и бизнес-правила и защитить бизнес-правила и данные от редистрибьюции. Например, бизнес-правилом может быть правило для расчета цен на авиабилеты. Авиакомпания не хочет, чтобы заказчики знали о том, как расчитываются цены на авиабилеты, а также может захотеть изменять правила расчета цены в любое время. Такие бизнес-правила могут быть реализованы на удаленном компьютере и при необходимости они могут быть изменены без влияния на компьютеры клиентов.
Прикладные объекты отделяют бизнес-правила от клиентского компьютера. Прикладные объекты могут находиться на удаленном сервере в вызываться клиентским компьютером по мере необходимости. Бизнес-правила и данные могут быть надежно защищены только когда на клиентском компьютере находится приложение с необходимой программной логикой (пользовательским интерфейсом), а все бизнес-правила вместе с кодом соединения с БД находятся на удаленном компьютере.
Для создания прикладных объектов можно использовать по умолчанию объект AdvancedDataFactory. Этот объект обеспечивает только чтение и запись данных. Объектная модель ADO является другой альтернативой для выполнения прикладных объектов. Объекты ADO обеспечивают не только всестороннее манипулирование данными, но и механизм удаленного набора записей.
Разъединение набора записей в прикладном объекте
Перед удалением (в смысле не delete, а remote) набора записей, он должен быть в состоянии функционировать как объект Recordset без реального соединения с сервером БД. После размещения набора записей на клиентском компьютере, соединение с данными не может и не размещается с набором записей и, поэтому, набор записей должен быть отсоединен от сервера БД. Набор записей без реального соединения с сервером БД называется разъединенным набором записей (disconnected record set).
Разъединенный набор записей можно создать с использованием свойства CursorLocation объекта Recordset. Свойство CursorLocation позволяет определить будет ли курсор клиентским или серверным и, что более важно, плзволяет проводить изменения данных в пакетном режиме (batch mode). Если свойство CursorLocation определено как adUseClient, то любые обновления в разъединенном наборе записей будут сделаны в пакетном режиме и для кэширования набора записей будет использоваться клиентский курсор.
Следующий пример демонстрирует создание разъединенного набора записей:
Public Function GetData() As Object
Dim cn As New ADODB.Connection
Dim rs As New ADODB.Recordset
cn.Open "dsn=testing;uid=sa;pwd=;"
rs.CursorLocation = adUseClient
rs.Open "select * from authors for browse", cn, adOpenUnspecified, adLockUnspecified, adCmdUnspecified
Set GetData = rs
End Function

Помещение удаленных изменений обратно в прикладной объект
После того, как клиентский компьютер получает разъединенный набор записей, он может использовать элементы управления ADC или наборы записей ADO для манипулирования данными. После внесения изменений в набор записей, клиентский компьютер может выбрать, какой способ будет использоваться для возвращения данных обратно: набор записей целиком или только измененные данные. Для этого служит свойство MarshalOptions набора записей ADO.
Следующий код может быть размещен на Web странице для возвращения набора записей от прикладного объекта и помещению набора записей обратно в прикладной объект:
Set rs = BusObj.GetData
Adc1.recordset=rs

Set rso = adc.recordset
rso.MarshalOptions=1
BusObj.SetData rso

Reconnecting and Applying Changes
После того, как обновленный набор данных передан от клиентского компьютера, прикладной объект может переустановить связь с набором данных и использовать метод UpdateBatch для обновления сделанных изменений в данных на сервере БД. Прикладной объект может переустановить связь с набором данных при помощи метода Open, как показано в следующем примере:
Public Function SetData(rso As Object)
Dim rs As New ADODB.Recordset
rs.Open rso, "dsn=pubs;uid=sa"
rs.UpdateBatch
End Function


ГЛАВА 4

Модель ADO.NET: провайдеры данных

Порой кажется, что не успели еще разработчики приложений баз данных привыкнуть к новой технологии, как компания Microsoft предложила совершенно новую модель доступа к базам данных. В этой главе основное внимание уделяется модели ADO.NET, которая является наиболее новым воплощением модели ADO. Сначала обосновывается необходимость введения новой модели доступа к базам данных (с точки зрения авторов), а затем предлагается описание самой модели и ее архитектуры.

В этой главе преследуется цель изложить основные принципы функционирования технологии ADO.NET. Здесь обсуждаются базовые операции и описываются базовые объекты провайдера данных ADO.NET, в частности Connection, Command, Parameter и DataReader. Далее (в главах 5, "ADO.NET: объект DataSet", 6, "ADO.NET: объект DataAdapter", и 7, "ADO.NET: дополнительные компоненты") рассматриваются более сложные объекты, которые тесно связаны с основным объектом ADO.NET - DataSet.

Обзор технологии ADO.NET

Разработчики приложений для работы с базами данных на основе Visual Basic уже привыкли к тому, что Microsoft каждые несколько лет предлагает новую усовершенствованную модель доступа к данным. Кроме новой трехбуквенной аббревиатуры, технология ADO.NET также предлагает новую модель API-интерфейсов и объектов. В течение последних нескольких лет разработчики уже успели познакомиться с предшественниками ADO.NET - технологиями ODBC, DAO, RDO и ADO. При знакомстве с каждой новой технологией им требовалось тщательно изучить ее назначение и принципы работы. При этом они часто задавали себе один и тот же вопрос: имеет ли смысл переходить на новую технологию? В большинстве случаев ответ был положительным, если только новшества не оказывали никакого влияния на текущие и будущие требования проекта. Действительно, чаще всего переход на новые технологии был вполне обоснован, за исключением технологии RDO (Remote Data Objects) для проектов с процессором баз данных Jet (потому что технология ОАО по-прежнему является более удачной технологией работы с Jet).

Мотивация и философия

Итак, зачем же все-таки нужна новая объектная модель доступа к данным? Наиболее простой ответ на этот вопрос можно сформулировать по аналогии с рекламным лозунгом компании Toyota конца 1970-х годов: "Спрашивали? Так получите". Технология ADO.NET предоставляет множество функциональных возможностей, о которых разработчики мечтали с момента появления технологии ADO. Некоторые компоненты, например отсоединенные от источника данных наборы записей и поддержка XML, были добавлены в модель ADO уже после ее выпуска. Но, поскольку они являлись добавлениями к основной модели, которые отсутствовали в исходной архитектуре ADO, способ их применения был крайне неудобным.

Классическая модель ADO основана на модели COM и одном объекте recordset, применение которого может быть весьма разнообразным. В зависимости от параметров конфигурации, например типа курсора, его расположения и типа блокировки, объект recordset будет действовать по-разному и выполнять различные операции.

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

Это дает разработчику возможность специализированного применения объектов без дополнительного обременительного "багажа". В то же время совершенно разные объекты могут взаимодействовать друг с другом для выполнения более сложных функций.

Поддержка распределенных приложений и отсоединенной модели программирования

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

Расширенная поддержка XML

Хотя классическая модель ADO способна сохранять и считывать данные в формате XML, фактически используемый для этого формат имеет несколько необычную форму и не так прост в применении. Кроме того, поддержка XML в модели ADO была добавлена в ходе ее эволюции, а потому обладает некоторыми ограничениями, в то время как поддержка XML в технологии ADO.NET является ее ключевым элементом. Философия ADO.NET формулируется очень кратко и просто: "Данные - это данные". Независимо от источника поступления, они могут считываться и обрабатываться как реляционные или иерархические данные, исходя из поставленной задачи и используемых инструментов.

XML используется как формат передачи данных между уровнями и компьютерами. Это не только исключает проблему прохождения COM-объектов через брандмауэры, но и позволяет совместно использовать данные сразу несколькими приложениями, которые работают на платформах, отличных от Windows (так как практически любая платформа способна обрабатывать текстовые данные XML).

Интеграция с.NET Framework

ADO.NET – это не просто следующая версия ADO, она специально предназначена для использования на платформе.NET Framework. Код всех объектов ADO.NET полностью управляемый, т.е. работает под управлением платформы.NET Framework. Технология ADO.NET является составной частью платформы.NET Framework, что позволяет избежать проблем с контролем версий объектов, которые хорошо известны разработчикам, имеющим опыт работы с ADO.

Внешний вид объектов ADO.NET

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

ADO.NET И ADO 2.X

При работе с моделью ADO.NET нужно учитывать перечисленные ниже отличия от классической модели ADO.

В классической модели ADO предусмотрен присоединенный к источнику данных способ доступа и использование физической модели данных.

В модели ADO.NET проводиться четкое разделение между присоединенным способом доступа к данным и отсоединенной моделью программирования.

В модели ADO.NET нет свойств CursorType, CursorLocation или LockType, потому что в ADO.NET предусмотрены только статические курсоры, клиентские курсоры и оптимистическая блокировка.

Вместо использования простого многоцелевого объекта recordset в ADO.NET разные функции распределены среди объектов меньшего размера - DataReader, DataSet и DataTable.

В ADO.NET разрешается полноценное манипулирование данными в формате XML, а не только использование его в качестве формата ввода-вывода данных.

В ADO.NET предусмотрена поддержка строго типизированных наборов данных DataSet, а не использование для всех полей типа Variant. Это позволяет эффективнее обнаруживать ошибки времени выполнения и повышает производительность работы приложений.

Место ADO.NET в архитектуре.NET Framework

На рис. 4.1 показано место классов ADO.NET в архитектуре.NET Framework. В основе этой платформы лежит общеязыковая исполняющая среда (Common Language Runtime - CLR), которая образует единую среду выполнения для всех.NET-совместимых приложений, независимо от используемого языка программирования. Среда CLR включает общую систему типов, управление памятью и жизненным циклом объектов.

На следующем логическом уровне над средой CLR располагаются базовые системные классы. Именно эти классы отвечают за выполнение базовых функций, которые могут использоваться в.NET-приложениях. На рис. 4.1 показаны только некоторые классы библиотеки классов.NET Framework, которая, по сути, является новым набором API-интерфейсов Windows. В прошлом доступ к функциям операционной системы Windows осуществлялся только через API-интерфейсы, которые состояли из большого набора разрозненных и плохо продуманных функций. На платформе.NET Framework такой доступ организован на основе свойств и методов, которые предлагаются базовыми системными классами. Это объектно-ориентированный, последовательный и комфортабельный способ создания приложений Windows, независимо от типа клиентского приложения: традиционного настольного приложения, броузера или Web-службы.

РИС. 4.1. Классы ADO.NET в структуре платформы.NET Framework


Этот уровень включает несколько пространств имен (групп классов и других определений), предназначенных для организации доступа к данным: System.Data, System.OleDb и System.Data.SqlClient. В оставшейся части данной главы, а также в главах 5, 6 и 7 эти классы и пространства имен рассматриваются более подробно.

Прикладные интерфейсы

На этом наиболее высоком уровне происходит дифференциация, или разделение, выполняемых функций, которые разработчики могут использовать в разных типах приложений. Он содержит классы и элементы управления для создания (классических) приложений Windows на основе форм (Windows Forms), другие классы и элементы управления для создания Web-ориентированных приложений (Web Forms), а также классы для создания приложений на основе Web-служб. Однако все они используют для прикладной логики базовую библиотеку классов - системные базовые классы.

Теперь после первого знакомства с расположением классов ADO.NET в общей структуре платформы.NET Framework рассмотрим подробнее основные объекты ADO.NET.

Провайдеры данных ADO.NET

Несмотря на подчеркнутое значение отсоединенной модели программирования, для извлечения, обновления, вставки и удаления данных все же придется подключиться к физической базе данных. Программное обеспечение ADO.NET для подсоединения и взаимодействия с физической базой данных называется провайдером данных ADO.NET. Провайдер данных (data provider) - это управляемый код.NET, который эквивалентен провайдеру OLEDB или драйверу ODBC. Провайдер данных состоит из нескольких объектов, которые реализуют необходимую функциональность в соответствии с определениями своих классов и интерфейсов.

В настоящее время существует три разных провайдера данных ADO.NET, каждый из которых определен в своем собственном пространстве имен. Для всех объектов в этих пространствах имен используются следующие префиксы: OleDb, Sql и Odbc. Однако при упоминании этих объектов в рамках своего пространства имен имя объекта можно указывать без употребления префикса данного пространства имен.

Провайдер данных SqICIient

Оптимизирован для работы с SQL Server версии 7.0 (или выше) и позволяет добиться более высокой производительности по следующим причинам:

Взаимодействует с базой данных непосредственно через собственный протокол табличной передачи данных (Tabular Data Stream - TDS), а не через OLEDB с отображением интерфейса OLEDB на протокол TDS;

Исключает накладные расходы, связанные с использованием COM-служб взаимодействия;

Отсутствуют ненужные функции, которые не поддерживаются в SQL Server (объекты этого провайдера данных находятся в пространстве имен System.Data.SqlClient).

Провайдер данных Oledb

Основан на существующем COM-поставщике OLEDB и COM-службах взаимодействия платформы.NET Framework, предназначенных для доступа к базе данных. Этот провайдер данных используется для работы с SQL Server более ранних версий, чем 7.0. Он позволяет осуществлять доступ к любой базе данных, для которой имеется поставщик OLEDB. Объекты этого провайдера данных находятся в пространстве имен System.Data.Oledb.

Провайдер данных Odbc

Используется для доступа к базам данных, которые не имеют собственного провайдера данных.NET или COM-поставщика OLEDB. Иногда драйвер ODBC демонстрирует более высокую производительность, чем драйвер OLEDB, поэтому для сравнения их фактической производительности при работе с конкретной базой данных рекомендуется провести ряд тестов. Объекты этого провайдера данных находятся в пространстве имен System. Data.Odbc.

НА ЗАМЕТКУ

Создание провайдера данных для ODBC несколько задержалось и отстало от создания платформы.NET Framework и Visual Studio .NET. Поэтому он не был включен в исходный выпуск Visual Studio .NET и его можно скопировать с Web-узла компании Microsoft. Кроме того, в скором будущем следует учитывать возможное появление дополнительных.NET-совместимых провайдеров данных.

В настоящее время на Web-узле компании Microsoft также можно скопировать провайдер данных Oracle.NEТ. Эти провайдеры данных ОDBС и Oracle будут включены в следующую версию 1.1 платформы.NET Framework и Visual Studio .NET 2003. В результате этого пространство имен провайдера данных ODBC Microsoft.Data.Odbc станет называться System.Data.Odbc.

В примерах этой главы используется провайдер данных ODBC версии 1.0, поэтому при использовании провайдера данных ODBC версии 1.1 нужно изменить имя его пространства имен.

Основные объекты

Каждый провайдер данных имеет четыре основных объекта, которые указаны в табл. 4.1.

Таблица 4.1. Основные объекты провайдера данных

Каждый объект основан на базовом родовом классе и реализует родовой интерфейс, но имеет собственную реализацию. Например, объекты SqlDataAdapter, OleDBDataAdapter и OdbcDataAdapter являются производными от класса DbDataAdapter и реализуют те же интерфейсы. Однако каждый из них реализует их своим собственным способом для соответствующего источника данных.

Пространство имен System. Data. OleDb содержит объекты:

OleDbConnection;

OleDbDataReader;

OleDbDataAdapter.

Пространство имен System.Data.SqlClient содержит объекты:

SqlConnection;

SqlDataReader;

SqlDataAdapter.

Пространство имен Microsoft.Data.Odbc содержит объекты:

OdbcConnection;

OdbcDataReader;

OdbcDataAdapter.

Аналогично, все будущие провайдеры данных будут находиться в отдельных пространствах имен и содержать такие же объекты, но с другими реализациями и префиксами.

Объект Connection

Этот объект модели ADO.NET очень похож на объект Connection в классической модели ADO. Его предназначение очевидно: он служит для установления соединения с заданным источником данных и с указанным в строке подключения учетным именем и паролем. Соединение можно настроить, редактируя нужным образом значения параметров строки подключения. Объект Command (или DataAdapter) может затем использовать это подключение для выполнения нужных операций с источником данных.

НА ЗАМЕТКУ

В отличие от объекта Connection в модели ADO в объекте Connection в модели ADO.NET нет методов Execute и ОpenSchema. Для выполнения команд SQL следует использовать объекты Command или DataAdapter. Функции метода OpenSchema реализуются с помощью методов GetOleSchemaTable объекта OleDbConnection.

Хотя объекты OleDbConnection, SqlConnection и OdbcConnection реализуют одинаковые интерфейсы, они все же имеют разные реализации. Например, они имеют разный формат строки подключения. В объекте OleDbConnection используется стандартный формат строки подключения OLEDB с незначительными исключениями. В объекте OdbcConnection также используется стандартный формат строки подключения ODBC, но с незначительными отклонениями. Наконец, в объекте SqlConnection используется совершенно другой формат строки подключения, который имеет отношение только к SQL Server версии 7.0 или выше.

Более того, некоторые объекты обладают дополнительными свойствами. Например, объект OleDbConnection имеет свойство Provider для указания используемого провайдера данных OLEDB, а объект OdbcConnection имеет свойство Driver для указания используемого драйвера ODBC. Объект SqlConnection вообще не имеет этих свойств, так как используется с предопределенным источником данных, т.е. с SQL Server. Однако он имеет свойства PacketSize и WorkstationID, которые используются только для работы с SQL Server и не нужны для других типов подключения.

Итак, приступим к созданию кода. Познакомимся последовательно со всеми типами источников данных, начав с самого простого примера, который по мере продвижения к концу главы будет постепенно усложняться.

1. Запустите интегрированную среду разработки приложений Visual Studio .NET.

2. Создайте новый проект Visual Basic Windows Application. Для этого в диалоговом окне New Project (Новый проект) выберите тип проекта Visual Basic Project в области Project Types (Типы проектов), а затем шаблон Windows Application (Приложение Windows) в области Templates (Шаблоны).

3. Назовите проект DataProviderObjects.

6. В окне Properties укажите значение Data Provider Objects для свойства Text формы Form1.

7. В верхнем левом углу формы создайте кнопку, перетаскивая ее из панели элементов управления.

8. В окне Properties укажите значение cmdConnection для свойства (Name) и значение Connection для свойства Text этой кнопки.

9. В верхнем правом углу формы создайте текстовое поле, перетаскивая его из панели элементов управления.

10. В окне Properties укажите значение txtResults для свойства (Name), значение True для свойства Multiline и значение Both для свойства ScrollBars этого текстового поля.

11. Увеличьте размер текстового поля, чтобы оно занимало до 80% всей площади формы.

По окончании этих операций форма будет выглядеть так, как на рис. 4.2.


РИС. 4.2. Форма Form1 проекта DataProviderObjects


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

Обратите внимание, что эти пространства имен содержат классы и определения объектов ADO.NET для каждого провайдера данных.

НА ЗАМЕТКУ

Среда Visual Studio .NET может не обнаружить пространство имен Data.Odbc, потому что оно является расширением базовой версии продукта. В таком случае выполните ряд действий.

1. Скопируйте инсталлятор провайдера данных ODBC с Web-узла компании Microsoft и выполните все инструкции по инсталляции.

2. В окне SolutionExplorer щелкните правой кнопкой мыши на папке References проекта DataProviderObjects.

3. Выберите в контекстном меню команду Add Reference.

4. Во вкладке.NET диалогового окна Add Reference прокрутите список компонентов и найдите файл Microsoft.Data.Odbc.dll.

5. Щелкните дважды на файле Microsoft.Data.Odbc.dll для включения его в список избранных компонентов Selected Components в нижней части диалогового окна Add Reference.

6. Щёлкните на кнопке ОК для закрытия диалогового окна Add Reference.

Если по какой-либо причине не распознано какое-то другое импортированное пространство имен, потребуется привести ссылку на файл System.Data.dll. Для этого выполните действия, перечисленные в пп. 2-6, где вместо файла Microsoft.Data.Odbc.dll при выполнении п. 4 нужно использовать файл System.Data.dll.

Теперь для кнопки btnConnection нужно создать код, приведенный в листинге 4.1, для создания подключения к базе данных pubs сервера SQL Server. Этот код создает подключение и отображает состояние подключения до и после попытки подключения к базе данных.

Листинг 4.1. Код открытия подключения и отображения его состояния
Private Sub btnConnection Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnConnection.Click
" Открытие подключения
txtResults.Text & "Opening DB connection…" _
& ControlChars.CrLf & ControlChars.CrLf
" Отображение состояния подключения
If (cnn.State = System.Data.ConnectionState.Open) Then
txtResults.Text = txtResults.Text & "Connection is Open"
txtResults.Text = txtResults.Text & "Connection is Closed"
txtResults.Text = txtResults.Text & ControlChars.CrLf
СОВЕТ

Новым полезным компонентом Visual Basic .NET является возможность получения текстового представления для значений перечисления (enum) вместо создания специальной подпрограммы на основе операторов select-case для всех возможных значений перечисления. Все типы перечисления, которые являются объектами, наследуют метод ToString, возвращающий строку с текстовым представлением текущего значения. В листинге 4.1 используется приведенный ниже фрагмент кода для отображения состояния подключения на основе операторов if-else.

" Отображение состояния подключения – вариант 1
If (cnn.State = System.Data.ConnectionState.Open) Then
txtResults.Text = txtResults.Text& "Connection is Open"
txtResults.Text = txtResults.Text& "Connection is Closed

Его можно заменить другим вариантом кода, содержащим только одну строку.

" Отображение состояния подключения вариант 2
txtResults.Text & "Connection is " & cnn.State.ToString & ControlChars.CrLf

После запуска полученного приложения DataProviderObjects и щелчка на кнопке Connection в текстовом поле появятся строки о закрытии подключения, о состоянии подключения и повторном открытии подключения, как показано на рис. 4.3.

НА ЗАМЕТКУ

При создании рабочего кода нужно выбрать и реализовать стратегию обработки исключительных ситуаций для большинства подпрограмм и операций. Обычно основана на блоках try-catch обработки исключительных ситуаций. Этот код не включен в приведенные здесь примеры, потому что наша цель - сфокусировать внимание на концепциях программирования баз данных, а не на описании общих принципов программирования для Visual Basic .NET.

Объект Command

Аналогично объекту Connection, объект Command модели ADO.NET очень похож на своего предшественника из прежней модели ADO 2.X. Объект Command позволяет выполнять команды по отношению к источнику данных и получать возвращенные данные или результаты выполнения команд.


РИС. 4.3. Состояние приложения DataProviderObjects до и после открытия подключения с помощью кода из листинга 4.1


Этот объект имеет следующие свойства: CommandText и СommandType для определения текста и типа фактической команды; Connection для указания подключения, используемого для выполнения команды; СommandTimeout для указания времени ожидания, по истечении которого команда отменяется и выдается сообщение об ошибке; Parameters для коллекции параметров команды; Transaction для указания транзакции, в которой используется данная команда.

Все три версии объекта Command (в пространствах имен OleDb, Sql, Odbc) имеют идентичные свойства и методы, за исключением того, что объект SqlCommand имеет дополнительный метод, которого нет у двух других вариантов этого объекта, а именно ExecuteXmlReader. Он использует преимущества SQL Server для автоматического возвращения данных в формате XML (если в запрос SQL добавлено предложение FOR XML).

НА ЗАМЕТКУ

Еще одно отличие между версиями объекта Command для разных провайдеров данных заключается в использовании значений свойства CommandType. Все они поддерживают значения Text и StoredProcedure, а объекты OleDbCommand и SqlCommand поддерживают еще одно, третье возможное значение - TableDirect. Это позволяет эффективно загружать все содержимое таблицы за счет установки значения TableDirect для свойства CommandType и имени таблицы для свойства CommandText.

Продолжим работу с формой, показанной на рис. 4.3.

1. Добавим еще одну кнопку сразу под кнопкой btnConnection, перетаскивая пиктограмму кнопки из панели элементов управления.

2. В окне свойств Properties установите значение btnCommand для свойства Name и значение Command для свойства Text.

3. Добавьте для кнопки код, показанный в листинге 4.2.

Листинг 4.2. Код открытия подключения к базе данных и подготовки объекта Command
" Создание экземпляра объекта Connection
" Создание экземпляра объекта Command
Dim cmd As SqlCommand = New SqlCommand() txtResults.Clear()
"Select au_lname, state from authors"
" Вывод текста команды
txtResults.Text = txtResults.Text & ControlChars.Tab & cmd.CommandText() & ControlChars.CrLf

После запуска на выполнение приложения DataProviderObjects щелкните на кнопке Command, и в текстовом поле будет показана команда SQL, которая находится в свойстве CommandText объекта SqlCommand, а именно: SELECT au_lname, state FROM authors.

НА ЗАМЕТКУ

Многие классы платформы.NET Framework, а также созданные другими разработчиками классы перегружают конструкторы объектов. Иначе говоря, существует несколько способов создания нового экземпляра класса, где каждый конструктор принимает свойственный только ему набор аргументов. Таким образом можно выбрать версию, оптимальную для выполнения текущей задачи. Конструктор в листинге 4.2 для объекта SqlConnection отличается от го по умолчанию конструктора в листинге 4.1, в котором не используется никаких аргументов. Позже строке подключения присваивается объект SqlConnection с помощью свойства ConnectionString.

" Создание экземпляра объекта Connection
Dim cnn As SqlConnection = New SqlConnection()
" Создание строки подключения
cnn.ConnectionString = "server=localhost;uid=sa;database=pubs"

В листинге 4.2 используется конструктор объекта SqlConnection, который принимает строку подключения в качестве параметра. Это позволяет создать объект и сразу же указать для него строку подключения в одной строке кода.

" Создание экземпляра объекта Connection
Dim cnn SqlConnection = New SqlConnection("server=localhost;uid-sa;database=pubs")

Применение объекта Command с параметрами и хранимыми процедурами

При создании запросов или команд для источника данных часто требуется передавать значения параметров действия (обновление, вставка или удаление данных) или хранимой процедуры. Для решения этой проблемы в объекте Command предусмотрено свойство Parameters, которое является объектом-коллекцией ParameterCollection и содержит коллекцию объектов-параметров Parameter. Это аналогично способу работы, применимому в модели ADO 2.X.

Объекты Parameter и ParameterCollection тесно связаны с соответствующим провайдером данных, поэтому они должны быть реализованы как составная часть провайдера данных ADO.NET. Способы программирования объекта SqlParameterCollection и использование объектов OdbcParameterCollection и OledbParameterCollection имеют существенные отличия. Объекты OdbcParameterCollection и OledbParameterCollection основаны на позиционных параметрах, а объект SqlParameterCollection – на именованных параметрах. Эти различия в значительной степени влияют на способ определения запросов и параметров.

Начнем с создания простого запроса с параметрами для извлечения из базы данных pubs имен и фамилий всех авторов из заданного штата.

С одной стороны, при использовании провайдеров данных OLEDB или ODBC запрос будет иметь следующий вид:

SELECT state, au_fname, au_lname from authors WHERE state = ?

Здесь вопросительный знак заменяет один параметр, а для нескольких параметров можно использовать несколько вопросительных знаков. Порядок расположения параметров в коллекции ParameterCollection должен точно соответствовать порядку, в котором располагаются их заменители в запросе или хранимой процедуре.

С другой стороны, при использовании провайдера данных SqlClient запрос будет выглядеть следующим образом:

SELECT state, au_fname, au_lname from authors WHERE state = @MyParam

Здесь заменителем параметра является его имя, а дополнительные параметры также обозначаются их именами, поэтому порядок расположения параметров в коллекции ParameterCollection не имеет значения.

Объект Parameter можно создать явно, используя конструктор Parameter (т.е. с использованием оператора New) или передавая нужные аргументы методу Add объекта-коллекции ParameterCollection (свойство Parameters объекта Command). Помните, что оба метода (конструктор Parameter и метод Add) имеют перегруженные версии.

Ниже приведен один из способов включения параметра команды за счет явного указания объекта-параметра.

Dim rayParameter As New OdbcParameter("@MyParam", OdbcType.Char, 2)

А способ включения параметра команды с помощью метода Add выглядит иначе.

Второй метод короче и обычно предпочтительнее, если только нет особой причины для повторного использования объекта Parameter.

Для метода Add объекта Parameter обычно требуется указать имя, тип и длину параметра. Затем нужно указать направление передачи данных: Input, Output, InputOutput или ReturnValue. По умолчанию используется направление Input. Наконец, для присвоения значения параметру нужно использовать свойство Value объекта Parameter. Кроме того, для параметра можно указать другие свойства, например масштаб (свойство Scale), точность (свойство Precision) и допустимость использования неопределенных значений (свойство IsNullable).

При использовании провайдера данных SqlClient можно применять практически идентичный код. Единственным отличием являются префиксы Odbc вместо префиксов Sql, а также тип перечисления SqlDbType вместо OdbcType.

Dim myParameter As New SqlParameter("@MyParam", SqlDbType.Char, 2)
myParameter.Direction = ParameterDirection.Input
cmd.Parameters.Add(myParameter)

Аналогично выглядит способ включения параметра команды с помощью метода Add.

cmd.Parameters("@MyParam").Direction = ParameterDirection.Input
cmd.Parameters("@MyParam").Value = "CA"
СОВЕТ

Для передачи параметру неопределенного значения можно использовать свойство Value объекта DBNull.

cmd.Parameters("@MyParam").Value = DBNull.Value

Измените код кнопки cmdButton, как показано в листинге 4.3. После запуска программы и щелчка на кнопке cmdButton в текстовом поле появится текст запроса, а также имя и значение параметра.

Листинг 4.3. Код подготовки и отображения команды и ее параметров
Private Sub btnCommand_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnCommand.Click
" Создание экземпляра объекта Connection
Dim cnn As SqlConnection = New SqlConnection(_
"server=localhost;uid=sa;database=pubs")
" Создание экземпляра объекта Command и объектов Parameter
Dim prm As SqlParameter = New SqlParameter()
" Открытие подключения cnn.Open()
" Указание подключения и текста команды
cmd.CommandType = CommandType.Text
"Select au_lname, state from authors where state = @MyParam"
cmd.Parameters.Add(New SqlParameter("@MyParam", SqlDbType.Char, 2))
cmd.Parameters("@MyParam").Value = "CA"
" Вывод текста команды
txtResults.Text = "Command String:" & ControlChars.CrLf
cmd.CommandText() & ControlChars.CrLf
" Вывод параметров команды
txtResults.Text = txtResults.Text & "Command parameters:" & _
For Each prm In cmd. Parameters
txtResults.Text = txtResults.Text & ControlChars.Tab & _
prm.ParameterName & " = " & prm.Value & ControlChars.CrLf

Аналогично вызываются хранимые процедуры, за исключением того, что вместо свойства CommandType.Text используется свойство CommandType.StoredProcedure, а имя хранимой процедуры присваивается свойству CommandText. Таким образом, код вызова хранимой процедуры GetAuthorsFromState с двухсимвольным параметром для извлечения информации обо всех авторах заданного штата будет выглядеть, как показано ниже.

cmd.CommandText = "GetAuthorsFromState"
cmd.Parameters.Add("@MyParam", SqlDbType.Char, 2)
cmd.Parameters("@MyParam").Direction = ParameterDirection.Input
cmd.Parameters("@MyParam").Value = "CA"
СОВЕТ

Для вызова хранимой процедуры с помощью OdbcCommand нужно использовать стандартные последовательности символов ODBC (с участием фигурных скобок), а не только имя процедуры для свойства CommandText. В качестве заменителей параметров в ODBC используются вопросительные знаки. Вот как выглядит приведенный выше для провайдера данных ODBC:

cmd.CommandType = CommandType.StoredProcedure
cmd.CommandText = "{GetAuthorsFromState ?}"
cmd.Parameters.Add("@MyParam", OdbcType.Char, 2)
cmd.Parameters("@MyParam").Direction = ParameterDirection.Input
cmd.Parameters("@MyParam").Value = "CA"

Хранимая процедура также возвращает значение, которое указывается с помощью специальной последовательности символов (вопросительного знака и знака равенства) у открывающей скобки, как показано ниже.

cmd.CommandText = "{? = GetAuthorsFromState ?}"

Чтобы хранимая процедура возвращала значение, нужно указать направление Output, а затем считать значение свойства параметра после вызова хранимой процедуры. В данном примере также определяется возвращаемое значение хранимой процедуры. Поскольку здесь указан тип Int сервера SQL Server, то для этого параметра не нужно указывать длину, так как по определению она составляет 4 байт.

cmd.Parameters.Add(New SqlParameter("result", SqlDbType.Int)
cmd. Parameters ("result").Direction = ParameterDirection.ReturnValue
cmd.Parameters.Add(New SqlParameter("@MyParam", SqlDbType.Int)
cmd.Parameters("@MyParam").Direction = ParameterDirection.Output
" Вызов хранимой процедуры
Msg (cmd.Parameters("@MyParam").Value)
НА ЗАМЕТКУ

При определении параметра, который будет использоваться для возвращаемого значения ReturnValue хранимой процедуры его нужно указать первым в списке параметров коллекции Parameters. Это обязательное условие для провайдеров данных OLEDB и ODBC, потому что, как отмечалось выше, параметры этих провайдеров определяются по порядку их расположения, а возвращаемое значение может располагаться только с начала. Однако при определении возвращаемого значения для провайдера данных SQL его можно pасполагать в произвольном месте коллекции параметров, потому что параметры провайдера данных SQL определяются по именам.

Выполнение команд

До сих пор мы только указывали свойства и параметры объекта Command, но не выполняли эти команды. Существует три стандартных способа выполнения команд для объекта Command и один способ для объекта SqlCommand.

Метод ExecuteNonQuery. Выполняет команду SQL и не возвращает записей.

Метод ExecuteScalar. Выполняет команду SQL и возвращает первое поле первой записи.

Метод ExecuteReader. Выполняет команду SQL и возвращает набор записей с помощью объекта DataReader.

Метод ExecuteXmlReader (только для объекта-команды SqlCommand). Выполняет команду SQL и возвращает набор записей в формате XML с помощью объекта XmlReader.

Метод ExecuteNonQuery

Этот метод позволяет выполнять команды без возвращения значений (наборов записей или скалярных значений), кроме значения, сообщающего об успешном или неудачном исходе выполнения команды. Это наиболее эффективный способ выполнения команд по отношению к источнику данных. Таким образом можно выполнять команды SQL или хранимые процедуры, которые являются DDL-командами определения данных (например, для создания или изменения структуры базы данных: таблиц, представлений или хранимых процедур) либо DML-командами управления данными (например, их обновления, вставки или удаления).

НА ЗАМЕТКУ

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

При удачном выполнении DDL-команд определения данных для изменения структуры баз данных возвращается значение -1, а при удачном выполнении DML управления данными для их обновления, вставки или удаления возвращается количество строк, задействованных в команде. При неудачном выполнении команд обоих типов возвращается значение 0.

Продолжая работу с проектом DataProviderObjects, попробуем использовать объекты пространства имен OleDb и базу данных pubs. Наша задача – создать новую таблицу tblStateZipCodes для этой базы данных с помощью DDL-команды. Новая таблица tblStateZipCodes предназначена для организации связи между почтовыми индексами и штатами. Определения ее полей совпадают с определениями полей в других таблицах базы данных pubs, но отличаются от определений полей в других таблицах базы данных Novelty. Эта таблица имеет два поля: ZipCode для почтового индекса и State для названия соответствующего штата. Ниже приведена команда SQL для создания этой таблицы.

CREATE TABLE tblStateZipCodes (

Теперь нужно изменить исходную форму Form1, выполнив ряд действий.

1. Откройте форму Form1 в интегрированной среде разработки Visual Studio .NET.

2. В верхнем левом углу формы создайте еще одну кнопку, перетаскивая ее пиктограмму из панели элементов управления.

3. В окне свойств Properties укажите значение btnNonQuery для свойства (Name) и значение ExecuteNonQuery для свойства Text.

Затем создайте код подпрограммы btnNonQuery_Click, который приведен в листинге 4.4.

Листинг 4.4. Код создания таблицы базы данных с помощью объектов пространства имен OleDb
Private Sub btnNonQuery_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnNonQuery.Click
Dim sql As String Dim result As Integer
Dim cmd As OleDbCommand = New OleDbCommand()
" Указание подключения и текста команды
cmd.CommandType = CommandType.Text
" Указание команды SQL для создания новой таблицы
sql = "CREATE TABLE tblStateZipCodes (" & _
"ZipCode char (5) NOT NULL, " & _
MsgBox(sql) cmd.CommandText = sql
" Открытие подключения перед вызовом метода ExecuteNonQuery.
" Для обработки исключительных ситуаций нужно поместить
" код в блоке Try-Catch, потому что неудачное выполнение
" команды ТАКЖЕ генерирует ошибку времени выполнения.
" Отображение сообщения об ошибке.
" Вывод результатов выполнения команды.
MessageBox.Show("Command completed successfully")
" MessageBox.Show("Команда выполнена успешно")
MessageBox.Show("Command execution failed")
" MessageBox.Show("Команда не выполнена")

После запуска полученного приложения и щелчка на кнопке ExecuteNonQuery сначала появится диалоговое окно с сообщением об успешном выполнении команды. Правильность выполнения команды можно проверить, просматривая список таблиц базы данных pubs в диалоговом окне Server Explorer интегрированной среды разработки Visual Studio .NET (которое описывается в главе 1, "Основы построения баз данных") или в программе SQL Server Enterprise Manager (которая рассматривается в главе 3, "Знакомство с SQL Server 2000").

При повторном щелчке на кнопке ExecuteNonQuery появятся два диалоговых окна с сообщениями: одно с сообщением о возникшей исключительной ситуации (оно создается блоком обработки исключительных ситуаций try-catch), а другое - о неудачном выполнении команды.

Аналогичным способом можно создать представление или хранимую процедуру. Для создания представления EmployeeJobs_view, которое возвращает отсортированный по должностям перечень сотрудников с указанием их имен, фамилий и должностей, замените команду SQL в листинге 4.4 приведенной ниже командой.

sql = "CREATE VIEW EmployeeJobs_view AS" & _
"SELECT TOP 100 PERCENT jobs. job_desc," & _
"employee.fname, employee.lname" &_
"employee ON jobs. job_id = employee. job_id &_
НА ЗАМЕТКУ

Для включения предложения ORDER BY в определение представления с сортировкой результатов нужно включить в команду SELECT предложение ТОР.

Для создания хранимой процедуры, которая принимает один параметр и возвращает значение, нужно изменить эту команду SQL, как показано в листинге 4.5.

Листинг 4.5. Код, содержащий команду SQL для создания хранимой процедуры AuthorsInState1
sql = "CREATE PROCEDURE AuthorsInState1 @State char(2)" & _
" AS declare @result int" & _
" select @result = count (*) from authors " & _
НА ЗАМЕТКУ

Хотя метод не возвращает записи, входные и выходные параметры, а также возвращаемые значения можно передавать или возвращать с помощью коллекции Parameters. Это более эффективный подход, чем выполнение команды, которая возвращает набор записей или скалярное значение.

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

Продолжая работу с проектом DataProviderObjects, предположим, что издательство, которое создало базу данных pubs, решило повысить авторам выраженный в процентах гонорар. Включение новой кнопки и нового текстового поля в форму позволяет главному бухгалтеру издательства повысить гонорары авторов с помощью параметра команды UPDATE. Для этого нужно выполнить перечисленные ниже действия.

1. Создайте новую кнопку под кнопкой cmdExecuteNonQuery.

2. В окне свойств Properties для этой кнопки укажите значение cmdUpdate для свойства (Name) и значение Update для свойства Text.

3. Создайте новое текстовое поле под новой кнопкой Update.

4. В окне свойств Properties для этого текстового поля укажите значение txtParam1 для свойства (Name) и значение 0 для свойства Text. Установка такого значения гарантирует, что при запуске программы и случайном нажатии кнопки Update не будет причинен ущерб данным.

5. Создайте код подпрограммы btnUpdate_Click, приведенный в листинге 4.6.

Листинг 4.6. Код обновления таблицы базы данных с помощью команды UPDATE с параметром
Private Sub btnUpdate_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnUpdate.Click
" Создание экземпляра объекта Connection.
Dim cnn As SqlConnection = New SqlConnection(_
"server=localhost;uid=sa;database=pubs")
" Создание экземпляра объекта Command.
Dim cmd As SqlCommand = New SqlCommand()
cmd.CommandType = CommandType.Text
cmd.CommandText = "UPDATE roysched SET royalty = royalty + @param1"
" Создание параметра и указание его значения.
cmd.Parameters.Add(New SqlParameter("@param1", SqlDbType.Int))
cmd.Parameters("@param1").Direction = ParameterDirection.Input
cmd.Parameters("@param1").Value = Val(txtParam1.Text)
" Открытие подключения перед вызовом метода ExecuteReader().
result = cmd.ExecuteNonQuery()
MessageBox.Show(result & " records updated", "DataProviderObjects")

Теперь таблицу с гонорарами авторов в базе данных pubs можно обновить, запустив приложение DataProviderObjects, задав новое значение гонорара в текстовом поле под кнопкой Update и щелкнув на этой кнопке. После этого на экране появится диалоговое окно с указанием количества охваченных записей. Этот результат можно проверить с помощью программы SQL Server Enterprise Manager, просматривая данные о гонорарах в таблице roysched до и после обновления.

Точно такое же обновление можно выполнить с помощью хранимой процедуры, что позволяет добиться более высокой производительности и централизованно хранить код. Потенциальным недостатком использования хранимых процедур является необходимость назначения администратора базы данных или специалиста с опытом создания хранимых процедур. В крупных организациях порой уходят целые дни на то, чтобы администратор базы данных изменил хранимые процедуры, которые можно самостоятельно изменить за несколько минут. Хранимые процедуры создаются с помощью программ SQL Server Enterprise Manager или SQL Query Analyzer, которые описаны в главе 3, "Знакомство с SQL Server 2000". Это можно также сделать с помощью проекта DataProviderObjects, изменив команду SQL, как это делалось ранее.

Итак, в данном примере хранимая процедура имеет следующий вид:

CREATE PROCEDURE UpdateRoyalties
UPDATE roysched SET royalty = royalty + @param1

В листинге 4.6 для организации вызова хранимой процедуры потребуется заменить свойства CommandText и CommandType объекта Command.

cmd.CommandType = CommandType.StoredProcedure
cmd.CommandText = "UpdateRoyalties"

Запуск этой программы должен привести к тем же результатам, что и прежде, однако теперь обновление происходит благодаря хранимой процедуре, а не команде SQL из кода приложения.

Метод ExecuteScalar

Иногда нужно выполнить команду, которая возвращает скалярное значение, т.е. только одно значение. Типичными примерами являются команды SQL для вычисления суммы всех значений SUM и общего количества значений COUNT. Другими примерами являются справочные таблицы для подстановки одного значения или команды, возвращающие логическое значение. Метод ExecuteScalar выполняет заданную команду и возвращает значение первой записи из первого поля возвращенного набора записей, а все другие поля и записи игнорируются.

Включим приведенную ниже хранимую процедуру в базу данных pubs.

CREATE PROCEDURE AuthorsInState2
SELECT count(*) FROM authors WHERE state = @param1

Хранимая процедура AuthorsInState2 принимает параметр, который имеет двухсимвольный код штата, и возвращает из таблицы authors количество авторов из этого штата. Эта процедура с функциональной точки зрения эквивалентна процедуре AuthorsInState1 из листинга 4.5, но возвращает вычисленное значение для набора записей, а не только индикатор успешности выполнения команды.

НА ЗАМЕТКУ

Использование метода ExecuteScalar вместо метода ЕxecuteNonQuerу и передача скалярного значения с помощью параметра ReturnValue связаны с дополнительными накладными расходами. Зачем же его используют? Он проще в употреблении, потому что не нужно заботиться об указании параметров в определениях и вызовах команд.

Для вызова данной хранимой процедуры с помощью провайдера данных ODBC выполните следующее.

1. Создайте дополнительную кнопку под текстовым полем txtParam1.

2. В окне свойств Properties укажите значение cmdScalar для свойства (Name) и значение ExecuteScalar для свойства Text.

3. Создайте код подпрограммы btnExecuteScalar_Click, приведенный в листинге 4.7.

Листинг 4.7. Код извлечения скалярного значения из хранимой процедуры с помощью провайдера данных ODBC
Private Sub btnExecuteScalar_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnExecuteScalar.Click
" Создание экземпляра объекта Connection.
Dim cnn As OdbcConnection = New OdbcConnection(_
"DRIVER={SQL Server};server=localhost;uid=sa;database=pubs")
" Создание экземпляра объекта Command.
Dim cmd As OdbcCommand = New OdbcCommand()
" Указание подключения и текста команды.
cmd.CommandType = CommandType.StoredProcedure
cmd.CommandText = "{call AuthorsInState2(?) }"
" Создание параметра и указание его значения
cmd.Parameters.Add("@param1", OdbcType.Char, 2)
cmd.Parameters("@param1").Value = txtParam1.Text
MessageBox.Show("Count is " & result, "DataProviderObjects")

Запустите приложение и введите в текстовом поле над кнопкой ExecuteScalar двухсимвольный код штата. После щелчка на кнопке ExecuteScalar появится диалоговое окно с сообщением о количестве авторов в данном штате. Полученный результат можно проверить с помощью программы SQL Server Enterprise Manager, просматривая данные в таблице authors в базе данных pubs.

НА ЗАМЕТКУ

Учтите, что по умолчанию база данных pubs содержит двух авторов из штата Юта (код UТ) и 15 авторов из штата Калифорния (код СА).

Метод ExecuteReader

Этот метод применяется для возвращения набора записей. В большинстве приложений для работы с базами данных именно он используется чаще всего. Работа этого метода основана на объекте DataReader, с помощью которого записи обрабатываются последовательно одна за другой. Более подробно метод ExecuteReader и объект DataReader рассматриваются в следующем разделе.

Объект DataReader

Данный объект предназначен для чтения в прямом направлении небуферизуемого потока записей, полученных от метода ExecuteReader объекта Command. Объект DataReader в основном эквивалентен объекту Recordset модели ADO 2.X, который также предназначен для чтения в прямом направлении. Объект DataReader предлагает наиболее быстрый способ доступа к источнику данных, но в нем не предусмотрены возможности прокрутки и обновления данных. Поскольку данные не буферизуются и не сохраняются в кэше, этот метод прекрасно подходит для извлечения большого объема данных. Для перехода к следующей записи объекта DataReader нужно вызвать его метод Read.

Кроме коллекции Fields, доступ к полям каждой записи можно осуществить с помощью строго типизированных методов. Доступ к полям с помощью коллекции Fields аналогичен способу доступа к полям в модели ADO 2.X.

НА ЗАМЕТКУ

Объект DataReader не имеет явного конструктора, т.е. его нельзя создать с помощью оператора New(). Для инициализации нового объекта нужно вызвать метод ExecuteReader объекта Command.

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

В самом первом примере используется имя поля Myfield, что значительно упрощает его чтение, использование и совместимость с унаследованными программами. Во втором примере используется номер поля, что значительно повышает производительность, поскольку исключается преобразование имени поля в его номер.

Для демонстрации способов использования этих методов выполните следующее.

1. Создайте под кнопкой ExecuteScalar дополнительную кнопку.

2. В окне свойств Properties укажите значение cmdExecuteReader для свойства (Name) и значение ExecuteReader для свойства Text.

3. Создайте код подпрограммы btnExecuteReader_Click, приведенный в листинге 4.8.

НА ЗАМЕТКУ

Кроме способов использования объекта DataReader, в этом примере демонстрируются и другие функциональные возможности. Например, здесь помимо членов Text и StoredProcedure свойства-перечисления CommandType для указания типа команды используется член TableDirect. Он содержит имя таблицы, все поля которой возвращаются данной командой. Учтите, что этот член перечисления поддерживается только для провайдера данных ODBC.

Представления обычно обрабатываются точно также, как и таблицы. Следовательно, при использовании члена TableDirect свойства-перечисления CommandType можно указывать не только имя таблицы, но и имя представления.

Листинг 4.8. Код создания объекта DataReader и извлечения значений полей с помощью представления и члена TableDirect
Private Sub btnExecuteReader_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnExecuteReader.Click
" Создание экземпляра объекта Connection.
Dim cnn As OleDbConnection = New OleDbConnection(_
"provider=SQLOLEDB;server=localhost;uid=sa;database=pubs")
" Создание экземпляра объекта Command.
Dim As OleDbCommand = New OleDbCommand()
" Указание подключения и текста команды
cmd.CommandType = CommandType.TableDirect
cmd.CommandText = "EmployeeJobs_View"
" Открытие подключения перед вызовом метода
Dim reader As OleDbDataReader
txtResults.Text = txtResults.Text & reader("fname") & _

(Здесь предполагается, что представление EmployeeJobs_view уже создано с помощью подпрограммы btnNonQuery_Click из листинга 4.4, как описано выше. – Прим. ред.)

НА ЗАМЕТКУ

Перед попыткой доступа к данным объекта DataReader не забывайте вызывать метод Read(). В отличие от объекта Recordset в модели ADO 2.X, в которой после загрузки данных текущее расположение автоматически находится на первой записи, в модели ADO.NET в объекте DataReader нужно явно указать текущее расположение возле первой записи с помощью исходного вызова метода

Для организации доступа к данным можно также применить цикл While с более эффективными строго типизированными методами доступа к полям.

txtResults.Text = txtResults.Text & reader.GetString(1) & _
ControlChars.Tab & reader.GetString(2) & _
ControlChars.Tab & ControlChars.Tab & _
reader. GetString(0) & ControlChars.Ctrlf

Еще одно изменение, которое диктуется личным вкусом и стилем программирования автора, заключается в объединении определения объекта DataReader и выполнения метода ExecuteReader в одной строке, т.е. вместо фрагмента кода

Dim reader As OleDbDataReader reader = cmd.ExecuteReader()

можно использовать следующую строку:

Dim reader As OleDbDataReader = cmd.ExecuteReader()

После запуска приложения DataProviderObjects щелкните на кнопке ExecuteReader, и в текстовом поле справа будут отображены данные из представления EmployeeJobs_view, как показано на рис. 4.4.


РИС. 4.4. Результаты выполнения команды ExecuteReader из листинга 4.8


НА ЗАМЕТКУ

По окончании использования объекта DataReader следует вызвать метод Close. Дело в том, что выходные данные или возвращаемые значения объекта Command недоступны до тех пор, пока объект DataReader открыт. Он остается открытым до тех пор, пока открыто само подключение или объект DataReader.

Объект DataReader также предлагает простой и эффективный способ создания Web-страниц для работы сданными на основе элемента управления DataGrid, который подробно рассматривается в главе 11, "Web-формы: приложения на основе ASP.NET для работы с базами данных".

Использование объектов Connection и Command во время создания приложения

Вкладка Data панели элементов управления среды Visual Studio .NET содержит компоненты, которые соответствуют некоторым методам доступа к данным. С помощью окна Properties они позволяют указывать значения свойств во время создания приложения, а не только во время его выполнения. Они также предлагают визуальные инструменты указания значений для более сложных свойств.

Например, для реализации кода из листинга 4.8 с помощью таких элементов управления выполните перечисленные ниже действия.

1. Создайте еще одну форму Form2 в проекте DataProviderObjects.

2. В окне свойств Properties укажите значение Connection and Command Components для свойства Text формы Form2.

3. Увеличьте размер формы.

4. Включите в форму текстовое поле Textbox1.

5. В окне свойств Properties укажите значение True для свойства Multiline и значение Both для свойства Scrollbars этого текстового поля.

6. Увеличьте размер текстового поля так, чтобы оно покрывало большую часть формы.

7. Из вкладки Data панели элементов управления перетащите элемент управления OleDbConnection в форму Form2. Этот компонент невидим во время выполнения приложения, поэтому он появится в разделе невизуальных компонентов в нижней части окна редактирования формы.

8. В окне свойств Properties укажите приведенное ниже значение для свойства ConnectionString элемента управления OledbConnection1.

provider=SQLOLEDB;server=localhost;uid=sa;database=pubs

9. Из вкладки Data панели элементов управления перетащите еще один элемент управления OleDbCommand в форму Form2. Этот компонент также невидим во время выполнения приложения, поэтому он появится в нижней части окна редактирования формы.

10. В окне свойств Properties укажите значение OledbConnection1 для свойства Connection и приведенное ниже значение для свойства CommandText элемента управления OledbCommand1.

SELECT * FROM EmployeeJobs_view

11. Создайте код подпрограммы Form2_Load, приведенный в листинге 4.9.

СОВЕТ

В главе 6, "ADO.NET: объект DataAdapter" представлены графические инструменты, которые позволяют автоматически создать строку подключения и текст команды SQL вместо создания вручную их кода.

Листинг 4.9. Код создания объекта DataReader и извлечения значений полей с помощью компонентов SqlConnection и SqlCommand
Private Sub Form2_Load(ByVal sender As System.Object, _
" Открытие подключения перед вызовом метода ExecuteReader.
Dim reader = OleDbCommand1.ExecuteReader()
TextBox1.Text = TextBox1.Text & reader("fname") & _
ControlChars.Tab & reader("lname") & _
ControlChars.Tab & ControlChars.Tab & _
reader("job_desc") & ControlChars.CrLf
" Отмена выбора всех строк в текстовом поле.

12. Щелкните правой кнопкой мыши на проекте DataProviderObjects в окне Solution Explorer и выберите команду Properties в контекстном меню.

13. В папке Common Properties выберите элемент General, а затем выберите форму Form2 в текстовом поле Startup object (Объект запуска) данного приложения.

После запуска приложения DataProviderObjects в текстовом поле формы Form2 будут отображены данные из представления EmployeeJobs_view, как показано на рис. 4.5.

Другие провайдеры данных

Выше были представлены классы нескольких основных провайдеров данных (например, Parameter и Parameters), а также четыре основных объекта в табл. 4.1. В главе 5, "ADO.NET: объект DataSet", более подробно рассматривается объект DataSet и связанные с ним объекты, а в главе 6, "ADO.NET: объект DataAdapter", – объект DataAdapter.

В оставшейся части главы рассматривается еще один провайдер данных - объект-транзакция Transaction. Транзакции используются для гарантированного успешного завершения сразу нескольких связанных операций по принципу "все или ничего". Это значит, что либо все операции транзакции успешно выполняются, либо они вообще не выполняются. Классическим примером транзакции является банковская операция перечисления денежных средств. Эта операция состоит из двух этапов: удержание денежной суммы с одного счета и зачисление ее на другой счет. При этом желательно избегать ситуаций, когда успешно выполняется только первый этап транзакции!


РИС. 4.5. Результаты отображения данных в форме Form2 с помощью элементов управления OleDbConnection и OleDbCommand


Провайдеры данных ADO.NET содержат объект Transaction, который имеет фундаментальные методы обработки транзакций. Метод Commit фиксирует текущую транзакцию, а метод Rollback – откатывает (отменяет) текущую транзакцию. Выполнение транзакции и создание объекта Transaction осуществляется с помощью вызова метода BeginTransaction по отношению к открытому объекту Connection. Способ использования объекта Transaction демонстрируется на примере бизнес-ситуации 4.1.

Бизнес-ситуация 4.1: создание процедуры для архивирования старых заказов по годам

После относительно длительного использования системы управления базами данных некоторые данные рекомендуется архивировать. В каждой рабочей системе операцию архивирования следует включить в состав обязательных и регулярно выполняемых операций резервного копирования. Архивируемые данные - это данные, которые нужны не для постоянного использования (т.е. в оперативном режиме), а только изредка. Удаление этих данных из основных оперативных таблиц базы данных может повысить производительность операций доступа к этим таблицам, так как при этом приходится обрабатывать и фильтровать меньше записей. Однако архивная таблица часто хранится в идентичном формате таблицы и доступ к ней в случае необходимости можно организовать аналогичным образом. В этой бизнес-ситуации создается простая форма для выполнения простого архивирования данных из таблицы tblOrder базы данных Novelty. Она позволит выбирать и архивировать заказы по годам, т.е. после выбора нужного года выполняются перечисленные ниже действия.

1. Сначала в базе данных создается новая таблица tblOrderXXXX, где ХХХХ обозначает тот год, записи о заказах которого будут архивироваться.

2. Затем все записи о заказах за указанный год копируются из таблицы tblOrder втаблицу tblOrderXXXX.

3. Все скопированные записи о заказах за указанный год удаляются из таблицы tblOrder.

Хитрость здесь заключается в том, чтобы отменить всю транзакцию при неудачном выполнении какой-либо ее операции. Нам не нужна новая таблица, если в нее нельзя скопировать данные. Не нужно архивировать данные, если они не удаляются из основной таблицы. Аналогично, не нужно удалять никакие записи из основной таблицы, если они не скопированы в архивную таблицу. Для решения этой задачи можно использовать объект Transaction и вернуть (откатить) базу данных в исходное состояние в случае сбоя каких-то операций. Для создания формы с этими функциями выполните перечисленные ниже действия.

Запустите интегрированную среду разработки Visual Studio .NET.

2. Создайте новый проект Visual Basic Windows Application.

3. Назовите проект BusinessCase4.

4. Укажите путь к файлам проекта.

5. Увеличьте размер формы Form1.

6. В окне Properties укажите значение frmArchive для свойства (Name) и значение Archive Orders для свойства Text формы Form1.

7. Создайте в форме поле со списком lstYears, надпись Label1, кнопку bntOK и кнопку btnCancel, перетаскивая их из панели элементов управления.

8. В окне Properties укажите значение Archive all orders for the year для свойства Text надписи, значение OK для кнопки btnOK и значение Cancel для кнопки btnCancel.

9. Расположите все элементы управления, как показано на рис. 4.6.

РИС. 4.6. Расположение элементов управления в форме frmArchive


В верхней части файла с исходным кодом вставьте приведенную ниже строку кода для импорта пространства имен SqlClient.

В теле определения класса для формы frmArchive включите код из листинга 4.10.

Листинг 4.10. Код архивирования данных в новой таблице
Private Sub frmArchive_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
" Указание значения по умолчанию.

Private Sub btnCancel_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnCancel.Click

Private Sub btnOK_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnOK.Click
" Создание экземпляров объектов Connection и Command.
Dim cnn As SqlConnection = New SqlConnection(_
"server=localhost;uid=sa;database=novelty")
" Получение значения года.
SelectedYear = lstYears.SelectedItem.ToString
" Размещение кода внутри блока Try-Catch для
" обработки исключительных ситуаций.
" Открытие объекта Connection и запуск транзакции.
" Включение команды в транзакцию.
" Указание команды SQL для вставки соответствующих
" записей в архивную таблицу.
sql = "SELECT * INTO tblOrder" & SelectedYear & _
FROM tblOrder WHERE year (OrderDate) = " & SelectedYear
" Передача текста команды SQL в транзакцию.
result = cmd.ExecuteNonQuery()
" Отображение результатов вставки записей в архивную таблицу.
records = result MessageBox.Show(records & _
" records inserted successfully into tblOrder" & SelectedYear)
"No records inserted into tblOrder" & SelectedYear)
" При отсутствии записей созданная таблица
" не нужна и транзакцию нужно откатить.
" Команда SQL для удаления соответствующих
" записей из текущей таблицы.
sql = "delete FROM tblOrder WHERE year (OrderDate) = " _
" Эта команда находится в той же транзакции.
result = cmd.ExecuteNonQuery()
" Показать результаты удаления записей.
" records deleted successfully")
" Все действия успешно выполнены, можно фиксировать транзакцию.

Подпрограмма btnCancel_Click обработки щелчков мышью на кнопке Cancel просто закрывает форму, что в данном случае приводит к закрытию программы. Все необходимые действия выполняются обработчиком щелчков мышью на кнопке OK. После объявлений переменных следует получить выбранный год из списка lstYears и сохранить его для дальнейшего использования. Для гарантированной отмены транзакции в случае возникновения любой исключительной ситуации следует окружить активный код блоком Try-Catch-Finally.

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

Первые два этапа создания архивной таблицы и копирования выбранных строк выполняются с помощью одной команды SELECT, которая содержит предложение INTO имя_таблицы. Указанная таким образом таблица создается автоматически, а если такая таблица уже существует, то генерируется исключительная ситуация. Выбранное значение года добавляется к имени таблицы tblOrder для создания имени новой архивной таблицы.

НА ЗАМЕТКУ

Команда SELECT INTO не создает индекс, если он существует в исходной таблице. Для повышения производительности выполнения запросов по отношению к данной таблице, вероятно, придется создать индексы по одному или нескольким полям.

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

Если хотя бы одна запись скопирована в таблицу, то ее прототип в исходной таблице tblOrder удаляется с помощью команды DELETE, содержащей предложение WHERE с заданным годом. В случае успешного выполнения этой команды, т.e. если количество скопированных и удаленных строк совпадает, транзакция считается успешно завершенной и фиксируется. В противном случае, т.е. если какая-то отдельная операция завершится неудачно (нарушится процесс удаления записей, будет отменено разрешение на удаление архивируемых данных или произойдет сбой сервера), вся транзакция будет отвергнута. Откат транзакции гарантирует, что при неудачной попытке удаления корректных записей из таблицы tblOrder архивная таблица tblOrderХХХХ будет удалена.

До сих пор рассматривались только те исключительные ситуации, которые возникают при нарушении последовательного выполнения операций подпрограммы. Однако следует также позаботиться о многих других исключительных ситуациях, которые могут возникнуть во время выполнения. Например, исключительная ситуация может возникнуть при попытке создания уже существующей таблицы. Для ее перехвата и обработки следует использовать блок Try-Catch, который в данном случае выводит текстовое сообщение об исключительной ситуации, а вся транзакция отвергается.

НА ЗАМЕТКУ

Еще один блок Try-Catch потребуется для обработки исключительной ситуации, когда архивная таблица не может быть создана (например, из-за наличия именно такой таблицы). Основная причина, по которой она предлагается, заключается в том, что транзакция началась, но никаких изменений данных не произошло, а потому в файл регистрации транзакций не записано никаких действий, которые нужно откатить.

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

Выполните все описанные выше действия и поэкспериментируйте с этим приложением, создавая архивы для записей разных лет. Для проверки полученных результатов сопоставьте содержимое новой архивной и исходной таблиц до и после архивирования. Не забывайте, что содержимое исходной таблицы tblOrder всегда можно вернуть в первоначальное состояние, запуская сценарий ее создания и наполнения данными.

Резюме

В этой главе приводятся общие сведения об ADO.NET и некоторых объектах провайдеров данных.NET. Провайдеры данных образуют интерфейс ADO.NET для взаимодействия с физическими хранилищами данных и предлагают модель программирования в режиме подключения. Здесь кратко рассматриваются свойства и методы объектов Connection, Command, Parameter, DataReader и Transaction, включая стандартные провайдеры данных SqlClient, OleDb и Odbc. В главе 5, "ADO.NET: объект DataSet", описываются способы работы с данными в отключенном режиме на основе объектов DataSet и DataAdapter.

Вопросы и ответы

Судя по содержанию этой главы, модель ADO.NET предназначена для работы в отключенном режиме и нет никакой поддержки для серверных курсоров или пессимистической блокировки. А что же делать, если уже существующее приложение использует их или спецификации нового проекта требуют их применения? Следует ли мне использовать для этого только Visual Basic 6.0?

Во-первых, необходимо тщательно проанализировать приложение и убедиться в том, что действительно всегда нужно использовать серверные курсоры или пессимистические блокировки. Если они (или какие-то другие компоненты, которые не поддерживаются в ADO.NET) действительно необходимы, не стоит отчаиваться. Visual Basic. NET все равно можно использовать для таких приложений, так как на платформе.NET Framework предусмотрена расширенная поддержка взаимодействия с COM, что позволяет использовать в.NET-совместимом приложении COM-объекты, которые, в свою очередь, могут использовать управляемый (.NET-совместимый) код. Иначе говоря, можно использовать не только ADO 2.X, но и любые другие COM-объекты, для которых нет аналогов в.NET. Конечно, за организацию взаимодействия между COM и.NET придется заплатить снижением производительности. В какой мере? Ответ на этот вопрос можно получить только после тщательного тестирования приложения.

Похоже, что программирование объектов, методов и свойств ничем не отличается от способов их программирования в модели ADO 2.X. Зачем же переходить к модели ADO.NET?

Конечно, выполнение основных операций подключения к базе данных не очень отличается от таких же операций в модели ADO 2.X. Однако, кроме уже упомянутых здесь небольших усовершенствований, следует иметь в виду ряд важных отличий.

1. Visual Basic .NET и платформа.NET представляют собой совершенно новый мир, а модель ADO.NET является способом доступа к данным в этом мире.

2. Хотя при создании.NET-совместимых приложений можно продолжать использование уже существующих COM-компонентов, например из модели ADO 2.X, такой способ связан с сокращением производительности при доступе к COM-компонентам и необходимости их корректной инсталляции и регистрации.

3. Рассмотренные в этой главе объекты разных провайдеров данных отвечают только за часть всех операций с базой данных, а именно за чтение и запись данных в подключенном режиме. Другая часть операций выполняется в отключенном режиме и связана с объектом DataSet. Именно в этом режиме прекрасно проявляются преимущества модели ADO.NET, и потому в следующей главе описываются базовые блоки объекта DataSet.


Центр обработки и хранения данных под ключ от компании