В этой задаче. Программное создание Connection

Временами у программистов возникает жгучее желание прочитать что-то из базы данных. Программисты становятся нервными и раздражительными, теряют сон и лихорадочно тыкают пальцами в клавиатуру. Ради всеобщего блага и мира во всем мире рассмотрим несложную работу с базой данных из C# с помощью ADO.NET и OLE DB. Хотя данный механизм поддерживает разные базы данных вроде Oracle, здесь и сейчас будем использовать MS SQL Server.

Две основных задачи при работе с базой данных

2. Выполнение sql-команды, что-то делающей на сервере (insert, update, delete, вызов функции или хранимой процедуры

3. Последовательное чтение из select-выборки строки за строкой. В основном используется в веб-приложениях, в настольных проще скачать сразу всю выборку в оперативную память, экономия от чтения только нужных строк незначительна.

4. Редкий случай. Автоматическое обновление таблицы в базе на основании изменений в DataTable (как правило редактируемой через визуальный интрфейс). В реальной жизни данные обычно читаются через сложный запрос с кучей join или представление, так что автоматическая синхронизация не подходит.

Основные классы, используемые для этих целей: OleDbConnection - соединение с базой, создаем со строкой, содержащей параметры соеднинения, открываем, закрываем, OleDbCommand - создаем с экземпляром соединения и sql-командой, если нужно просто выполнить update или получить единичное значение, то хватит этого класса, OleDbDataAdapter - создается с OleDbCommand, специализируется на разовом чтении наборов строк в DataTable, может автоматически создавать колонки DataTable на основании выборки, переносить изменения из DataTable в таблицу в базе, OleDbDataReader - последовательное чтение строк в DataTable по одной за раз (он работает внутри OleDbDataAdapter), DataTable / DataSet - основной контейнер для данных. В перечислении OleDbType хранятся типы данных базы данных.

Using System.Data; using System.Data.OleDb; // строка соединения, используется система пользователей Windows (Integrated Security=SSPI;) string connString = "Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=databaseName;Data Source=serverName"; // альтернативная строка соединения с иcпользованием аутентификации MS SQL Server // connString = "Provider=SQLOLEDB.1;Persist Security Info=False;Initial Catalog=databaseName;Connect Timeout=20;Data Source=serverName;Uid=userName;Pwd=userPassword;" OleDbConnection dbConn = new OleDbConnection(connString); dbConn.Open(); someDataTable = new DataTable(); OleDbDataAdapter dbAdapter = new OleDbDataAdapter("select COLUMN1, COLUMN2 from TEST_TABLE ORDER BY COLUMN2", dbConn); // внутренняя структура пустой таблицы будет создана автоматически на основании прочитанных данных, если структура таблицы уже задана (например через типизированный DataSet), то данные будут писаться в столбцы с совпадающими именами или добавятся новые столбцы dbAdapter.Fill(someDataTable); // альтернативный вариант для заполнения таблицы в DataSet // dbAdapter.Fill(someDataSet, "someDataTable"); dbConn.Close();

2. Выполнение sql-команды, что-то делающей на сервере (insert, update, delete, вызов функции или хранимой процедуры .

Особенно стоить отметить проблемы с датами. Реализация дат в.Net крайне кривая - изначально даты не могут быть пустыми, а реальной жизни они пустые сплошь и рядом. Самое правильное решение - использовать специальный класс даты, исправляющий косяки программистов из Microsoft. Более ленивые разработчики держат все даты в коде строками и конвертируют их в DateTime только при необходимости, например при записи в базу или DataTable. Nullable DateTime не спасает, так как пустая дата в интерфейсе должна выглядеть пустой строкой, а при записи в базу как DBNull.Value - и банальный null в коде не конвертируется в эти значения без дополнительных плясок с бубном.

Первый вариант подразумевает банальное склеивание строки запроса. Считается плохой практикой и особенно опасен в веб-приложениях, так как уязвим для хакерских атак. Проблема с пустыми датами так просто не решается. Кроме того при работе с датами появляется дополнительная проблема - разные форматы строк даты в зависимости от региональный настроек.Net Framework, среды разработки и sql-сервера. Выглядеть это может сюрреалистически - один и тот же запрос работает в SQL Managment Studio, но вылетает при выполнении из кода. Частично спасает особый формат строки дат, Тем не менее так нередко делают в маленьких программках для внутреннего пользования, о существовании которых внешний мир никогда не узнает.

OleDbCommand dbCommand = dbConn.CreateCommand(); dbCommand.CommandText = "INSERT INTO TEST_TABLE (INT_COLUMN, VARCHAR_COLUMN, DATETIME_COLUMN) VALUES (" + intVariable", " + stringVariable" , " + dateTimeVariable.ToString("yyyyMMdd") +")"; dbCommand.ExecuteNonQuery();

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

OleDbCommand dbCommand = dbConn.CreateCommand(); dbCommand.CommandText = "INSERT INTO TEST_TABLE (INT_COLUMN, VARCHAR_COLUMN, DATETIME_COLUMN) VALUES (?, ?, ?)"; dbCommand.Parameters.Add("INT_COLUMN", OleDbType.Integer).Value = intVariable; dbCommand.Parameters.Add("VARCHAR_COLUMN", OleDbType.VarChar).Value = stringVariable; if (stringDate == "") { dbCommand.Parameters.Add("DATETIME_COLUMN", OleDbType.DateTime).Value =DBNull.Value; } else { dbCommand.Parameters.Add("DATETIME_COLUMN", OleDbType.DateTime).Value = Convert.ToDateTime(stringDate); } dbCommand.ExecuteNonQuery();

Хранимая процедура вызывается точно так же, разнообразия ради другой вариант записи значений в параметры (он не связан именно с хранимой процедурой):

OleDbCommand someDbComm = new OleDbCommand("someStoredProcedure", this.dbConn); someDbComm.CommandType = CommandType.StoredProcedure; someDbComm.Parameters.Add("@parameter1", OleDbType.VarChar); someDbComm.Parameters.Add("@parameter2", OleDbType.VarChar); someDbComm.Parameters.Value = "У всякой проблемы всегда есть решение - про­стое, удобное и, конечно, ошибочное"; someDbComm.Parameters.Value = "Генри Луис Менкен"; someDbComm.ExecuteNonQuery();

Расширенный вариант описания параметра с указанием размера поля и привязкой к конкретной колонке таблицы.

DbCommand.Parameters.Add("VARCHAR_COLUMN", OleDbType.VarChar, 100, "VARCHAR_COLUMN").Value = stringVariable;

Если нам не нужна привязка параметра команды к определенному полю DataTable, то лучше всего не указывать размер вообще, так например если строка будет меньше указанной длинны Varchar, то добрый.Net фреймворк добавит в строку пробелов до указанной длины, испортив передаваемые на сервер данные.

Значение единичного поля читается методом ExecuteScalar()

OleDbCommand dbCommand = dbConn.CreateCommand(); dbCommand.CommandText = "SELECT TEST_COLUMN FROM TEST_TABLE WHERE ID_COLUMN = ?"; dbCommand.Parameters.Add("INT_COLUMN", OleDbType.Integer).Value = intVariable; int result = Convert.ToInt32(dbCommand.ExecuteScalar());

Надо особо отметить, что ExecuteScalar возвращает Object и если запрос не вернул вообще ничего, то результат будет null и конвертация в нормальный тип данных вылетит с ошибкой. Если возможна ситуация, когда в ответ мы ничего не получим, то надо делать так:

OleDbCommand dbCommand = dbConn.CreateCommand(); dbCommand.CommandText = "SELECT TEST_COLUMN FROM TEST_TABLE WHERE ID_COLUMN = ?"; dbCommand.Parameters.Add("INT_COLUMN", OleDbType.Integer).Value = intVariable; object resultObj = dbCommand.ExecuteScalar() int result = -1; // значение по умолчанию, означающее пустой результат if(resultObj != null) { result = Convert.ToInt32(dbCommand.ExecuteScalar()); }

3. Последовательное чтение из select-выборки строки за строкой
Чтение одной строки (несколько читаются в цикле);

OleDbCommand dbCommand = new OleDbCommand(select PERSON_ID, NAME, SURNAME from TEST_TABLE, dbConn); OleDbDataReader dbReader = dbCommand.ExecuteReader(); dbReader.Read(); string name = Convert.ToString(dbReader["NAME"]); string surname = Convert.ToString(dbReader["SURNAME"]); dbReader.Close();

4. Редкий случай. Автоматическое обновление таблицы в базе на основании изменений в DataTable (как правило редактируемой через визуальный интерфейс).

Необходимо прописать для DbAdapter четыре команды на каждый возможный случай - select, insert, update, delete.

DbAdapter.InsertCommand = new OleDbCommand("insert into TEST_TABLE (NAME, FAMIL, AGE) values (?, ?, ?)", dbConnection); dbAdapter.InsertCommand.Parameters.Add("NAME", OleDbType.VarChar, 100, "NAME"); dbAdapter.InsertCommand.Parameters.Add("FAMIL", OleDbType.VarChar, 100, "FAMIL"); // для типов данных с неизменной длинной указанная в команде длинна игнорируется dbAdapter.InsertCommand.Parameters.Add("AGE", OleDbType.Integer, 100, "AGE"); // добавляем команду обновления dbAdapter.UpdateCommand = new OleDbCommand("update TEST_TABLE set NAME = ?, FAMIL = ?, AGE = ? where ID = ?", dbConnection); dbAdapter.UpdateCommand.Parameters.Add("NAME", OleDbType.VarChar,100, "NAME"); dbAdapter.UpdateCommand.Parameters.Add("FAMIL", OleDbType.VarChar, 100, "FAMIL"); dbAdapter.UpdateCommand.Parameters.Add("AGE", OleDbType.Integer, 100, "AGE"); dbAdapter.UpdateCommand.Parameters.Add("ID", OleDbType.Integer, 100, "ID"); // добавляем команду удаления dbAdapter.DeleteCommand = new OleDbCommand("delete from TEST_TABLE where ID = ?", dbConnection); dbAdapter.DeleteCommand.Parameters.Add("ID", OleDbType.Integer, 100, "ID"); try { // переносит все изменения из DataTable в таблицу в базе данных dbAdapter.Update(table); } catch (Exception error) { MessageBox.Show("Ошибка при сохранении данных! " + error.Message); return; } MessageBox.Show("Изменения сохранены!");

Было упомянуто всего несколько слов о том, что в состав Провайдера
Данных (Data Provider) входит объект Соединение (Connection ).
В данной статье мы рассмотрим этот объект более подробно, выясним его роль в
ADO.NET, основные свойства и методы.
Назначение объекта Connection
поддерживать физическую связь между хранилищем данных и приложением.NET.
Поскольку, как мы уже знаем, в составе ADO.NET имеется два Провайдера Данных, а
каждый из них имеет собственную реализацию объекта Connection, нам придется
рассмотреть обе разновидности.

OLE DB .NET Data Provider включает в себя OleDbConnection
(пространство имен System.Data.OleDB ), а SQL Server .NET Data
Provider, соответственно, - SqlConnection (пространство имен
System.Data.SqlClient
). Хотя различия между ними не столь
радикальны, тем не менее, их следует иметь в виду при переносе приложений на
другое хранилище данных.

Как упоминалось ранее, SqlClient работает с
хранилищем данных напрямую, поэтому при работе с Microsoft SQL Server 7.0 и
выше предпочтительнее использовать SQL Server .NET Data Provider и SqlConnection ,
поскольку это обеспечит более высокую эффективность работы приложения.

Как создать соединение?

Те, кто работает в интерактивной среде разработки Visual
Studio .NET, наверное, уже привыкли к тому, что в ней имеется большой набор
«мастеров», которые облегчают труд программиста, выполняя за него
обременительную рутинную работу. Не является исключением и создание Connection .

Те, кто проповедует аскетизм.NET Framework (или вынужден
довольствоваться им поневоле), могут пропустить этот раздел и перейти к
следующему, в котором описывается программное создание Connection.

Создание Connection посредством Server Explorer

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

Попробуем создать Соединение при помощи Server Explorer. Для
этого сначала создадим проект приложения Windows. Затем убедимся, что панель
Server Explorer доступна (если нет, включить ее можно посредством меню View
->Server Explorer). Выглядит она примерно таким образом:


Такая же кнопка имеется в меню Tools:


После нажатия на кнопку появляется окно «Свойства связи с
данными», хорошо знакомое тем, кто уже имеет опыт работы в ADO:


Попробуем создать соединение с базой данных «Борей», которая
поставляется в качестве примера вместе с Microsoft Access различных версий и
наверняка присутствует на подавляющем большинстве компьютеров, на которых
установлен Microsoft Office. Для этого:


    переходим на страницу «Поставщик данных»;


    выбираем из списка «Поставщики OLE DB» строку «Microsoft Jet
    4.0 OLE DB Provider»


Примечание. Microsoft Jet – это ядро базы данных, которое
Microsoft использует как основу для Access, а также в составе ряда других
продуктов (например, для хранения электронной корреспонденции в Exchange
Server). С помощью провайдера Microsoft Jet 4.0 OLE DB Provider можно получить
доступ к данным, хранящимся в базах данных, созданных посредством Access.



  • жмем кнопку «Далее >>» или переходим на страницу
    «Подключение»;

нажав на кнопку с многоточием «…», выбираем полный путь к
файлу базы данных (или вводим вручную, если есть желание); у меня он выглядит
так: C:\Program Files\Microsoft Office\Office10\Samples\Борей.mdb;


если на вашей системе база данных «Борей» защищена паролем,
введите его в соответствующем поле; если вы желаете, чтобы пароль для доступа к
базе данных хранился месте с другими данными о соединении, установите опцию
«Разрешить сохранение пароля» (разумеется, это нежелательно, если содержимое
базы не должно быть открыть для несанкционированного доступа);


    для проверки работоспособности нажмите кнопку «Проверить
    подключение»; если все сделано правильно, вы увидите результат:



Если по какой-либо причине подключение к базе данных не может
быть выполнено, вы получите сообщение об ошибке вроде следующего:


В этом случае проверьте правильность введенного пути к файлу
базы данных и, возможно, уточните пароль;


    если проверка подключения к базе данных прошла успешно,
    нажмите кнопку «OK» окна «Свойства связи с данными»;


    на панели Server Explorer появляется новое соединение с
    базой данных «Борей»:



Посредством Server Explorer мы можем не только исследовать
структуру таблиц, видов, хранимых процедур и прочих объектов базы данных, но и
получить доступ к их содержимому. Например, двойной щелчок левой кнопкой мыши
на значке таблицы открывает ее содержимое для просмотра и даже редактирования в
среде Visual Studio .NET.


    теперь осталось создать объект Connection, который позволит
    нашей программе установить соединение с базой данных «Борей»; для этого
    буксируем пиктограмму соединения с панели Server Explorer на форму нашего
    приложения.


На этом работа по созданию объекта Connection
завершена! Под формой приложения появился значок:


Это и есть объект типа System.Data.OleDb.OleDbConnection ,
который мы стремились получить. При необходимости имя объекта можно изменить
при помощи панели свойств.

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

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

Итак, подведем итог проделанной работе. В результате наших
действий на панели Server Explorer появилось соединение с базой данных «Борей»,
которое посредством буксировки мышью было помещено на форму нашего приложения.
Появилась соответствующая пиктограмма, при ее выборе мышью свойства объекта
Connection видны на панели свойств Проверка кода программыпоказывает, что среди
объектов формы появился новый
private System.Data.OleDb.OleDbConnection oleDbConnection1;
а в методе InitializeComponent появились строки:
this.oleDbConnection1 = new System.Data.OleDb.OleDbConnection();

// oleDbConnection1

This.oleDbConnection1.ConnectionString =

Source=C:\Program Files\Microsoft Office\Office10\Samples\Борей.mdb;Mode=Share
Deny None;Extended Properties="""";Jet OLEDB:System database="""";Jet
OLEDB:Registry Path="""";Jet OLEDB:Database Password="""";Jet OLEDB:Engine
Type=5;Jet OLEDB:Database Locking Mode=1;Jet OLEDB:Global Partial Bulk
Ops=2;Jet OLEDB:Global Bulk Transactions=1;Jet OLEDB:New Database
Password="""";Jet OLEDB:Create System Database=False;Jet OLEDB:Encrypt
Database=False;Jet OLEDB:Don"t Copy Locale on Compact=False;Jet OLEDB:Compact
Without Replica Repair=False;Jet OLEDB:SFP=False";
Мы видим, что помимо создания объекта OleDbConnection ,
его свойству ConnectionString присвоено значение в
виде довольно-таки длинной строки. Что означает сия конструкция, мы
разберем немного позже, а пока рассмотрим альтернативные способы создания
объекта Connection .

Примечание. Для полноты изложения стоит упомянуть, что помимо
панели Server Explorer, с Connection можно также работать и посредством ссылок
на базу даннах (Database References) из панели Solution Explorer Visual Studio
.NET. Однако подробно на этом останавливаться не будем, поскольку этот вопрос
скорее относится к работе IDE, а не к функциональности Connection ADO.NET.

Программное создание Connection

Разумеется, как и практически любой объект в Visual Studio
.NET, создаваемый посредством вызова «мастера», экземпляр объекта Connection
можно создать и программным путем. Особенно полезным такой способ является при
создании консольных приложений.NET, поскольку «мастер» создает экземпляр
Connection
при буксировании его на форму приложения, а отсутствие
самой формы делает такой подход непригодным.
Вряд ли для читателя будет неожиданностью, что для создания
Connection
нужно вызвать конструктор. Обе разновидности Connection
имеют два перегруженных варианта конструктора: с пустым списком параметров () и
с единственным параметром (ConnectionString ), который задает
строку подключения (и опять откладываем разъяснение, что же это такое).

Пример программного создания Connection :

Public SqlConnection cnnTest = new SqlConnection();
(разумеется, предполагается, что в программе предварительно
было объявлено, что используется пространство имен System.Data.SqlClient ).

Свойства Connection

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

Для управления соединением с источником данных из программы
объект Connection , разумеется, имеет набор свойств и методов.
Рассмотрим подробнее свойства обеих реализаций объекта.

Название Описание Тип Доступ OLE DB SQL Значение по умолчанию
ConnectionString Строка соединения System.String RW + +
ConnectionTimeout Время ожидания соединения в секундах System.Int32 R + + 15 сек
Database Имя текущей базы данных (или той, которая будет использоваться при открытии соединения) System.String R + +
DataSource Имя сервера баз данных (SQL) или спецификация файла с данными System.String R + +
PacketSize Размер сетевого пакета в байтах System.Int32 R - + 8192
Provider Имя провайдера OLE DB System.String R + -
ServerVersion Версия сервера баз данных System.String R + +
State Битовая комбинация значений ConnectionState ConnectionState R + + Closed
WorkstationId Строка, идентифицирующая клиента System.String R - + Имя клиентского компьютера

Примечания:


    В графе «Доступ» RW обозначает запись и чтение, R – только
    чтение.


    В графах «OLE DB» и «SQL» указано наличие данного свойства
    для OleDbConnection и SqlConnection ,
    соответственно. «+» – свойство поддерживается, «-» – не поддерживается.


    В графе «Значение по умолчанию прочерк «–» для свойств типа
    System.String
    означает пустую строку.


    Перечислены только собственные свойства объекта, без учета
    свойств, наследуемых от Component.


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

Обратите внимание на небольшое различие в свойствах OleDbConnection
и SqlConnection . Хотя основной набор их одинаков, все же
некоторые специфичны для провайдера. SqlConnection имеет
уникальные свойства PacketSize и WorkstationId ,
а OleDbConnection – свойство Provider . Это
может негативно повлиять на переносимость программ на другую платформу.

Следует избегать задания свойству ConnectionTimeout
нулевого значения, т.к. оно соответствует бесконечному тайм-ауту и может
привести к зависанию приложения.

Значение свойства Database может изменяться в
ходе выполнения приложения, если оно меняет текущую базу данных (либо
соответствующим оператором SQL, либо вызовом метода ChangeDatabase ).

Значение свойства PacketSize может быть
задано в диапазоне от 512 до 32767 байт. При пересылке больших объемов данных
увеличение размера пакета может повысить производительность, т.к. уменьшает
количество небходимых операций чтения/записи пакетов.

Свойство State может принимать значения из
перечисления ConnectionState . Эти значения следующие:

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

Строка соединения (Connection String)

Наконец-то пришла пора рассказать о том, что же такое
представляет собой строка соединения. Этот термин уже упоминался ранее. Однако
предмет достаточно сложен и должен быть рассмотрен подробнее.

Свойство ConnectionString является, пожалуй,
основным свойством Connection . Все остальные свойства, как мы
видели ранее, доступны только на чтение и передают значение одного из
параметров, задаваемых в строке соединения.

Строка соединения представляет собой текстовую строку
специального вида. Она состоит из пар вида «ключ=значение», разделенных
символом «точка с запятой» (;).

«Ключ» не зависит от регистра и может задаваться как на
верхнем, так и на нижнем регистрах; «значение» в общем случае зависит от
регистра.

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

Содержимое строки специфично для конкретного провайдера.
Обычно для построения корректной строки во время проектирования программы
полезно бывает воспользоваться следующей уловкой: создать соединение при помощи
Server Explorer (мы уже умеем это), а затем скопировать строку соединения из
его свойств.

Значение свойства ConnectionString можно
задавать только при закрытом соединении ! После установки Connection
анализирует строку и устанавливает значения остальных свойств в соответствии с
ее содержимым. Впрочем, полный анализ содержимого строки соединения
производится только в момент открытия Connection . Если при
этом встретится неподдерживаемое имя ключа или недопустимое значение, будет
сгенерировано соответствующее исключение (OleDbException или
SqlDbException
).

Объект Connection реализует интерфейс IDbConnection ,
поэтому для доступа к общим свойствам обоих провайдеров можно объявить
переменную типа IDbConnection . Это позволит немного смягчить
проблему неполной совместимости двух провайдеров данных.

Методы Connection

К счастью, методы SqlConnection и OleDbConnection
совершенно одинаковы.

Наиболее важными, пожалуй, являются Open и
Close
. Они могут вызываться как явно, так и неявно, другими
компонентами ADO.NET. Если метод Open вызывается компонентами
DataAdapter
или DataCommand (которые мы рассмотрим
в следующих статьях), то они оставляют Connection в начальном
состоянии, т.е. если Connection был открыт, то ничего не
меняется, а если закрыт – то вызывающий компонент открывает его, выполняет
требуемую операцию, а затем закрывает. Таким образом, программист может
игнорировать побочные эффекты этих компонентов.

Открытое соединение не закрывается автоматически при
уничтожении объекта Connection
! Необходимо помнить, что
открытое соединение потребляет ресурсы как рабочей станции, так и
сервера-источника данных. Поэтому следует взять за правило явно закрывать
соединение посредством вызова Close и делать это как можно
раньше, как только соединение больше не требуется. При закрытии соединения
Close
откатывает все незавершенные транзакции.

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

CreateCommand создает объект Datacommand ,
который мы еще не рассматривали. Поэтому нам придется пока довольствоваться
этим скудным описанием. Смысл данной операции станет ясен позже.

Пул соединений

Для более экономного использования ресурсов ADO.NET работает с
пулом соединений. SQL Server .NET Data Provider имеет собственный пул
соединений, а OLE DB .NET Data Provider использует пул OLE DB. Управлять пулом
можно соответствующими параметрами строки соединения, но подробное рассмотрение
этого вопроса выходит за рамки статьи.

Для начала достаточно знать, что пул соединений – это некое
хранилище готовых к употреблению соединений. По умолчанию пул соединений
разрешен, и при закрытии соединения через Close оно на самом
деле не закрывается физически, а помещается в пул. Если при попытке открыть
новое соединение через Open обнаруживается, что такое готовое
соединение есть в пуле, оно не создается, а просто выдается приложению из пула.
Поскольку на установление соединения требуется время, которое для удаленного
сервера может оказаться весьма ощутимым, использование пула может существенно
ускорить работу приложения.

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

События connection

Эти события немногочисленны, их всего два, и они совпадают для
обоих вариантов Connection .

StateChange возникает при изменении состояния
Connection, т.е. когда его свойство State меняет значение с
Open
на Closed или наоборот. Обработчик этого
события получает аргумент типа StateChangeEventArgs , который
имеет два свойства: OriginalState (исходное состояние
соединения) и CurrentState (его текущее состояние). Оба эти
свойства могут принимать одно из значений перечисления ConnectionState ,
которое мы уже рассмотрели ранее при изучении свойства State .

InfoMessage возникает, когда источник данных
возвращает предупреждение или информационное сообщение. Оба варианта соединения
возвращают коллекцию ошибок, текстовое описание ошибки и имя объекта, в котором
возникла ошибка. OleDbConnection.InfoMessage имеет
дополнительное свойство ErrorCode , которое идентифицирует
источник ошибки в соответствии со стандартом ANSI SQL.

Итоги

Итак, подведем итог, чему мы научились в этой статье.

Прежде всего – весьма, на мой взгляд, важный факт, про который
не следует забывать. Имеется две реализации Connection – для
OLE DB и MS SQL Server. Эти реализации довольно похожи, но не идентичны.
Поэтому на начальном этапе разработки следует определиться, чем пожертвовать –
эффективностью или переносимостью, так как в дальнейшем может потребоваться
переделка программы.

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

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

Основное свойство Connection – это ConnectionString ,
именно в нем задаются все параметры, необходимые для установления соединения.
Все остальные свойства, кроме State , являются производными и
возвращают значения отдельных параметров соединения, но изменять их можно
только через ConnectionString.

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

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

При изменении состояния соединения возникает событие StateChange ,
а при получении предупреждения или информационного сообщения – событие InfoMessage .

Заключение

И наконец в закрепление знаний, полученных нами о соединениях ADO.NET, приведу
небольшую программу, которая способна открывать и закрывать соединения с
учебной базой данных (кнопки "Open"
и
"Close" ), а также показывать состояние и свойства соединения
(кнопка "Show" ). Программа может быть откомпилирована и выполнена в среде
Visual Studio .NET или.NET Framework.

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


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


    Не все свойства Connection доступны при закрытом соединении, в
    чем вы можете убедиться, нажав кнопку
    при закрытом соединении. Впрочем, вряд ли кому-то понадобятся свойства
    закрытого соединения.


Код программы-примера для этой статьи:

Using System;

Using System.Windows.Forms;

Using System.Data;

Namespace ConnSample

{
public class frmConnSample: System.Windows.Forms.Form

{
private System.Windows.Forms.Button btnExit;
private System.Windows.Forms.Button btnOpen;
private System.Windows.Forms.Button btnClose;
private System.Windows.Forms.Label lblConnState;
private System.Windows.Forms.TextBox txtConnstate;
private System.Data.OleDb.OleDbConnection oleDbCnn1;
private System.Windows.Forms.Button btnShow;
private System.Windows.Forms.ListBox lstConnProps;
private System.ComponentModel.Container components = null;

Public frmConnSample()
{
InitializeComponent();
}

Protected override void Dispose(bool disposing
{
if(disposing)
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose(disposing);
}

#region Windows Form Designer generated code
private void InitializeComponent()
{
this.btnExit = new

this.oleDbCnn1 = new
System.Data.OleDb.OleDbConnection();
this.btnOpen = new
System.Windows.Forms.Button();
this.btnClose = new
System.Windows.Forms.Button();
this.lblConnState = new
System.Windows.Forms.Label();
this.txtConnstate = new
System.Windows.Forms.TextBox();
this.btnShow = new
System.Windows.Forms.Button();
this.lstConnProps = new
System.Windows.Forms.ListBox();
this.SuspendLayout();
// btnExit
this.btnExit.Location = new
System.Drawing.Point(296, 232);
this.btnExit.Name = "btnExit";
this.btnExit.TabIndex = 0;
this.btnExit.Text = "Exit";
this.btnExit.Click += new
System.EventHandler(this.btnExit_Click);
// oleDbCnn1
// строка подключения; подкорректируйте ее
согласно размещению файлов на вашем компьютере
this.oleDbCnn1.ConnectionString =
@"Provider=Microsoft.Jet.OLEDB.4.0;Password="""";User ID=Admin;Data
Source=C:\Program Files\Microsoft Office\Office10\Samples\Борей.mdb";
// добавляем обработчик события StateChange
this.oleDbCnn1.StateChange += new
System.Data.StateChangeEventHandler(this.ShowConnState);
// btnOpen
this.btnOpen.Location = new
System.Drawing.Point(16, 232);
this.btnOpen.Name = "btnOpen";
this.btnOpen.TabIndex = 2;
this.btnOpen.Text = "Open";
this.btnOpen.Click += new
System.EventHandler(this.btnOpen_Click);
// btnClose
this.btnClose.Location = new
System.Drawing.Point(104, 232);
this.btnClose.Name = "btnClose";
this.btnClose.TabIndex = 3;
this.btnClose.Text = "Close";
this.btnClose.Click += new
System.EventHandler(this.btnClose_Click);
// lblConnState
this.lblConnState.Location = new
System.Drawing.Point(16, 200);
this.lblConnState.Name = "lblConnState";
this.lblConnState.Size = new
System.Drawing.Size(96, 16);
this.lblConnState.TabIndex = 4;
this.lblConnState.Text = "Connection state:";
// txtConnstate
this.txtConnstate.Location = new
System.Drawing.Point(112, 200);
this.txtConnstate.Name = "txtConnstate";
this.txtConnstate.TabIndex = 5;
this.txtConnstate.Text = "";
// btnShow
this.btnShow.Location = new
System.Drawing.Point(296, 200);
this.btnShow.Name = "btnShow";
this.btnShow.TabIndex = 6;
this.btnShow.Text = "Show";
this.btnShow.Click += new
System.EventHandler(this.btnShow_Click);
// lstConnProps
this.lstConnProps.Location = new
System.Drawing.Point(16, 16);
this.lstConnProps.Name = "lstConnProps";
this.lstConnProps.Size = new
System.Drawing.Size(344, 173);
this.lstConnProps.TabIndex = 7;
// frmConnSample
this.AutoScaleBaseSize = new
System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(384,
266);
this.Controls.AddRange(new
System.Windows.Forms.Control

{
this.lstConnProps,
this.btnShow,
this.txtConnstate,
this.lblConnState,
this.btnClose,
this.btnOpen,
this.btnExit
});
this.FormBorderStyle =
System.Windows.Forms.FormBorderStyle.Fixed3D;
this.MaximizeBox = false; this.MinimizeBox =
false; this.Name = "frmConnSample";
this.Text = "Connection Sample";
this.ResumeLayout(false);
}
#endregion


static void Main()
{
Application.Run(new frmConnSample());
}

Private void btnExit_Click(object sender, System.EventArgs
e)
{
Application.Exit();
}

Private void btnOpen_Click(object sender, System.EventArgs
e)
{
try
{
oleDbCnn1.Open();
}
catch (InvalidOperationException err) // попытка
открыть уже открытое соединение
{
MessageBox.Show("Connection is
already open!!!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
catch (System.Data.OleDb.OleDbException err)
{
MessageBox.Show("Error opening
Connection!!!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}

Private void btnClose_Click(object sender, System.EventArgs
e)
{
oleDbCnn1.Close();
}

Private void btnShow_Click(object sender, System.EventArgs
e)
{
try
{
lstConnProps.Items.Clear();

LstConnProps.Items.Add("ConnectionString = " + oleDbCnn1.ConnectionString);

LstConnProps.Items.Add("ConnectionTimeout = " +
oleDbCnn1.ConnectionTimeout.ToString());
lstConnProps.Items.Add("Database = "
+ oleDbCnn1.Database);
lstConnProps.Items.Add("DataSource =
" + oleDbCnn1.DataSource);
lstConnProps.Items.Add("Provider = "
+ oleDbCnn1.Provider);

LstConnProps.Items.Add("ServerVersion = " + oleDbCnn1.ServerVersion);
lstConnProps.Items.Add("State = " +
oleDbCnn1.State);
}
catch (System.InvalidOperationException err)
{
MessageBox.Show("Cannot get some
properties of the closed Connection!!!", "Warning", MessageBoxButtons.OK,
MessageBoxIcon.Warning);
}
}

// индикатор текущего состояния соединения
private void ShowConnState(object sender,
StateChangeEventArgs e)
{
txtConnstate.Text = oleDbCnn1.State.ToString();
}
}
}




Top