Работа с InterBase и Firebird в ADO.NET. Часть 2 — Использование визуальных средств Visual Studio 2008 (2005)
Создание подключения к Firebird в Server Explorer
Встроенные инструменты Visual Studio 2005 могут оказаться незаменимым подспорьем при написании приложений баз данных InterBase и Firebird. Давайте по шагам создадим пример простого приложения JobManager.
Выберем команду «Add Connection». Откроется диалог с выбором источника данных. В нем выбираем Data Source = <other> и Data provider = .Net Framework Data Provider for OLE DB. Жмем Ok:
Далее появиться уже знакомый диалог редактирования подключения:
Выберете из списка OleDb провайдеров — IBProvider v3. И нажмите кнопку Data Links:
Здесь есть два важных момента: необходимо включить опции «Разрешить автоматические транзакции» и «Разрешить сохранение пароля». Убедимся что, все настроено правильно, нажав на кнопку «Проверить подключение».
Если мы все сделали правильно в, списке подключений Server Explorer появится новое, для которого будет доступен список объектов базы данных:
Создание каркаса приложения
Создайте новую форму JobForm. Добавьте на форму ComboBox и перейдите в редактор ComboBox Tasks:
Далее необходимо в свойстве Data Source выбрать действие «Add Project Data Source». Откроется мастер создания источников данных:
Выбираем тип источника Database и идем далее. В списке подключений будет уже созданное ранее в Server Explorer подключение:
Делаем все, как показано на рисунке и переходим на следующий шаг:
Мастер предлагает нам сохранить параметры источника данных в файле конфигурации приложения. По умолчанию строка подключения будет сохранена в области «Application Settings» и будет не доступна для редактирования внутри приложения. Это можно изменить, установив для настройки свойство Scope = User. А пока разрешим создание секции в конфигурационном файле и двигаемся далее:
На завершающем шаге нам предлагают создать DataSet. Выберем для него все доступные таблицы базы данных и жмем кнопку «Finish».
Теперь в качестве источника данных для нашего ComboBox выбираем таблицу EMPLOYEE. Свойству DisplayMember установим значение FULL_NAME:
После завершения операции на форму будут добавлены три новых компонента. Это DataSet, TableAdapter и BindingSource. DataSet нам уже знаком, а вот два других объекта появились только в Net 2.0 и будут рассмотрены далее.
Класс TableAdapter
Он является ключевым звеном в цепочке связи данных с пользовательскими элементами управления. Если провести аналогию с терминами М. Фаулера [1], то TableAdapter является шлюзом таблицы данных для DataTable. Он инкапсулирует в себе логику обновления загрузки и поиска данных и относится к Data Access Layer. Что же касается DataSet и DataTable, то их можно отнести к уровню бизнес логики (Business Layer)
Visual Studio .Net 2005 сама позаботилась о генерации кода этого класса. Давайте посмотрим, что же она нам предлагает. Итак:
- методы Fill() и GetData() — единственное отличие в том, что Fill принимает существующую DataTable в качестве аргумента, а GetData() создает новую, заполняет её данными и возвращает клиенту.
- Свойство ClearBeforeFill, которое используется вышеназванными методами дял определения стоит ли очищать таблицу перез её заполнением.
- Стандартный набор CRUD операций: insert, update, delete среди которых присуствуют перегруженные методы с типизированными аргументами, соответствующими таблице базы данных
- Общее свойство Connection
Так же у нас есть возможность создать дополнительные запросы к базе данных. Для этого нам понадобиться инструмент Search Criteria Builder. Для его запуска необходимо у нашего адаптера выбрать пункт меню «Add Query» :
Вводим название запроса (а фактически название нового метода в вашем TableAdapte-е), а так же его текст либо вручную указав условие выборки либо используя инструмент Query Builder:
При записи условия выборки используйте позиционные параметры (символ «?»). Код метода будет сгенерирован автоматически, так что особого неудобства это не доставит.
После всех операций будет сгенерирован соответствующий метод. Так же дизайнер VS .Net 2005 добавит компонент ToolStrip к вашей форме с кнопкой запуска этого метода и полем для задания фильтра. Мне кажется это излишним, но может быть кому-нибудь понравиться.
Visual Studio .Net 2005 разделяет код, используемый дизайнером и пользовательский код за счет partial классов. В нормальном приложении нам наверняка понадобиться расширить логику TableAdapter. Сделать это можно, описав partial class, соответствующий классу конкретного TableAdapter-a, сгенерированного дизайнером.
Передача изменений в базу данных через TableAdapter
Отдельно необходимо остановиться на методах передачи изменений обратно в базу данных. VS .Net 2005 умеет генерировать код запросов для методов insert, update, delete. В некоторых случая этого может оказаться достаточно, но, как показывают опыт, полученные SQL выражения пытаются претендовать на универсальность и поэтому не оптимальны, а порой не работоспособны.
Приведу пример: В поставке с Firebird идет база данных employee.fdb. В ней есть таблица SALES. Прежде всего, обращаю внимание на поле AGED, которое доступно только для чтения, т.к. вычисляется с помощью выражения
COMPUTED BY (ship_date — order_date)
|
Если указать для Select Command текст:
SELECT * FROM SALES |
то колонка AGED будет добавлена во все команды обновления. При попытке передать изменения в базу будет сгенерировано исключение. Необходимо вручную отредактировать текст запросов для insert,update,delete команд и убрать из обновления данную колонку.
Отредактировать SQL выражения можно вызвав команду «Edit Queries in DataSet designer»:
Откроется дизайнер DataSet, в котором необходимо выбрать нужный TableAdapter (в данном случае это SALESTTableAdapter):
В списке свойств появятся необходимые нам объекты OleDbCommand:
Читатель может подумать, что данный случай исключение, но это не так. Если вы выберете такой способ создания слоя доступа к данным (Data Access Layer), то будьте готовы постоянно вмешиваться в автоматический процесс генерации SQL запросов.
BindingSource
С выходом Net 2.0 технология Data Binding получила свое дальнейшее развитие. Появился новый класс BindingSource. Он является прокси-объектом между поставщиками данных и элементами управления, отображающими эти данные. Теперь элемент управления привязывается не к объектам, поставляющим данные (DataTable, DataSet, DataView), а к объекту BindingSource. Это позволяет использовать привязку ещё незагруженных данных, а так же синхронизировать данные в случае использования общего BindingSource для нескольких элементов управления.
Продолжая построение нашего приложения, приведу, на мой взгляд, яркий пример, который демонстрирует все преимущества объекта BindingSource.
Добавим на форму новый элемент управления DataGridView. Давайте попробуем отобразить в нем список проектов, у которых текущий служащий, выбранный в ComboBox-e, был руководителем. Известно, что таблицы PROJECT и EMPLOYEE связаны между собой, через поле TEAM_LEADER по внешнему ключу INTEG_36. Воспользуемся уже знакомым механизмом связи элемента управления и источника данных:
Необходимо найти в списке возможных источников данных, уже использованный мной ранее BindingSource для таблицы служащих EMPLOYEE и запросить дизайнер создать новый BindingSource для связи между проектами и служащими по внешнему ключу INTEG_36:
Добавим элемент TextBox для отображения описания проекта, которое хранится в BLOB поле PROJECT_DESCR таблицы PROJECT. Для того, чтобы связать его с текущим проектом в списке DataGridView необходимо установить Binding для свойства Text:
Добавление логики управления данными
Разобравшись, как отображать, связывать и редактировать данные, давайте завершим пример и научим приложение записывать сделанные изменения обратно в базу данных.
Единственным редактируемым полем является описание текущего проекта. Для записи изменений сделанных в нем воспользуемся методом TableAdapter.Update() для таблицы PROJECT. Добавим элемент Button на форму и в обработчике cсобытия Click напишем следующий код:
private void btnSaveChanges_Click(object sender, EventArgs e) { try { this.pROJECTTableAdapter.Update(this.jobDataSet.PROJECT); MessageBox.Show(«Save was successful»); } catch (Exception exception) { MessageBox.Show(exception.Message); } } |
Так же неплохо было бы иметь возможность откатить сделанные изменения. Добавим ещё одну кнопку и в обработчике события Click поместим код, который будет отменять все изменения в DataSet, произведенные с момента последнего сохранения:
private void btnUndoChanges_Click(object sender, EventArgs e) { this.jobDataSet.RejectChanges(); //требуется для обновления содержимого TextBox this.iNTEG36BindingSource.CurrencyManager.Refresh(); } |
В процессе написания кода передачи изменений в БД я столкнулся со следующей проблемой: при редактировании связанных данных через TextBox изменения не передавались в DataSet и метод DataSet.HasChanges() всегда возвращал false. Для решения этой проблемы необходимо в обработчик события TextBox.Validate нужно добавить следующий код:
private void textBox1_Validated(object sender, EventArgs e) { this.iNTEG36BindingSource.EndEdit(); } |
Завершенное приложение JobManager вы можете скачать в архиве с примерами к статье.