Интернет-Университет Информационных Технологий
   http://www.INTUIT.ru
Создание Windows-приложений на основе Visual C#
4. Лекция: Работа с данными: версия для печати и PDA
Что такое реляционная база данных? Элементы языка SQL. Агрегатные функции. Типы данных. Оператор сравнения like. Создание таблицы с помощью запросов. Команды изменения языка DML. Подключение к базе данных – технология ADO.NET. Модель объектов ADO.NET. Таблицы и поля (объекты DataTable и DataColumn). Объекты DataRelation. Строки (объект DataRow). DataAdapter. Объекты DBConnection и DBCommand. Использование визуальной среды для работы с ADO.NET. Server Explorer. Программирование объектов ADO.NET. CommandText. СonnectionString. Управление соединением. Объект Connection. Объект Command. Вывод связанных таблиц. Связывание элементов управления с данными. Перемещение по записям. Объект CurrencyManager. Изменение записей

Для работы с данной лекцией используйте  примеры.

Что такое реляционная база данных?

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

Реляционная база данных представляет собой множество взаимосвязанных таблиц, каждая из которых содержит информацию об объектах определенного типа. Каждая строка таблицы содержит данные об одном объекте (например, товаре, фирме, клиенте), а столбцы таблицы содержат различные характеристики этих объектов — атрибуты (например, наименования и цены товаров, адреса и телефоны фирм или клиентов). Строки таблицы называются записями; все записи имеют одинаковую структуру — они состоят из полей, в которых хранятся атрибуты объекта. Каждое поле записи содержит одну характеристику объекта и строго определенный тип данных (например, текстовая строка, число, дата). Все записи имеют одни и те же поля, только в них содержатся разные значения атрибутов.

Для работы с данными используются системы управления базами данных (СУБД). Основные функции СУБД — это определение данных (описание структуры баз данных), обработка данных и управление данными.

Любая СУБД позволяет выполнять следующие операции с данными:

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

Для выполнения этих операций используется механизм запросов. Результатом выполнения запросов является либо отобранное по определенным критериям множество записей, либо изменения в таблицах. Запросы к базе формируются на специально созданном для этого языке, который так и называется — язык структурированных запросов (SQL — Structured Query Language).

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

Рассмотрим более подробно концепцию реляционных баз данных. Предположим, что вам нужно создать таблицу, содержащую информацию о товарах и поставщиках. Каждая строка этой таблицы (называемая также записью) будет соответствовать определенному порядку: каждый столбец (называемый также полем) будет содержать значение для каждого типа данных – наименования, цены, названия поставщика, представляемого в каждой строке. Таблица будет выглядеть примерно так (рис. 4.1):

Простое занесение записей в таблицу

увеличить изображение
Рис. 4.1.  Простое занесение записей в таблицу

Это и есть основа реляционной базы данных. Конечно же, реальная таблица будет содержать гораздо большее число записей и столбцов. Однако реляционные базы почти никогда не состоят из одной таблицы. Создав несколько таблиц, связанных между собой, мы сможем выполнить более сложные и мощные операции с данными. Но зачем связывать между собой таблицы? Один поставщик может предлагать большое количество различных товаров. В результате в нашей таблице будет большое количество повторяющихся однотипных записей. Если вынесем информацию о поставщике в отдельную таблицу, то в результате получим две таблицы: в одной будет информация о товаре, в другой — информация о поставщике. Связать две получившиеся таблицы мы сможем, вводя уникальный код поставщика (ID), который будет присутствовать в обеих таблицах. Схематическая связь таблиц, отображаемая в СУБД Microsoft Access ( входящая в комплект Microsoft Office) (рис. 4.2):

Связь таблиц по полю "Код поставщика"

Рис. 4.2.  Связь таблиц по полю "Код поставщика"

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

Сами таблицы будут выглядеть так (рис. 4.3):

Связанные таблицы

увеличить изображение
Рис. 4.3.  Связанные таблицы

Нажимая на знак (+), мы видим таблицу с товарами. Пользоваться связанными таблицами стало несравненно удобней, но самое главное — мы добились отсутствия повторяющихся записей. На практике, при оперировании с большими таблицами это означает экономию места на жестком диске и значительное увеличение скорости. Последний параметр является особенно важным.

Введенный столбец "Код поставщика" содержит неповторяющиеся значения для различных поставщиков. Для таблицы "Поставщики" он является первичным ключом. Первичный ключ — это столбец или комбинация столбцов, которые уникально идентифицируют запись в таблице. Аналогично, для таблицы "Товары" первичным ключом будет столбец "Код продукта".

На прилагаемом диске находится документ Microsoft Access RBProduct (Code\Glava4\ RBProduct.mdb), который содержит рассматриваемые выше примеры.

Элементы языка SQL

SQL (обычно произносимая как "sequel") символизирует собой Структурированный Язык Запросов. Это язык, который дает нам возможность работать с данными в реляционных базах данных.

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

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

Стандарт SQL определяется ANSI (Американским Национальным Институтом Стандартов), а также ISO (Международной организацией по стандартизации). Однако большинство коммерческих программ баз данных расширяют SQL без уведомления ANSI, добавляя разные особенности в этот язык, которые, как они считают, будут полезны. Иногда они несколько нарушают стандарт языка, хотя хорошие идеи имеют тенденцию развиваться и вскоре становиться стандартами сами по себе в силу полезности своих качеств.

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

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

Microsoft Query Analyzer, входящий в комплект Microsoft SQL Server 2000 – отличный инструмент для изучения языка SQL. Если у вас установлен этот пакет, то можно приступать к работе. Запускаем Query Analyzer и в появившемся окне указываем следующие параметры (рис. 4.4):

Задание параметров подключения

Рис. 4.4.  Задание параметров подключения

Мы используем подключение к серверу, находящемуся на нашем компьютере, — поэтому указываем имя сервера (local) (Можно также использовать знак точки — (.)). В других случаях – для подключения по локальной сети, например, — используйте кнопку обзора для поиска нужного сервера. Установка галочки – "Запускать SQL сервер в случае его остановки" (Start SQL Server if it is stopped) — позволяет поддерживать бесперебойную связь с сервером. Параметр Connect using задает аутентификацию при подключении к серверу — при выбранном значении Windows authentification нет надобности вводить имя пользователя и пароль.

Если вы все сделали правильно, то появляется главное окно программы, содержащее чистый бланк для записи SQL-запросов. Добавим окно, в котором мы будем просматривать содержимое базы данных, – пункт меню Tools/Object Browser/(Show/hide) или клавиша F8. Нам также потребуется окно для отображения результатов запросов — Window/Show Results Pane (Ctrl+R). В результате получим следующее (рис. 4.5):

Главное окно программы

увеличить изображение
Рис. 4.5.  Главное окно программы

Первое, что мы видим в окне Object Browser, — имя компьютера и связанного с ним сервера. На рисунке это 7EA2B2F6068D473. Это имя формируется, когда мы устанавливаем операционную систему Windows. Далее располагается древовидная структура — содержимое пакета SQL Server 2000, состоящего из баз данных master, model, msdb, NorthwindCS и tempdb. В отличие от других баз данных, обеспечивающих работу самой программы, NorthwindCS является специальной учебной базой. С ней мы и будем работать. На панели инструментов из выпадающего списка выбираем базу, для подключения — NorthwindCS. Можно этого не делать, но тогда в окне бланка необходимо будет каждый раз указывать строку — use NorthwindCS;. Будем полагать в дальнейшем, что на панели инструментов определена база данных NorthwindCS;.

Итак, напишем первый запрос1) и нажмем клавишу F5 (пункт меню Query —>Execute):

select * from Customers;

В результате возвращаются все записи из таблицы Customers базы данных NorthwindCS. Для просмотра содержимого базы данных используем Object Browser, щелкая на знак (+) возле соответствующего объекта. Переключившись на вкладку NorthwindCS, видим сообщение, означающее, что была извлечена 91 запись:

(91 row(s) affected)

Главное окно программы принимает следующий вид (рис. 4.6):

Запрос извлек таблицу Customers

Рис. 4.6.  Запрос извлек таблицу Customers

Вы можете менять вид данных, отображаемых на панели результатов: пункт меню Query/Results in Text (Ctrl+T) — результат в виде текста, Results in Grid (Ctrl+D) — в виде таблицы (по умолчанию), Results to File (Ctrl+Shift+F) — cохранение результата в виде файла в собственном формате программы *.rpt.

Таблица Customers состоит из следующих полей (рис. 4.7):

Содержание таблицы Customers

Рис. 4.7.  Содержание таблицы Customers

Для извлечения не всей таблицы, а столбцов СustomerID и Address, напишем запрос:

select CustomerID, Address from Customers;

Результатом будет следующее (рис. 4.8):

Извлечение столбцов CustomerID и Address

Рис. 4.8.  Извлечение столбцов CustomerID и Address

Если мы сделаем ошибку и укажем поле, которого нет в таблице Customers, например AddressID, в окне результатов на вкладке Messages появится соответствующее предупреждение:

Server: Msg 207, Level 16, State 1, Line 3
Invalid column name 'AddressID'.

Для вывода определенного количества записей используем запрос (рис. 4.9):

select top 5 CustomerID from Customers;

Извлечение нескольких записей

Рис. 4.9.  Извлечение нескольких записей

Извлекаются первые пять записей поля CustomerID, расположенные в самой таблице Customers в алфавитном порядке, — этот запрос не производит сортировки!

Вводя оператор percent, получаем указанный процент записей от общего числа:

select top  5 percent  CustomerID  from Customers;

В данном случае результат будет в точности таким же, как и при использовании запроса без оператора percent. В чем же дело? Общее число записей поля CustomerID таблицы Customers — 91 (В этом нетрудно убедиться: введя запрос select * from Customers; и переключившись на вкладку NorthwindCS, увидим сообщение: (91 row(s) affected).) Простой подсчет показывает, что пять процентов от 91 равняется 4,55; Query Analyzer округляет это число до пяти и возвращает результат.

Для вывода записей, отвечающих заданному условию, используем оператор where:

select *  from Products where UnitPrice > 100;

Этот запрос возвращает все записи из таблицы Products, в которых Столбец (поле) UnitPrice имеет значение, большее 100 (рис. 4.10):

Отбор записей со всеми полями по заданному значению

Рис. 4.10.  Отбор записей со всеми полями по заданному значению

Можно группировать операторы так:

select ProductName,UnitPrice  from Products where UnitPrice > 100;

Здесь извлекаются поля ProductName и UnitPrice из таблицы Products, где поле UnitPrice > 100 (рис. 4.11):

Отбор записей с несколькими  полями по заданному значению

Рис. 4.11.  Отбор записей с несколькими полями по заданному значению

Оператор where поддерживает работу со знаками <, >, >=, <=.

Точную выборку осуществляет оператор in, в следующем примере извлекаются лишь те записи, в которых значения поля UnitPrice в точности равно либо 10, либо 15, либо 23 (рис. 4.12):

select ProductName,UnitPrice   from Products where UnitPrice in (10,15,23);

Отбор записей по точному совпадению значений поля UnitPrice

Рис. 4.12.  Отбор записей по точному совпадению значений поля UnitPrice

Выборка для значений, лежащих в указанном интервале, осуществляется оператором between первое _значение and второе_значение (рис. 4.13):

select ProductName,UnitPrice   from Products where UnitPrice between 10 and 13;

Отбор записей по значениям в указанном интервале

Рис. 4.13.  Отбор записей по значениям в указанном интервале

Агрегатные функции

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

  • count извлекает количество записей данного поля;
  • sum извлекает арифметическую сумму всех выбранных значений данного поля;
  • avg извлекает арифметическое среднее (усреднение) всех выбранных значений данного поля;
  • max извлекает наибольшее из всех выбранных значений данного поля;
  • min извлекает наименьшее из всех выбранных значений данного поля.

Для определения общего числа записей в таблице Products используем следующий запрос:

select count (*) from Products;

результатом которого будет (рис. 4.14):

Результат запроса с функцией count

Рис. 4.14.  Результат запроса с функцией count

Обратите внимание — на вкладке Messages возникает сообщение, что только одна запись извлечена:

(1 row(s) affected)

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

Для определения количества записей поля ProductName таблицы Products используем следующий запрос:

select count (ProductName) from Products;

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

Как быть со значениями полей, которые были незаполненными? Обращение к таким полям осуществляет оператор null. Величина null не означает, что в поле стоит число 0 (нуль) или пустая текстовая строка — " ". Как возникают значения полей null? Существует два способа образования таких значений:

  • Microsoft SQL Server 2000 автоматически подставляет значение null, если в значение поля не было введено никаких значений и если тип данных для этого поля не препятствует присвоению значения null (если поле не является обязательным для заполнения);
  • если пользователь явным образом вводит значение null (подробнее о создании таблиц см. далее).

Вы можете самостоятельно попробовать определить, есть ли в какой-либо таблице базы данных Northwind поля, имеющие значения:

select *( или название столбца(ов))  from название_таблицы 
where название столбца  is null;

Для обратной задачи используем запрос типа

select *( или название столбца(ов))  from название_таблицы 
where название столбца  is not  null;

Оператор count учитывает записи со значением поля null.

Синтаксис использования других операторов одинаков — следующие запросы извлекают сумму, арифметическое среднее, наибольшее и наименьшее значения поля UnitPrice таблицы Products:

select sum(UnitPrice) from Products;
select avg(UnitPrice) from Products;
select max(UnitPrice) from Products;
select min(UnitPrice) from Products;

Выполните самостоятельно эти запросы и просмотрите результаты.

Типы данных

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

Наиболее употребительными типами данных для хранения чисел являются integer (целое число) и decimal (десятичное число) (которые можно сокращать как int и dec соответственно). Разумеется, что тип "целое число" можно представить как десятичное число, которое не содержит никаких цифр справа от десятичной точки.

Тип для текста — char (или символ), который относится к строке текста. Поле типа char имеет заданную длину, определяемую максимальным числом символов, которые могут быть введены в это поле. Больше всего реализаций также имеют нестандартный тип, называемый varchar (переменное число символов), который является текстовой строкой, способной принимать любую длину до определенного реализацией максимума (обычно 254 символа). Значения character и varchar включаются в одиночные кавычки как "текст". Различие между char и varchar в том, что char должен резервировать достаточное количество памяти для максимальной длины строки, а varchar распределяет память так, как это необходимо.

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

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

Оператор сравнения like

Оператор сравнения like для поиска записей по заданному шаблону. Это одна из наиболее часто встречаемых задач – например, поиск клиента с известной фамилией в базе данных.

Предположим, что в таблице Customers требуется найти записи клиентов с фамилиями, начинающимися на букву "C" и содержащие поля CustomerID, ContactName и Address:

select CustomerID, ContactName, 
Address from Customers where ContactName like 'C%';

Результатом этого запроса будет таблица (рис. 4.15):

Запрос с оператором like

Рис. 4.15.  Запрос с оператором like

Оператор like содержит шаблоны, позволяющие получать различные результаты (таблица 4.1).

Таблица 4.1. Шаблоны оператора like
ШаблонЗначение
like '5[%]'5%
like '[_]n'_n
like ‘[a-cdf]’a, b, c, d, или f
like '[-acdf]'-, a, c, d, или f
like '[ [ ]'[
like ']']
like 'abc[_]d%'abc_d и abc_de
like 'abc[def]'abcd, abce, и abcf

Создание таблицы с помощью запросов

Вплоть до этого места мы выполняли команды по извлечению таблиц и данных из таблиц, полагая, что сами таблицы были созданы кем-то до нас. Это, действительно, наиболее реальная ситуация, когда небольшое количество людей создают таблицы, которые затем используются другими людьми. Тем не менее особая область SQL, называемая DDL (Язык Определения Данных), специально работает с созданием объектов данных.

Таблицы создаются командой create table. Эта команда создает пустую таблицу — таблицу без строк. Команда create table в основном определяет имя таблицы в виде описания набора имен столбцов, указанных в определенном порядке. Она также определяет типы данных и размеры столбцов. Каждая таблица должна иметь по крайней мере один столбец.

Синтаксис команды create table:2)

create table ClientInfo
(
FirstName varchar(20),
LastName varchar(20),
Address varchar(20),
Phone varchar(15)
);

После выполнения этого запроса в окне Messages появляется сообщение:

The command(s) completed successfully.

Перезапустите Query Analyzer. В базе данных NorthwindCS появилась созданная нами таблица (рис. 4.16):

Созданная таблица находится в базе NorthwindCS

Рис. 4.16.  Созданная таблица находится в базе NorthwindCS

Итак, мы создали таблицу, состоящую из четырех полей, типа varchar, причем для трех полей было зарезервировано 20 байт, а для одного — 15. Значение полей на заполнены — на это указывает величина Null.

Вы можете удалить созданную таблицу непосредственно в интерфейсе Query Analyzer, щелкнув правой кнопкой и выбрав Delete.

Команды изменения языка DML

Значения могут быть помещены и удалены из полей, тремя командами языка DML (Язык Манипулирования Данными):

  • insert (вставить);
  • update (изменить);
  • delete (удалить).

Команда insert имеет свои особенности:

  • При указании значений конкретных полей вместо использования каких-либо значений можно применять ключевое слово DEFAULT.
  • Вставка пустой строки приводит к добавлению пробела ' ', а не значения NULL.
  • Строки и даты задаются в апострофах.
  • Не задавайте данные для столбца, имеющего свойство IDENTITY.
  • Можно задать NULL явно, можно задать DEFAULT.

Примеры:

insert into ClientInfo
(FirstName, LastName, Address, Phone)
values('Petr','Petrov','Chehova 13','1234567');

Однократное выполнение этого запроса (нажатие клавиши F5 один раз) приводит к добавлению одной записи. Добавляем еще несколько записей, изменяя значения value:

insert into ClientInfo
(FirstName, LastName, Address, Phone)
values('Ivan',Ivanov,'Naberejnaya 13','1234568');

insert into ClientInfo
(FirstName, LastName, Address, Phone)
values(null,'Sidorov','Naberejnaya 25','1234569');

Извлечем все записи созданной таблицы (рис. 4.17):

select * from ClientInfo;

Все записи таблицы ClientInfo

Рис. 4.17.  Все записи таблицы ClientInfo

Убедимся в том, что третья запись поля FirstName действительно содержит неопределенное значение null (а не строку NULL), c помощью следующего запроса (рис. 4.18):

select * from ClientInfo where FirstName is null;

Таблица ClientInfo действительно содержит запись со значением поля First Name "NULL"

Рис. 4.18.  Таблица ClientInfo действительно содержит запись со значением поля First Name "NULL"

Команда update позволяет изменять заданные значения записей:

update ClientInfo set  FirstName  = 'Andrey' where FirstName  = 'Petr';

В этом случае в первой записи поля FirstName значение Petr изменится на Andrey (рис. 4.19):

Изменение одной записи

Рис. 4.19.  Изменение одной записи

Если не указывать значение, которое необходимо изменить, команда update затронет все записи (рис. 4.20).

update ClientInfo set  FirstName  = 'Andrey';

Изменение всех  записей

Рис. 4.20.  Изменение всех записей

Команда delete позволяет изменять заданные значения записей:

delete from ClientInfo where LastName like 'Petrov';

Результатом этого запроса будет удаление первой записи из таблицы ClientInfo.

delete from ClientInfo;

Этот запрос удаляет все записи из таблицы, но не удаляет саму таблицу (рис. 4.21):

Все записи удалены, но сама таблица осталась!

Рис. 4.21.  Все записи удалены, но сама таблица осталась!

Запросы с командами insert, update и delete могут содержать в себе все прочие конструкции языка SQL.

Подключение к базе данных — технология ADO.NET

ADO.NET — это набор библиотек, входящий в состав Microsoft .NET Framework и предназначенный для взаимодействия с различными базами данных из .NET-приложений. Библиотеки ADO.NET включают классы для подсоединения к источнику данных, выполнения запросов и обработки их результатов. ADO.NET также можно использовать в качестве надежного, иерархически организованного, отсоединенного кэша данных для автономной работы с данными.

Мастер Data Form Wizard из Visual Studio .NET позволяет быстро, буквально в несколько шагов, создать связанную с данными форму. Код, генерируемый мастером, можно будет просматривать и изменять.

Мы рассмотрим подключение к базе данных Microsoft Access RBProduct, которую мы уже рассматривали выше. Наша задача: вывести содержимое двух связанных таблиц — "Поставщики" и "Товары" — на Windows-форму.

Запустите Visual Studio .NET, создайте новый проект, тип проекта — Windows Application. Назовите его DataWizardMDB.

В окне Solution Explorer щелкаем правой кнопкой мыши на имени проекта — DataWizardMDB — и в появившемся меню выбираем Add/Add New Item. В появившемся окне выбираем мастер Data Form Wizard, как показано на рис. 4.22.

Создание Data Form Wizard

Рис. 4.22.  Создание Data Form Wizard

Называем новую форму DataFormMDB.

В появившемся мастере нажимаем кнопку Next. В следующем шаге определяется объект DataSet, название которого должно соответствовать содержимому, поэтому называем его dsProviders. В следующем шаге Мастера —Choose a data connection — требуется создать подключение к базе данных. Поскольку у нас еще нет никакого подключения, нажимаем на кнопку New Connection. В появившемся окне "Свойства связи с данными" необходимо определить параметры создаваемого подключения. Переключаемся на вкладку "Поставщик данных" для выбора нужного поставщика OLE DB (рис. 4.23).

Определение поставщика данных

Рис. 4.23.  Определение поставщика данных

Поставщик данных Microsoft Jet 4.0 OLE DB Provider позволяет подключаться к базам данных формата Microsoft Aceess (mdb). Нажимаем кнопку "Далее" (или переходим на вкладку "Подключение") (рис. 4.24).

Задание параметров подключения

Рис. 4.24.  Задание параметров подключения

В пункте 1 нажимаем кнопку (…) и указываем путь к базе данных RBProduct.mdb, расположенной на вашем компьютере. В пункте 2 можно оставить значение по умолчанию — если вы специально не изменяли имя пользователя и пароль доступа к базе, то стандартное имя Admin позволит создать подключение. Если вы правильно настроили права доступа к базе данных, то при нажатии кнопки "Проверить подключение" система выдаст сообщение "Проверка подключения выполнена". На вкладках "Дополнительно" и "Все" можно устанавливать права доступа и просматривать все свойства формируемого подключения. Не изменяя ничего на этих вкладках, нажимаем OK.

Далее нам предстоит определить, какую таблицу базы данных мы собираемся извлекать. В окне мастера перечислены все доступные в схеме базы данных таблицы, представления и хранимые процедуры. Выберите в списке Available Items нужные таблицы и переместите их в список Selected Items, щелкнув кнопку с направленной вправо стрелкой. Если вы ошиблись и хотите удалить какие-то таблицы из списка Selected Items, выберите их и щелкните кнопку со стрелкой влево. Кроме того, добавлять и удалять таблицы можно, дважды щелкнув их название мышью. Выбираем две таблицы: "Поставщики" и "Товары" (рис. 4.25).

Выбор таблиц, которые будут отображаться на форме

Рис. 4.25.  Выбор таблиц, которые будут отображаться на форме

Теперь необходимо определить отношения между таблицами. Отношения позволяют обеспечивать соблюдение правил ссылочной целостности, каскадно передавая изменения от одной таблицы к другой. Кроме того, они упрощают поиск данных в таблицах. Название отношения рекомендуется составлять из имен родительской и дочерней таблиц (именно в таком порядке). В качестве имени выберем ProvidersProducts. Далее определяем родительскую таблицу (Parent table) — "Поставщики" и дочернюю (Child table) – "Товары". Поле "Код поставщика" определяет связь между этими таблицами. Щелкните кнопку со стрелкой вправо, чтобы добавить отношение в список Relations, и затем щелкните Next.

Далее определяем столбцы (поля), извлекаемые из обеих таблиц. Оставляем все поля (рис. 4.26).

Задание связей между таблицами

Рис. 4.26.  Задание связей между таблицами

В последнем шаге мастера предстоит определить вид размещения данных на форме — всех записей в виде таблицы (All records in a grid) либо каждой записи в отдельном текстовом поле (Single records in individual control). Выбираем второй вариант. Здесь же можно определить наличие дополнительных элементов управления — кнопок навигации и изменения записей. Оставляем эти значение по умолчанию. Завершаем работу мастера, нажимая кнопку Finish.

Возникает сообщение: "Пароль будет сохранен в виде текста и будет доступен для чтения в коде и сборке". Выбрав Include password, вы включаете пароль в приложение (он будет доступен), но избавляетесь от необходимости введения имени пользователя и пароля каждый раз при подключении к базе данных (рис. 4.27).

Выбор расположения пароля

Рис. 4.27.  Выбор расположения пароля

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

[STAThread]
		static void Main() 
	{
		Application.Run(new Form1());
	}

В коде DataFormMDB после Windows Form Designer generated code вставляем код и изменяем название запускаемой формы:

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

В окне Solution Explorer щелкаем правой кнопкой на Form1.cs и удаляем его. Теперь, запуская приложение, мы видим DataFormMDB (рис. 4.28):

Готовая форма

Рис. 4.28.  Готовая форма

В созданном приложении имеется несколько кнопок. Для загрузки данных нажимаем кнопку Load. Для перемещения по записям используем навигационные кнопки. При этом во второй таблице отображается список продуктов, поставляемых данным поставщиком. Для добавления новой или удаления текущей записи служат кнопки Add и Delete, для отмены изменений текущей записи – Cancel. Все изменения буферизуются и могут быть отменены нажатием кнопки CancellAll. Для передачи изменений в базу данных нажимаем кнопку Update — тогда записи изменяются в самом файле RBProduct.mdb.

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

Модель объектов ADO.NET

Модель объектов ADO.NET состоит из набора классов. DataSet представляет собой класс ADO.NET, который отвечает за отображение таблицы (или таблиц, или даже всей базы) используемой базы данных на компьютере пользователя без непрерывной связи с базой данных. В приложении, созданном с помощью Data Form Wizard, мы имели возможность вносить изменения в загруженную таблицу, перемещаться по записям, причем до нажатия кнопки Update изменения в самой базе данных, т.е. в файле RBProduct.mdb, не происходили. В чем же дело? Дело в том, что все данные и были загружены в объект DataSet, созданный мастером. Только при нажатии кнопки Update происходила передача данных из DataSet в саму базу данных. Не следует путать объект DataSet c оперативной памятью компьютера – загруженная таблица не находится в оперативной памяти компьютера.

Представьте себе, что вы обслуживаете гипотетическую доску объявлений. Каждое утро вы берете обычный бумажный блокнот, записываете в него все свежие объявления, хранящиеся в централизованном банке данных, и отправляетесь к доске. Вы переписываете с блокнота на доску все объявления, а в блокнот записываете все те объявления, которые были добавлены на доску посетителями. Затем вы возвращаетесь в банк данных и вносите в него информацию, которую записали в блокнот c доски. Ваш блокнот и будет являться экземпляром класса DataSet. В этом процессе самым важным — для вас конечно, не для централизованного банка данных, — будет ваш блокнот. Аналогично, класс DataSet является ключевым во всей модели классов ADO.NET.

DataSet состоит из объектов типа DataTable и объектов DataRelation. В коде к ним можно обращаться как к свойствам объекта DataSet, т. е. используя точечную нотацию. Свойство Tables возвращает объект типа DataTableCollection, который содержит все объекты DataTable используемой базы данных.

Таблицы и поля (объекты DataTable и DataColumn)

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

DataColumn — это блок для создания схемы DataTable. Каждый объект DataColumn имеет свойство DataType, которое определяет тип данных, содержащихся в каждом объекте DataColumn. Например, вы можете ограничить тип данных до целых, строковых и десятичных чисел. Поскольку данные, содержащиеся в DataTable, обычно переносятся обратно в исходный источник данных, вы должны согласовывать тип данных с источником.

Объекты DataRelation

Объект DataSet имеет также свойство Relations, возвращающее коллекцию DataRelationCollection, которая, в свою очередь, состоит из объектов DataRelation. Каждый объект DataRelation выражает отношение между двумя таблицами (сами таблицы связаны по какому-либо полю (столбцу); следовательно, эта связь осуществляется через объект DataColumn).

Строки (объект DataRow)

Коллекция Rows объекта DataTable возвращает набор строк (записей) заданной таблицы. Эта коллекция используется для изучения результатов запроса к базе данных. Мы можем обращаться к записям таблицы как к элементам простого массива.

DataAdapter

DataSet — это специализированный объект, содержащий образ базы данных. Для осуществления взаимодействия между DataSet и источником данных используется объект типа DataAdapter. Само название этого объекта — адаптер, преобразователь, — указывает на его природу. DataAdapter содержит метод Fill() для обновления данных из базы и заполнения DataSet.

Объекты DBConnection и DBCommand

Объект DBConnection осуществляет связь с источником данных. Эта связь может быть одновременно использована несколькими командными объектами. Объект DBCommand позволяет послать базе данных команду (как правило, команду SQL или хранимую процедуру). Объекты DBConnection и DBCommand иногда создаются неявно в момент создания объекта DataSet, но их также можно создавать явным образом.

Использование визуальной среды для работы с ADO.NET

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

Рассмотрим работу с базой данных Microsoft Acсess xtreme3), входящей в состав Microsoft Visual Studio .NET, и работу с базой Microsoft SQL4) NorthwindCS. В каждой базе есть таблица Customer(s)5). Наша задача – вывести содержимое двух таблиц Customer на две Windows- формы.

Microsoft Access, база данных xtremeMicrosoft SQL, база данных NorthwindCS

Запустите Visual Studio .NET, создайте новый проект, тип проекта — Windows Application.

Назовите его VisualDataMDBНазовите его VisualDataSQLs

Размещаем на создавшейся форме элемент управления DataGrid, свойству Dock устанавливаем значение Fill. Теперь в окне ToolBox переходим на закладку Data (рис. 4.29).

На вкладке Data находятся все элементы управления для работы с ADO.NET

Рис. 4.29.  На вкладке Data находятся все элементы управления для работы с ADO.NET

Поместите на форму объект OleDbDataAdapterПоместите на форму объект SqlDataAdapter

Перед нами появляется Мастер настройки элемента управления. Для OleDbDataAdapter этот Мастер тот же самый, который был описан и изображен на рис. 4.23-4.24. Вернитесь к этим рисункам и сконфигурируйте эти два шага OleDbDataAdapter самостоятельно. Для SqlDataAdapter на вкладке "Поставщик данных" по умолчанию выбран необходимый поставщик (рис. 4.30).

Поставщик данных Microsoft OLE DB Provider for SQL Server

Рис. 4.30.  Поставщик данных Microsoft OLE DB Provider for SQL Server

Нажимаем кнопку "Далее" (или переходим на вкладку "Подключение") (рис. 4.31).

Определение параметров подключения

Рис. 4.31.  Определение параметров подключения

Для подключения к базе данных SQL, расположенной на вашем компьютере, в пункте 1 вводим (local) или просто точку — ".", в пункте 2 используем учетные сведения системы (по умолчанию пароль учетной записи администратора SQL Server – пустой). В пункте 3 выбираем базу данных NorthwindCS. В следующем шаге Мастера — определение типа запроса — параметры, предлагаемые Мастером, будут отличаться (рис. 4.32 и рис. 4.33):

Определение типа запросов объекта OleDbDataAdapter

Рис. 4.32.  Определение типа запросов объекта OleDbDataAdapter

Определение типа запросов объекта SqlDbDataAdapter

Рис. 4.33.  Определение типа запросов объекта SqlDbDataAdapter

Для базы данных Microsoft Access мы можем использовать только SQL-запросы: другие значения не активны. Для базы данных Microsoft SQL Server мы можем либо использовать SQL-запросы, либо создать новую хранимую процедуру (stored procedure), либо использовать существующую хранимую процедуру. Различия подобного рода определяются типом баз данных. Для объекта SqlDataAdapter оставляем значение, предложенное по умолчанию, — использование SQL-запросов.

В следующем шаге — создание SQL-запросов (Generate SQL statements) — мы можем вводить текст запроса непосредственно. Но удобнее всего воспользоваться "Построителем запросов": нажимаем на кнопку "Query Builder…" (рис. 4.34) (Поскольку окна добавления таблиц для обеих баз данных одинаковы — незначительно различается лишь содержимое — я привожу рисунок для OleDbDataAdapter).

Добавление таблицы в окно Построителя запросов

Рис. 4.34.  Добавление таблицы в окно Построителя запросов

Выбираем таблицу Customer (Customers), нажимаем кнопку Add и затем Close. Затем в окне Query Builder выбираем все столбцы (All Columns), как показано на рис. 4.35.

Добавление столбцов. Поставив галочку напротив пункта (All Columns), мы извлекаем все столбцы из таблицы

Рис. 4.35.  Добавление столбцов. Поставив галочку напротив пункта (All Columns), мы извлекаем все столбцы из таблицы

Нажимаем кнопку OK. В окне Generate the SQl statements, к которому мы вернулись, приводится SQL-запрос, созданный Мастером:

SELECT
    Customer.*
FROM
    Customer	SELECT
    Customers.*
FROM
    Customers

Обратите внимание, что синтаксис этих запросов несущественно отличается от "классического" SQL- запроса, рассмотренного выше.

Нажимаем кнопку Next. В окне "Обзор результатов мастера" (View Wizard Results) приводится список результатов работы Мастера. Вид этого списка может отличаться так, как это изображено на рис. 4.36 и рис. 4.37.

Список  результатов работы Мастера объекта OleDbDataAdapter

Рис. 4.36.  Список результатов работы Мастера объекта OleDbDataAdapter

Список  результатов работы Мастера объекта SqlDbDataAdapter

Рис. 4.37.  Список результатов работы Мастера объекта SqlDbDataAdapter

Чем обусловлено это различие? Для базы данных Access мастером создана возможность не только просмотра данных из базы данных — SQL-запрос SELECT, но и возможность ее изменения — запросы INSERT, UPDATE, DELETE. Для базы данных SQL Server создана лишь возможность просмотра базы данных. Для изменения данных необходимо проходить авторизацию (рис. 4.31) с учетной записью администратора базы данных.

Нажимаем кнопку Finish. Для объекта OleDbDataAdapter возникает окно (рис. 4.27), которое мы уже описывали выше.

Мастер создал на панели компонент по два элемента, oleDbDataAdapter1 и oleDbConnection1 для VisualDataMDB и sqlDbDataAdapter1 и sqlDbConnection1 для VisualDataSQL. oleDbDataAdapter1 (или sqlDbDataAdapter1) является основным компонентом типа OleDbDataAdapter (или SqlDataAdapter), который мы создавали при помощи мастера. Объект oleDbConnection1 (или sqlDbConnection1) был создан мастером как необходимый элемент подключения к базе. Образно говоря, мы получили адаптер (DataAdapter1) и вилку (Connection1) для подключения к источнику данных. Теперь нам необходимо добавить объект DataSet — тот самый "буфер", в котором будет храниться информация, полученная из базы данных. Щелкаем на oleDbDataAdapter1 (или sqlDbDataAdapter1) для его выделения. Открываем свойства этого элемента. На информационной панели щелкаем на ссылку Generate DataSet для создания объекта DataSet6) (р ис. 4.38):

Создание DataSet

Рис. 4.38.  Создание DataSet

В появившемся окне Generate DataSet задаем имена — dsCustomer и dsCustomers соответственно. Итак, все "невидимые" части механизма подключения к базе данных у нас уже есть. Осталось теперь связать видимый интерфейс с данными. Объектом, отображающим данные на форме, у нас является dataGrid1. В свойстве этого элемента DataSource (источник данных) выбираем в качестве источника созданный объект dsCustomer1 (рис. 4.39).

Определение источника данных для элемента управления dataGrid1

Рис. 4.39.  Определение источника данных для элемента управления dataGrid1

На форме в режиме дизайна у нас немедленно появилась символическая таблица со знаком (+). Последнее, что нам осталось сделать, — это заполнить объект DataSet (здесь dsCustomer1). Переходим в код форм и вносим соответствующие добавления:

using System.Data.OleDb;
// Подключаем  пространство имен  для работы с поставщиком OleDb 

private void Form1_Load(object sender, System.EventArgs e)
	{
			oleDbDataAdapter1.Fill(dsCustomer1);
}
	using System.Data.SqlClient;
// Подключаем  пространство имен  для работы с поставщиком SQL

private void Form1_Load(object sender, System.EventArgs e)
	{
			sqlDataAdapter1.Fill(dsCustomers1);
            }

В обоих случаях в методе загрузки формы вызываем метод Fill объекта oleDbDataAdapter1 (или oleDbDataAdapter1) и передаем ему в качестве параметра dsCustomer1. Опять же, говоря образно, мы "заливаем" данные из адаптера в DataSet, который затем распоряжается ими, — здесь мы определили, что DataSet выступает в качестве источника данных для dataGrid1.

Запустите приложения. Щелкните на знак (+), а затем на ссылку Customer. Готовые приложения будут иметь следующий вид (рис. 4.40 и рис. 4.41):

Готовое приложение VisualDataMDB

Рис. 4.40.  Готовое приложение VisualDataMDB

Готовое приложение VisualDataSQL

Рис. 4.41.  Готовое приложение VisualDataSQL

На диске, прилагаемом к книге, вы найдете эти приложения (Code\Glava4\VisualDataMDB и Code\Glava4\VisualDataSQL).

Server Explorer

В состав Visual Studio .NET входит замечательный инструмент управления и обзора подключениями к базам данных — Server Explorer. С его помощью можно практически мгновенно создавать приложения, использующие базы данных. Создайте новый проект. Назовите его Server_Explorer. Выберите в меню View пункт Server Explorer (или воспользуйтесь сочетанием клавиш Ctrl+Alt+S). Появится окно Server Explorer. Щелкаем на Data Connections правой кнопкой мыши и выбираем пункт Add Connection (рис. 4.42).

Добавление соединения

Рис. 4.42.  Добавление соединения

Появляется окно Мастера "Свойства связи с данными", с которым мы уже сталкивались (см. рис. 4.30 и рис. 4.31). Создайте самостоятельно подключение к базе данных xtreme — после того как вы это сделаете, оно появится в списке всех подключений окна Server Explorer (рис. 4.43).

В окне Server Explorer выводятся все созданные подключения к базам данных

увеличить изображение
Рис. 4.43.  В окне Server Explorer выводятся все созданные подключения к базам данных

Щелкните на знак (+) около названия подключения, откройте Tables, выберите таблицу Customer и перетащите ее на форму (рис. 4.44).

Содержимое базы данных xtreme

Рис. 4.44.  Содержимое базы данных xtreme

При появлении уже знакомого окна расположения пароля (см. рис. 4.27) выбираем Include Password.

На панели компонент формы появились два элемента: oleDbConnection1 и oleDbDataAdapter1. Выделяем щелчком oleDbDataAdapter1, открываем его свойства, на информационной панели нажимаем Generate DataSet. Называем объект DataSet dsCustomer и нажимаем ОК. Помещаем на форме элемент управления DataGrid, в свойствах этого элемента указываем расположение Dock — Fill, DataSource — dsCustomer, и переходим в код формы. Добавляем уже знакомый нам код:

private void Form1_Load(object sender, System.EventArgs e)
{
	oleDbDataAdapter1.Fill(dsCustomer1);
}

Запускаем приложение. Его внешний вид ничем не отличается от приложения, рассмотренного в предыдущем примере.

Созданное подключение к базе данных xtreme теперь будет отображаться во всех последующих проектах в окне Server Explorer. Вам остается только выбирать нужную таблицу и перетаскивать ее на форму.

Вы также можете использовать окно Solution Explorer для быстрого просмотра содержимого баз данных и — если подключение было создано с правами администратора — изменять их. Откройте нужную таблицу и просто дважды щелкните на ней (рис. 4.45):

Просмотр таблицы Customers базы данных NorthwindCS

увеличить изображение
Рис. 4.45.  Просмотр таблицы Customers базы данных NorthwindCS

Программирование объектов ADO.NET

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

Будет работать с теми же самыми таблицами.

Microsoft Access, база данных xtreme Таблица CustomerMicrosoft SQL, база данных NorthwindCS Таблица Customers

Запустите Visual Studio .NET, создайте новый проект, тип проекта — Windows Application.

Назовите его ProgrammDataMDBНазовите его ProgrammDataSQL

Размещаем на создавшейся форме элемент управления DataGrid, свойству Dock устанавливаем значение "Fill". Переходим в код формы. Подключаем соответствующие пространства имен:

using System.Data.OleDb;
using System.Data.SqlClient;

В конструкторе формы после InitializeComponent создаем объект DataAdapter:

public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
OleDbDataAdapter dataAdapter = new OleDbDataAdapter(CommandText, ConnectionString);
Листинг 4.1.
}	public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
SqlDataAdapter dataAdapter = new SqlDataAdapter(CommandText, ConnectionString);
}

В качестве параметров DataAdapter мы передаем CommandText и ConnectionString. Переменная типа string CommandText представляет собой обычный SQL-запрос на выборку из таблицы Customer, а переменная типа СonnectionString — это так называемая строка подключения, в которой указываются расположение базы данных, ее название, параметры авторизации и проч. Далее мы рассмотрим более подробно эти строки. Как составить эти переменные? Можно, конечно, написать вручную, но мы сейчас воспользуемся строчками, сгенерированными мастером. Откройте предыдущие проекты — VisualDataMDB и VisualDataSQL. Перейдите в код формы. Раскройте область Windows Form Designer generated code, щелкнув на знаке (+). Найдите следующие строчки:

Для CommandText:

this.oleDbSelectCommand1.CommandText = @"SELECT Address1, Address2, City, 
[Contact First Name], [Contact Last Name], [Contact Position], [Contact Title], 
Country, [Customer Credit ID], [Customer ID], 
[Customer Name], [E-mail], Fax, [Last Year's Sales], Phone, [Postal Code], Region,
 [Web Site] FROM Customer";
Листинг 4.2.
this.sqlSelectCommand1.CommandText = "SELECT CustomerID, CompanyName, ContactName, 
ContactTitle, Address, City, Region, PostalCode, Country, Phone, Fax FROM Customers";
Листинг 4.3.

Для :ConnectionString7)

this.oleDbConnection1.ConnectionString = @"Jet OLEDB:Global Partial Bulk Ops=2;Jet OLEDB:Registry Path=;
Jet OLEDB:Database Locking Mode=1;Jet OLEDB:Database Password=;Data Source=""E:\Program
Files\Microsoft Visual Studio .NET 2003\Crystal Reports\Samples\Database\xtreme.mdb"";Password=;
Jet OLEDB:Engine Type=5;Jet OLEDB:Global Bulk 
Transactions=1;Provider=""Microsoft.Jet.OLEDB.4.0"";Jet OLEDB:System database=;Jet 
OLEDB:SFP=False;Extended Properties=;Mode=Share Deny None;Jet OLEDB:New Database Password=;Jet 
OLEDB:Create System Database=False;Jet OLEDB:Don't Copy Locale on Compact=False;Jet OLEDB:Compact Without 
Replica Repair=False;User ID=Admin;Jet OLEDB:Encrypt Database=False";
Листинг 4.4.
 
this.sqlConnection1.ConnectionString = "workstation id=7EA2B2F6068D473;packet size=4096;integrated 
security=SSPI;data source=\"(local)\";persist security info=False;initial catalog=NorthwindCS";
Листинг 4.5.

Скопируйте эти строчки, начиная от названия самих переменных, затем в коде форм ProgrammDataMDB и ProgrammDataSQL в классе Form 1 объявите две переменные по две переменных типа string и вставьте скопированные значения:

string CommandText = @"SELECT Address1, Address2, City, [Contact First Name], [Contact Last Name], [Contact 
Position], [Contact Title], Country, [Customer Credit ID], [Customer ID], [Customer Name], [E-mail], Fax, [Last Year's 
Sales], Phone, [Postal Code], Region, [Web Site] FROM Customer";
string ConnectionString = @"Jet OLEDB:Global Partial Bulk Ops=2;Jet OLEDB:Registry Path=;Jet OLEDB:Database 
Locking Mode=1;Jet OLEDB:Database Password=;Data Source=""E:\Program Files\Microsoft Visual Studio 
.NET 2003\Crystal Reports\Samples\Database\xtreme.mdb"";Password=;Jet OLEDB:Engine Type=5;Jet 
OLEDB:Global Bulk Transactions=1;Provider=""Microsoft.Jet.OLEDB.4.0"";Jet OLEDB:System 
database=;Jet OLEDB:SFP=False;Extended Properties=;Mode=Share Deny None;Jet OLEDB:New Database 
Password=;Jet OLEDB:Create System Database=False;Jet OLEDB:Don't Copy Locale on Compact=False;Jet 
OLEDB:Compact Without Replica Repair=False;User ID=Admin;Jet OLEDB:Encrypt Database=False";
string CommandText = "SELECT CustomerID, CompanyName, ContactName, ContactTitle, Address, City, Region, 
PostalCode, Country, Phone, Fax FROM Customers";
Листинг 4.6.
string ConnectionString = "workstation id=7EA2B2F6068D473;packet size=4096;integrated security=SSPI;data sou" +
"rce=\"(local)\";persist security info=False;initial catalog=NorthwindCS";
Листинг 4.7.

Обратите внимание на названия переменных CommandText и ConnectionString. Когда мы создаем объект DataAdapter, в качестве параметров мы можем передать названия строк, таких как cmdText и conString, или даже cmt и cns, — совершенно равноправно, не забыв, конечно же, назвать также эти переменные в классе Form1. Но сама среда VisualStudio.NET генерирует эти строки именно с такими названиями — CommandText и ConnectionString, поэтому если вы пишите их не вручную, то облегчаете работу, называя их так же, как и среда.

Возвращаемся к нашим приложениям — ProgrammDataMDB и ProgrammDataSQL. Дальнейший код будет совершенно одинаковым для обоих приложений.

Итак, создаем объект DataSet:

DataSet ds = new DataSet();

Заполняем таблицу Customer объекта ds данными из базы:

dataAdapter.Fill(ds, "Customer");

Cвязываем источник данных объекта dataGrid1 ( который мы нанесли на форму в режиме дизайна) с таблицей Customer объекта ds:

dataGrid1.DataSource = ds.Tables["Customer"].DefaultView;

Все! Запускаем оба приложения. Если вы были внимательны в самом начале, то заметили, что в базе данных xtreme таблица называется Customer, а в базе данных NorthwindCS — Customers (s на конце). Тем не менее код работает для обоих приложений. В чем же дело? Таблица, которую мы называем Customer, при вызове метода Fill объекта dataAdapter может быть названа как угодно — ее содержимое будет представлять собой извлекаемую из базы данных таблицу. При указании источника данных (DataSource) для объекта dataGrid1 мы ссылаемся именно на таблицу Customer, которая была создана при вызове метода Fill. Точнее говоря, эта таблица определена в объекте DataSet, а именно к нему мы и обращаемся для заполнения данными нашей формы в соответствии с моделью ADO.NET. Чтобы убедиться в этом, попробуйте такой код:

DataSet ds = new DataSet();
		dataAdapter.Fill(ds, "Bezimyannaya");
	dataGrid1.DataSource = ds.Tables["Bezimyannaya"].DefaultView;

По-прежнему все работает. Означает ли это, что можно небрежно относится к названию таблицы в DataSet? Нет — в нашем учебном примере мы с самого начала извлекали только одну таблицу из базы данных. В реальных приложениях приходится иметь дело с десятками таблиц, и будет возникать большая путаница, если называть таблицы как попало. Поэтому следует давать названия таблицам в объекте DataSet те же названия, образы которых они представляют.

Полный8) листинг программы — подключение к базе данных xtreme:

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Data.OleDb;

namespace ProgrammDataMDB
{
	/// <summary>
	/// Summary description for Form1.
	/// </summary>
	public class Form1 : System.Windows.Forms.Form
	{
		private System.Windows.Forms.DataGrid dataGrid1;
		/// <summary>
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.Container components = null;
		string CommandText = @"SELECT Address1, Address2, City, [Contact First Name], [Contact Last Name], [Contact Position], 
		 	[Contact Title], Country, [Customer Credit ID], [Customer ID], [Customer Name], [E-mail], Fax, [Last Year's Sales], 
			Phone, [Postal Code], Region, [Web Site] FROM Customer";
		string ConnectionString = @"Jet OLEDB:Global Partial Bulk Ops=2;Jet OLEDB:Registry Path=;Jet OLEDB:Database Locking Mode=1;
			Jet OLEDB:Database Password=;Data Source="
			"E:\Program Files\Microsoft Visual Studio .NET 2003\Crystal Reports\Samples\Database\xtreme.mdb"
			";Password=;Jet OLEDB:Engine Type=5;Jet OLEDB:Global Bulk Transactions=1;Provider=""Microsoft.Jet.OLEDB.4.0"
			";Jet OLEDB:System database=;Jet OLEDB:SFP=False;Extended Properties=;Mode=Share Deny None;Jet OLEDB:New Database Password=;
			Jet OLEDB:Create System Database=False;Jet OLEDB:Don't Copy Locale on Compact=False;Jet OLEDB:Compact Without Replica Repair=False;
			User ID=Admin;Jet OLEDB:Encrypt Database=False";
			
		public Form1()
		{
			
			
			//
			// Required for Windows Form Designer support
			//
			InitializeComponent();

			OleDbDataAdapter dataAdapter = new OleDbDataAdapter(CommandText, ConnectionString);
			DataSet ds = new DataSet();
			dataAdapter.Fill(ds, "Bezimyannaya");
			dataGrid1.DataSource = ds.Tables["Bezimyannaya"].DefaultView;
			//
			// TODO: Add any constructor code after InitializeComponent call
			//
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if (components != null) 
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		Windows Form Designer generated code
		

		/// <summary>
		/// The main entry point for the application.
		/// </summary>
		[STAThread]
		static void Main() 
		{
			Application.Run(new Form1());
		}

		private void Form1_Load(object sender, System.EventArgs e)
		{
			
		}
	}
}
Листинг 4.8.

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

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Data.SqlClient;

namespace ProgrammDataSQL
{
	/// <summary>
	/// Summary description for Form1.
	/// </summary>
	public class Form1 : System.Windows.Forms.Form
	{
		private System.Windows.Forms.DataGrid dataGrid1;
		/// <summary>
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.Container components = null;
		string CommandText = "SELECT CustomerID, CompanyName, ContactName, ContactTitle, Address, City, Region," +
			" PostalCode, Country, Phone, Fax FROM Customers";
		string ConnectionString = "workstation id=7EA2B2F6068D473;packet size=4096;integrated security=SSPI;data sou" +
			"rce=\"(local)\";persist security info=False;initial catalog=NorthwindCS";
			
		public Form1()
		{
			//
			// Required for Windows Form Designer support
			//
			InitializeComponent();
			SqlDataAdapter dataAdapter = new SqlDataAdapter(CommandText, ConnectionString);
			DataSet ds = new DataSet();
			dataAdapter.Fill(ds, "Customer");
			dataGrid1.DataSource = ds.Tables["Customer"].DefaultView;

			//
			// TODO: Add any constructor code after InitializeComponent call
			//
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if (components != null) 
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		Windows Form Designer generated code
		

		/// <summary>
		/// The main entry point for the application.
		/// </summary>
		[STAThread]
		static void Main() 
		{
			Application.Run(new Form1());
		}

		private void Form1_Load(object sender, System.EventArgs e)
		{
		
		}
	}
}
Листинг 4.9.

CommandText

Для извлечения таблиц и содержащихся в них данных используются SQL-запросы. Переменная CommandText содержит в себе SQL-запрос, синтаксис которого адаптирован для данного поставщика данных. Мы можем управлять извлечением данных, изменяя строку CommandText. Скопируйте папку проекта ProgrammDataSQL и назовите ее CommandText. Запустите проект и перейдите в код формы. Изменим string СommandText так, чтобы извлекать в DataGrid только поля CustomerID, ContactName, Country и Phone. Для этого удалим ненужные поля. В результате получится следующий SQL-запрос:

string CommandText = "SELECT CustomerID, ContactName, Country, Phone  FROM Customers";

Запустите приложение. Теперь на форму выводятся только соответствующие четыре поля (рис. 4.46).

Ограничение выводимых полей

Рис. 4.46.  Ограничение выводимых полей

Выведем теперь все записи клиентов, имена которых начинаются на "М":

string CommandText = "SELECT CustomerID, ContactName, Country, 
Phone  FROM Customers where ContactName like 'M%'";

Запускаем приложение. Запрос выбрал только записи на букву "M" (рис. 4.47).

Ограничение выводимых полей

Рис. 4.47.  Ограничение выводимых полей

Вы можете использовать все возможности языка SQL для отбора данных и модификации строки CommandText для получения нужного результата. Не пробуйте только использовать команды insert, update или delete — изменение записей в базе данных мы рассмотрим позже.

ConnectionString

Строка соединения ConnectionString определяет параметры, необходимые для установления соединения с источником данных. Строка соединений при использовании мастеров генерируется средой, но можно (и желательно — во избежание неточностей и ошибок) писать эту строчку вручную. Рассмотрим еще раз строки соединения, которые были созданы при подключении к базам данных xtreme и Northwind:

string ConnectionString = @"Jet 
OLEDB:Global Partial Bulk Ops=2;Jet OLEDB:Registry Path=;Jet OLEDB:Database Locking Mode=1;Jet OLEDB:Database 
Password=;Data Source=""E:\Program Files\Microsoft Visual Studio .NET 2003\Crystal 
Reports\Samples\Database\xtreme.mdb"";Password=;Jet OLEDB:Engine Type=5;Jet OLEDB:Global Bulk 
Transactions=1;Provider=""Microsoft.Jet.OLEDB.4.0"";Jet OLEDB:System database=;Jet 
OLEDB:SFP=False;Extended Properties=;Mode=Share Deny None;Jet OLEDB:New Database Password=;Jet 
OLEDB:Create System Database=False;Jet OLEDB:Don't Copy Locale on Compact=False;Jet OLEDB:Compact Without 
Replica Repair=False;User ID=Admin;Jet OLEDB:Encrypt Database=False";
Листинг 4.10. База данных xtreme, проект ProgrammDataMDB
string ConnectionString = 
"workstation id=7EA2B2F6068D473;packet size=4096;integrated security=SSPI;data sou" +
"rce=\"(local)\";persist security info=False;initial catalog=NorthwindCS";

В этой строках через точку с запятой просто перечисляются параметры соединения. В таблице 4.2 приводятся основные значения этих параметров.

Таблица 4.2. Основные параметры ConnectionString
ПараметрОписание
ПараметрОписание
Provider (Поставщик)Свойство применяется для установки или возврата имени поставщика для соединения, используется только для объектов OleDbConnection
Connection Timeout или Connect Timeout (Время ожидания связи)Длительность времени ожидания связи с сервером перед завершением попытки и генерацией исключения в секундах. По умолчанию 15
Initial Catalog (Исходный каталог)Имя базы данных
Data Source (Источник данных)
Имя используемого SQL-сервера, когда установлено соединение, или имя файла базы данных Microsoft Access
Password (Пароль)Пользовательский пароль для учетной записи SQL Server.
User ID (Пользовательский ID)Пользовательское имя для учетной записи SQL Server
Workstation IDИмя рабочей станции или компьютера
Integrated Security или Trusted Connection (Интегрированная безопасность или Доверительное соединение) Параметр, который определяет, является ли соединение защищенным. True, False и SSPI — возможные значения. (SSPI – эквивалент True.)
Persist Security Info (Удержание защитной информации)Когда установлено False, нуждающаяся в защите информация, такая как пароль, не возвращается как часть соединения, если связь установлена или когда-либо была установленной. Выставление этого свойства в True может быть рискованным в плане безопасности. По умолчанию False

При создании строки ConnectionString Мастером происходит генерирование довольно многих лишних параметров. Нельзя сказать, что они не нужны, — просто Мастер предусматривает все возможности использования этого подключения и вставляет соответствующие значения. В действительности необходимых значений для простого извлечения данных всего несколько:

  • Provider (только OLE DB)
  • Data Source
  • Initial Catalog
  • User ID/Password
  • Persist Security Info

Скопируйте папки с проектами ProgrammDataMDB и ProgrammDataSQL. Переименуйте копии в ConnStringMDB и ConnStringSQL. Измените значения ConnectionString следующим образом:

string ConnectionString = @"Provider=""Microsoft.Jet.OLEDB.4.0""; Data 
Source=""E:\Program Files\Microsoft Visual Studio .NET 2003\Crystal 
Reports\Samples\Database\xtreme.mdb"";User ID=Admin;Jet OLEDB:Encrypt Database=False";
Листинг 4.11. База данных xtreme, проект ConnStringMDB
string ConnectionString = 
"workstation id=7EA2B2F6068D473;integrated security=SSPI;
data source=\"(local)\";persist security 
info=False;initial catalog=NorthwindCS";

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

Где определяется строка подключения, когда мы создаем объект DataAdapter не программно, а с помощью Мастера? Среда генерирует строку подключения вместе с кодом для DataAdapter, и в этом нетрудно убедиться. Запустите приложение, которое мы делали, вообще ничего не зная об объектах ADO.NET, — DataWizardMDB. Перейдите в код формы, откройте область Windows Form Designer generated code и найдите строку подключения:

this.oleDbConnection1.ConnectionString = @"Jet OLEDB:Global Partial Bulk Ops=2;Jet OLEDB:Registry Path=;Jet 
OLEDB:Database Locking Mode=1;Jet OLEDB:Database Password=;Data 
Source=""D:\Uchebnik\Code\Glava4\RBProduct.mdb"";Password=;Jet OLEDB:Engine Type=5;Jet 
OLEDB:Global Bulk Transactions=1;Provider=""Microsoft.Jet.OLEDB.4.0"";Jet OLEDB:System 
database=;Jet OLEDB:SFP=False;Extended Properties=;Mode=Share Deny None;Jet OLEDB:New Database 
Password=;Jet OLEDB:Create System Database=False;Jet OLEDB:Don't Copy Locale on Compact=False;Jet 
OLEDB:Compact Without Replica Repair=False;User ID=Admin;Jet OLEDB:Encrypt Database=False";
Листинг 4.12.

Когда мы переносим объект DataAdapter из панели инструментов Toolbox (со вкладки Data) на форму, вместе с ним образуется объект DBConnection, в свойствах которого указывается строка ConnectionString и другие параметры подключения (рис. 4.48)

Свойства объекта sqlConnection1 проекта VisualDataSQL

увеличить изображение
Рис. 4.48.  Свойства объекта sqlConnection1 проекта VisualDataSQL

Управление соединением. Объект Connection

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

Когда вы вызываете у объекта DataAdapter метод (например, Fill), то он сам проверяет, открыто ли соединение. Если соединения нет, то DataAdapter открывает соединение, выполняет задачи и затем закрывает соединение.

Явное управление соединением — лучший подход к работе с базами данных. Он обладает рядом преимуществ:

  • дает более чистый и удобный для чтения код;
  • помогает при отладке приложения;
  • является более эффективным.

Для явного управления соединением используется объект Connection. Создайте новый проект и назовите его ConnectionMDB. Перетащите элемент управления DataGrid из панели инструментов Toolbox и установите свойству Dock значение Fill. Перейдите в код формы. Подключаем пространство имен:

using System.Data.OleDb;

В конструкторе Form1 после InitializeComponent создаем объект Connection:

OleDbConnection  conn = new OleDbConnection(ConnectionString);

В качестве параметра объекту conn передается строка подключения ConnectionString.

Можно также устанавливать строку подключения через свойство созданного объекта сonn:

OleDbConnection  conn  =  new OleDbConnection();
conn.ConnectionString  =  ConnectionString;

Теперь нам необходимо определить параметры самой строки ConnectionString. Вы можете сделать это вручную или скопировав код из приложения ConnStringMDB (подключаться будем к базе данных xtreme).

string ConnectionString = @"Provider=""Microsoft.Jet.OLEDB.4.0""; 
Data Source=""E:\Program Files\Microsoft Visual Studio .NET 2003\Crystal 
Reports\Samples\Database\xtreme.mdb"";User ID=Admin;Jet OLEDB:Encrypt Database=False";
Листинг 4.13.

Теперь можно устанавливать соединение, вызывая метод Open объекта Connection:

OleDbConnection  conn  =  new OleDbConnection();
conn.ConnectionString  =  ConnectionString;
conn.Open();

Объект Command

Объект Command применяется для выполнения SQL-запросов к источнику данных. Чтобы выполнить запрос, свойству Connection объекта Command следует задать объект "Имя" созданного Connection:

OleDbConnection  conn  =  new OleDbConnection();
conn.ConnectionString  =  ConnectionString;
conn.Open();
OleDbCommand myCommand = new OleDbCommand();
myCommand.Connection = conn;

Объект Connection также предоставляет метод CreateCommand, позволяющий упростить данный процесс — этот метод возвращает новый объект Command, уже инициализированный для использования вашего объекта Connection:

OleDbConnection  conn  =  new OleDbConnection();
conn.ConnectionString  =  ConnectionString;
conn.Open();
OleDbCommand myCommand = conn.CreateCommand();

Эти два способа совершенно эквивалентны.

Теперь нам следует определить SQL-запрос, который будет извлекать данные. Как и раньше, строкой, в которой будет содержаться этот запрос будет CommandText. Объявляем переменную CommandText, извлекающую все столбцы таблицы Customer:

string сommandText = @"SELECT Address1, Address2, City, [Contact First Name],
[Contact Last Name], [Contact Position], [Contact Title], Country, 
[Customer Credit ID], [Customer ID], [Customer Name], [E-mail], Fax, [Last Year'sSales], 
Phone, [Postal Code], Region, [Web Site] FROM Customer";
Листинг 4.14.

Объект myСommand имеет свойство, которое так и называется: CommandText. Чтобы избежать путаницы, изменим название переменной — CommandText на commandText (с маленькой буквы):

myCommand.CommandText = commandText;

Создаем объект OleDbDataAdapter:

OleDbDataAdapter dataAdapter = new OleDbDataAdapter();

Объект dataAdapter имеет свойство SelectCommand, в котором мы и будем указывать объект myCommand:

dataAdapter.SelectCommand = myCommand;

Создаем объект DataSet:

DataSet ds = new DataSet();

Заполняем ds данными из dataAdapter:

dataAdapter.Fill(ds, "Customers");

Указываем источник данных DataSource для dataGrid1:

dataGrid1.DataSource = ds.Tables["Customers"].DefaultView;

Закрываем соединение явным образом:

conn.Close();

Все! Запускаем приложение. Мы получили уже знакомый результат, но теперь мы действительно управляем всеми объектами, работающими с данными.

Полный листинг проекта ConnectionMDB:

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Data.OleDb;

namespace ConnectionMDB
{
	/// <summary>
	/// Summary description for Form1.
	/// </summary>
	public class Form1 : System.Windows.Forms.Form
	{
		private System.Windows.Forms.DataGrid dataGrid1;
		string ConnectionString = 
			@"Provider=""Microsoft.Jet.OLEDB.4.0"";
			Data Source=""E:\Program Files\Microsoft Visual Studio .NET 2003\Crystal Reports\Samples\Database\xtreme.mdb"
			";User ID=Admin;Jet OLEDB:Encrypt Database=False";
		string commandText = @"SELECT Address1, Address2, City, [Contact First Name], [Contact Last Name], 
			[Contact Position], [Contact Title], Country, [Customer Credit ID], [Customer ID], [Customer Name], 
			[E-mail], Fax, [Last Year's Sales], Phone, [Postal Code], Region, [Web Site] FROM Customer";


		/// <summary>
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.Container components = null;

		public Form1()
		{
			//
			// Required for Windows Form Designer support
			//
			InitializeComponent();
			OleDbConnection  conn = new OleDbConnection(ConnectionString);
 			//OleDbConnection  conn = new OleDbConnection(); 
//conn.ConnectionString = ConnectionString;
			conn.Open();
			OleDbCommand myCommand = new OleDbCommand();
			myCommand.Connection = conn;
			myCommand.CommandText = commandText;
			//OleDbCommand myCommand = conn.CreateCommand();
			myCommand.CommandText = commandText;
			OleDbDataAdapter dataAdapter = new OleDbDataAdapter();
			dataAdapter.SelectCommand = myCommand;
			DataSet ds = new DataSet();
			dataAdapter.Fill(ds, "Customers");
			dataGrid1.DataSource = ds.Tables["Customers"].DefaultView;
			conn.Close();
			//
			// TODO: Add any constructor code after InitializeComponent call
			//
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if (components != null) 
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		Windows Form Designer generated code
		
		/// <summary>
		/// The main entry point for the application.
		/// </summary>
		[STAThread]
		static void Main() 
		{
			Application.Run(new Form1());
		}

		private void Form1_Load(object sender, System.EventArgs e)
		{
		
		}
	}
}
Листинг 4.15.

Сравните использование строк ConnectionString и connectinText объектом myCommand с аналогичным использованием объектом DataAdapter (ProgrammDataMDB и ProgrammDataSQL).

Создание объекта Connection для базы данных SQL принципиально не отличается от примера, рассмотренного выше. В следующем фрагменте кода описываются те же объекты Connection и Command:

using System.Data.SqlClient;

string CommandText = "SELECT CustomerID, CompanyName, ContactName, ContactTitle, 
	Address, City, Region, PostalCode, Country, Phone, Fax FROM Customers";
string connectionString = "workstation id=7EA2B2F6068D473;integrated security=SSPI;
	data source=\"(local)\";persist security info=False;initial catalog=NorthwindCS";
			
SqlConnection  conn = new SqlConnection(connectionString);
 //SqlConnection  conn = new SqlConnection(); 
//conn.ConnectionString = ConnectionString;
			conn.Open();
			SqlCommand myCommand = new SqlCommand();
			myCommand.Connection = conn;
			myCommand.CommandText = CommandText;
			//SqlCommand myCommand = conn.CreateCommand();
			SqlDataAdapter dataAdapter = new SqlDataAdapter();
			dataAdapter.SelectCommand = myCommand;
			DataSet ds = new DataSet();
			dataAdapter.Fill(ds, "Customers");
			dataGrid1.DataSource = ds.Tables["Customers"].DefaultView;
			conn.Close();
Листинг 4.16.

На диске, прилагаемом к книге, вы найдете приложения ConnectionMDB и ConnectionSQL (Code\Glava4\ ConnectionMDB и ConnectionSQL).

У вас наверняка могло сложиться впечатление, что использование визуальных средств Microsoft Visual Studio .NET для добавления и конфигурирования объектов ADO.NET несравненно проще и логичней по сравнению с программным способом создания этих объектов. Однако это впечатление глубоко ошибочно. В действительности, только последний пример может претендовать на завершенное приложение — все остальные являются своего рода вспомогательными модулями. Повторюсь, что только полностью вручную написанный код объектов ADO.NET позволяет создавать ясные, надежные и гибкие приложения.

Вывод связанных таблиц

В начале этой лекции мы рассматривали две связанные таблицы — "Поставщики" и "Товары" базы данных Microsoft Access RBProduct (рис. 4.3). Элемент управления DataGrid позволяет представлять несколько связанных между собой таблиц.

Создайте новое приложение, назовите его DataGrid2Table. На создавшейся форме размещаем DataGrid, свойству этого элемента Dock устанавливаем значение Fill.

Подключаем пространство имен:

using System.Data.OleDb;

Создаем соединение и открываем его:

OleDbConnection conn = new OleDbConnection(connectionString);
conn.Open();

Создаем объект OleDbCommand и определяем для него соединение и строку CommandText:

OleDbCommand myCommand = new OleDbCommand();
myCommand.Connection = conn;
myCommand.CommandText = commandText;

Подключаемся к файлу базы данных Microsoft Access RBProduct.mdb, указываем соответствующие параметры строк connectionString и commandText:

string commandText = "SELECT [Адрес поставщика], [Код поставщика], Поставщик, 
Телефон FROM Поставщики";
string connectionString = @"Provider=""Microsoft.Jet.OLEDB.4.0"";Data 
Source=""D:\Uchebnik\Code\Glava4\RBProduct.mdb"";User ID=Admin;
Jet OLEDB:Encrypt Database=False";
Листинг 4.17.

Создаем объект DataAdapter и в свойстве SelectCommand устанавливаем значение myCommand:

OleDbDataAdapter dataAdapter = new OleDbDataAdapter();
dataAdapter.SelectCommand = myCommand;

Создаем объект DataSet:

DataSet ds = new DataSet();

В объекте DataSet здесь будут храниться две таблицы — главная и связанная с ней дочерняя. Поэтому воспользуемся свойством TableMappings объекта DataAdapter для занесения в него первой таблицы "Поставщики":

dataAdapter.TableMappings.Add("Table", "Поставщики");

Заполняем объект ds данными из dataAdapter:

dataAdapter.Fill(ds);

Свойству DataSource объекта dataGrid1 указываем таблицу "Поставщики" объекта ds. Обратите внимание на синтаксис — свойство Tables подразумевает наличие нескольких таблиц в объекте DataSet:

dataGrid1.DataSource = ds.Tables["Поставщики"].DefaultView;

Закрываем соединение:

conn.Close();

Запускаем приложение. Пока на форме появляется только одна таблица. Закрываем приложение и переходим в код формы. Теперь нам следует добавить объекты OleDbDataAdapter и OleDbCommand для таблицы "Товары":

OleDbCommand myCommand2 = new OleDbCommand();
myCommand2.Connection = conn;
myCommand2.CommandText = commandText2;
OleDbDataAdapter dataAdapter2 = new OleDbDataAdapter();

Обратите внимание — dataAdapter2 использует то же самое подключение conn, что и dataAdapter.

Строку commandText2 определим следующим образом:

string commandText2 = "SELECT [Код поставщика], [Код продукта], 
Наименование, [Цена, $] FROM Товары";

Теперь свяжем второй объект OleDbDataAdapter с только что созданной второй командой и отобразим "Товары" на его таблицу. Затем можно заполнить объект DataSet данными из второй таблицы:

dataAdapter2.SelectCommand = myCommand2;
dataAdapter2.TableMappings.Add("Table", "Товары");
dataAdapter2.Fill(ds);

В итоге у нас получился объект DataSet с двумя таблицами. Теперь можно выводить одну из этих таблиц на форму или две сразу. Но связь между таблицами еще не создана. Объявим объект DataRelation:

DataRelation dataRelation;

Этот объект будет представлять собой отношение между таблицами, связанными по полю "Код поставщика" (см. рис. 4.2). Для конфигурирования этого отношения нам понадобятся два объекта типа DataColumn:

DataColumn dataColumn1;
DataColumn dataColumn2;

Присваиваем каждому из этих объектов поле, по которому связаны таблицы:

dataColumn1 = ds.Tables["Поставщики"].Columns["Код поставщика"];
dataColumn2 = ds.Tables["Товары"].Columns["Код поставщика"];

Конструктору объекта dataRelation передаем имя отношения между таблицами и два объекта DataColumn:

dataRelation = new DataRelation("Товары этого поставщика", 
dataColumn1,dataColumn2);

Добавляем созданный объект отношения к объекту DataSet:

ds.Relations.Add(dataRelation);

Создаем объект DataViewManager, отвечающий за отображение DataSet в объекте DataGrid:

DataViewManager dsview = ds.DefaultViewManager;

Присвоим свойству DataSource объекта DataGrid созданный объект DataViewManager:

dataGrid1.DataSource = dsview;

Последнее, что нам осталось сделать, — сообщить объекту DataGrid, какую таблицу считать главной (родительской) и соответственно отображать на форме:

dataGrid1.DataMember = "Поставщики";

Закрываем соединение:

conn.Close();

Запускаем приложение. Теперь можно просматривать товары, поставляемые поставщикам, в том же самом виде, как это было реализовано в Microsoft Access (рис. 4.49 и 4.50).

С главной таблицы мы можем перейти на дочернюю

Рис. 4.49.  С главной таблицы мы можем перейти на дочернюю

Для возврата на главную форму можно использовать встроенную кнопку элемента управления DataGrid

Рис. 4.50.  Для возврата на главную форму можно использовать встроенную кнопку элемента управления DataGrid

Полный листинг проекта DataGrid2Table:

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Data.OleDb;

namespace DataGrid2Table
{
	/// <summary>
	/// Summary description for Form1.
	/// </summary>
	public class Form1 : System.Windows.Forms.Form
	{
		private System.Windows.Forms.DataGrid dataGrid1;
		string commandText = "SELECT [Адрес поставщика], [Код поставщика], Поставщик, Телефон FROM Поставщики";
		string connectionString = @"Provider=""Microsoft.Jet.OLEDB.4.0"";
			Data Source=""D:\Uchebnik\Code\Glava4\RBProduct.mdb"";User ID=Admin;Jet OLEDB:Encrypt Database=False";
		string commandText2 = "SELECT [Код поставщика], [Код продукта], Наименование, [Цена, $] FROM Товары";
			
			
		/// <summary>
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.Container components = null;

		public Form1()
		{
			//
			// Required for Windows Form Designer support
			//
			InitializeComponent();
			OleDbConnection conn = new OleDbConnection(connectionString);
			conn.Open();
			OleDbCommand myCommand = new OleDbCommand();
			myCommand.Connection = conn;
			myCommand.CommandText = commandText;
			OleDbDataAdapter dataAdapter = new OleDbDataAdapter();
			dataAdapter.SelectCommand = myCommand;
			DataSet ds = new DataSet();
			dataAdapter.TableMappings.Add("Table", "Поставщики");
			dataAdapter.Fill(ds);
			dataGrid1.DataSource = ds.Tables["Поставщики"].DefaultView;
			OleDbCommand myCommand2 = new OleDbCommand();
			myCommand2.Connection = conn;
			myCommand2.CommandText = commandText2;
			OleDbDataAdapter dataAdapter2 = new OleDbDataAdapter();
			dataAdapter2.SelectCommand = myCommand2;
			dataAdapter2.TableMappings.Add("Table", "Товары");
			dataAdapter2.Fill(ds);
			DataRelation dataRelation;
			DataColumn dataColumn1;
			DataColumn dataColumn2;
			dataColumn1 = ds.Tables["Поставщики"].Columns["Код поставщика"];
			dataColumn2 = ds.Tables["Товары"].Columns["Код поставщика"];
dataRelation = new DataRelation("Товары этого поставщика", dataColumn1, dataColumn2);
			ds.Relations.Add(dataRelation);
			DataViewManager dsview = ds.DefaultViewManager;
			dataGrid1.DataSource = dsview;
			dataGrid1.DataMember = "Поставщики";
			conn.Close();
			//
			// TODO: Add any constructor code after InitializeComponent call
			//
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if (components != null) 
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		Windows Form Designer generated code
		

		/// <summary>
		/// The main entry point for the application.
		/// </summary>
		[STAThread]
		static void Main() 
		{
			Application.Run(new Form1());
		}

		
	}
}
Листинг 4.18.

На диске, прилагаемом к книге, вы найдете проект DataGrid2Table (Code\Glava4\ DataGrid2Table).

Связывание элементов управления с данными

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

Создайте новый проект. Назовите его DataTextBox. Переходим на вкладку Data панели инструментов Toolbox и перетаскиваем oleDbDataAdapter. В запустившемся мастере устанавливаем подключение к файлу Microsoft Access RBProduct.mdb. В свойствах oleDbDataAdapter1 на информационной панели нажимаем Generate Dataset. Называем его dsProvider. Переходим в код формы и подключаем пространство имен:

using System.Data.OleDb;

В конструкторе формы после InitializeComponent вызываем метод Fill объекта oleDbDataAdapter:

oleDbDataAdapter1.Fill(dsProvider);

Переключаемся в режим дизайна. Располагаем на форме Label и TextBox. В свойстве Text элемента label вводим "Поставщик", в этом же свойстве элемента textBox оставляем пустую строку.

Щелкаем на знак (+) свойства DataBindings элемента управления textBox. В значении поля Text этой группы снова щелкаем на знак (+) около элемента dsProvider и выбираем поле "Поставщик" (рис. 4.51).

Связывание элемента textBox с данными

Рис. 4.51.  Связывание элемента textBox с данными

Запускаем приложение. Теперь в текстовое поле выводится первое значение столбца "Поставщик" (рис. 4.52).

Готовая форма

Рис. 4.52.  Готовая форма

На диске, прилагаемом к книге, вы найдете проект DataTextBox (Code\Glava4\ DataTextBox).

Теперь рассмотрим связывание элементов управления с данными, осуществляемое программным образом. Создайте новый проект. Назовите его DataBindings.На создавшейся форме располагаем по четыре элемента TextBox и Label(рис.рис. 4.53):

Расположение элементов на форме

Рис. 4.53.  Расположение элементов на форме

Текстовым полям, расположенным напротив надписей, устанавливаем свойство Name следующим образом:

  • "Адрес поставщика" — txtAddress
  • "Код поставщика" — txtID
  • "Поставщик" — txtProvider
  • "Телефон" — txtPhone.

Далее подключаем пространство имен:

using System.Data.OleDb;

Задаем строки подключения:

string commandText = "SELECT [Адрес поставщика], [Код поставщика], Поставщик, 
Телефон FROM Поставщики";
string connectionString = @"Provider=""Microsoft.Jet.OLEDB.4.0"";Data 
Source=""D:\Uchebnik\Code\Glava4\RBProduct.mdb"";User ID=Admin;Jet OLEDB:Encrypt 
Database=False";
Листинг 4.19.

Устанавливаем соединение и определяем все необходимые объекты:

OleDbConnection conn = new OleDbConnection(connectionString);
conn.Open();
OleDbCommand myCommand = new OleDbCommand();
myCommand.Connection = conn;
myCommand.CommandText = commandText;
OleDbDataAdapter dataAdapter = new OleDbDataAdapter();
dataAdapter.SelectCommand = myCommand;
DataSet ds = new DataSet();
dataAdapter.TableMappings.Add("Table", "Поставщики");
dataAdapter.Fill(ds);

Для связывания свойства Text текстового поля txtAddress с полем "Адрес поставщика" таблицы "Поставщики" используем код:

txtAddress.DataBindings.Add("Text", ds, "Поставщики.Адрес поставщика");
Листинг 4.20.

Аналогично для других текстовых полей:

txtID.DataBindings.Add("Text", ds, "Поставщики.Код поставщика");
txtPhone.DataBindings.Add("Text", ds, "Поставщики.Поставщик");
txtProvider.DataBindings.Add("Text", ds, "Поставщики.Телефон");

Обратите внимание на названия соответствующих столбцов. На практике вы никогда не встретите названий столбцов таблицы базы данных, содержащих пробелы между слов и к тому же на кириллице — это нонсенс. В нашем учебном проекте тем не менее все работает. Почему? Дело в том, что среда Visual Studio .NET поддерживает кодировку Unicode, поэтому теоретически названия переменных и объектов можно давать на русском языке. Вы можете убедиться в этом, переименовав, скажем переменную connectionString в "строкаПодключения" а ds в "ДатаСет"и запустить приложение – все будет работать. Но если мы будем создавать коммерческое приложение, которое должно будет работать в самых разных условиях, подобный код, сам по себе, может стать источником многочисленных ошибок. Поэтому давать названия на кириллице не следует. Для ясности изложения, однако, оставим названия столбцов как есть.

Закрываем соединение:

conn.Close();

Запускаем приложение. В текстовых полях выводится соответствующая информация.

Перемещение по записям. Объект CurrencyManager

При выводе данных в виде отдельных записей необходимо реализовать возможность перемещения по записям. Это можно сделать с помощью экземпляра класса CurrencyManager, обеспечивающего функционирование связывания с данными. Располагаем на форме четыре кнопки и надпись в ряд следующим образом (рис. 4.54):

Расположение кнопок

Рис. 4.54.  Расположение кнопок

Устанавливаем следующие свойства элементов управления:

ЭлементNameText
КнопкаbtnFirst|<
КнопкаbtnPrevious<
КнопкаbtnNext>
КнопкаbtnLast>|
НадписьlblRecordsPosition

Объявляем экземпляр cmRecords класса CurrencyManager ( в классе Form1):

CurrencyManager cmRecords;

В конструкторе формы Form1 связываем созданный объект cmRecords с таблицей "Поставщики" объекта ds:

cmRecords = (CurrencyManager)BindingContext[ds, "Поставщики"];

Создаем обработчики для событий ItemChanged и PositionChanged объекта cmRecords:

cmRecords.ItemChanged+=new ItemChangedEventHandler(cmRecords_ItemChanged);
cmRecords.PositionChanged+=new EventHandler(cmRecords_PositionChanged);
Листинг 4.21.

Вызываем метод, отображающий навигацию по записям:

DisplayRecordsPosition();

Создаем этот метод:

private void DisplayOrdersPosition()
  {
	lblRecordsPosition.Text = "Запись" + (cmRecords.Position + 1) +
+ "из" + cmRecords.Count;
  }
Листинг 4.22.

Добавляем методы, вызывающие метод DisplayOrdersPosition() в случае наступления событий ItemChanged и PositionChanged:

private void cmRecords_ItemChanged( object sender, ItemChangedEventArgs e)
	{
		DisplayRecordsPosition();
	}
private void cmRecords_PositionChanged( object sender, System.EventArgs e)
	{
		DisplayRecordsPosition();
	}
Листинг 4.23.

Добавляем обработчиков для нажатий навигационных кнопок:

private void btnFirst_Click(object sender, System.EventArgs e)
	{
		cmRecords.Position = 0;
	}
	
private void btnPrevious_Click(object sender, System.EventArgs e)
	{
		cmRecords.Position--;
	}

private void btnNext_Click(object sender, System.EventArgs e)
	{
		cmRecords.Position++;
	}

private void btnLast_Click(object sender, System.EventArgs e)
	{
		cmRecords.Position = cmRecords.Count - 1;
	}

Запускаем приложение. Теперь можно перемещаться по записям (рис. 4.55).

Форма с навигационными кнопками

Рис. 4.55.  Форма с навигационными кнопками

Изменение записей

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

Создайте новый проект и назовите его Change_Data. На этот раз мы поработаем с компонентами ADO.NET, и привязывать данные к элементам управления будем, также устанавливая соответствующие свойства в режиме дизайна. Для ускорения разработки интерфейса выделяем все надписи и текстовые поля в режиме дизайна формы в проекте DataBindings, и копируем их. Располагаем вставленные элементы на форме проекта Change_Data — при этом мы избавляемся от необходимости определять свойства Name и Text. Перетаскиваем компонент oleDbDataAdapter и устанавливаем соединение с таблицей "Поставщики" базы данных Microsoft Access RBProduct.mdb. Генерируем объект DataSet и называем его dsProviders. Связываем текстовые поля с соответствующими столбцами таблицы "Поставщики", используя свойство DataBindings (см. рис. 4.51). Переходим в код формы. В конструкторе формы после InitializeComponent вызываем метод Fill объекта DataAdapter:

oleDbDataAdapter1.Fill(dsProviders1);

Проверяем приложение, запуская его. Далее добавляем кнопки для навигации по записям и соответствующие обработчики, используя объект CurrencyManager (можно также воспользоваться готовым проектом DataBindings, изменяя названия объекта DataSet). Добавим теперь кнопки для добавления и удаления записей. Располагаем на форме две кнопки — "Добавить" и "Удалить", в свойстве Name устанавливаем значения btnAdd и btnRemove соответственно. В обработчике кнопки "Добавить" вызываем метод AddNew объекта CurrencyManager:

private void btnAdd_Click(object sender, System.EventArgs e)
{
	cmRecords.AddNew();
}

Для обработчика кнопки "Удалить" также используем встроенный метод RemoveAt, причем если на форме не будет записей, то вызываем окно предупреждения:

private void btnRemove_Click(object sender, System.EventArgs e)
{
 if(cmRecords.Count>0)cmRecords.RemoveAt(cmRecords.Position);
 else MessageBox.Show("Нет записи для удаления!", "Удаление записи", 
 MessageBoxButtons.OK, MessageBoxIcon.Error);

}

Запускаем приложение. Теперь можно добавлять и удалять записи — однако все сделанные изменения хранятся в объекте DataSet и не передаются в базу данных. Перезапустив приложение, мы обнаружим, что все записи остались прежними. Добавляем кнопку "Обновить", свойству Name этой кнопки устанавливаем значение btnUpdate. В обработчике кнопки создаем новый объект DataSet changes, в который будут записываться все изменения старого DataSet dsProviders1. Методу Update объекта oleDbDataAdapter1 передаем новый DataSet changes и, наконец, вызываем метод AcceptChanges для подтверждения изменений:

private void btnUpdate_Click(object sender, System.EventArgs e)
{
DataSet changes = dsProviders1.GetChanges();
	oleDbDataAdapter1.Update(changes);
	dsProviders1.AcceptChanges();
}

Запускаем приложение, вносим новую запись и нажимаем кнопку "Обновить". Возникает ошибка — Additional information: Value cannot be null — "Дополнительная информация, значение не может быть равным нулю". Дело в том, что объект CurrencyManager сохраняет изменения в DataSet после того, как мы перейдем от редактируемой записи к следующей. Но кроме этого, подобная функциональность кнопки "Обновить" нас не может устраивать. Поэтому добавим блок для обработки исключений, который будет проверять наличие изменений в dsProviders1:

private void btnUpdate_Click(object sender, System.EventArgs e)
{
	if (dsProviders1.HasChanges())
		try
		{
		DataSet changes = dsProviders1.GetChanges();
		oleDbDataAdapter1.Update(changes);
		dsProviders1.AcceptChanges();
		}

		catch(Exception ex)
		{
MessageBox.Show(ex.Message, "Неудачное обновление", MessageBoxButtons.OK, MessageBoxIcon.Error);
		}
			else				
MessageBox.Show("Нет записей для изменения", "Изменение записей", MessageBoxButtons.OK, MessageBoxIcon.Information);
				
}
Листинг 4.24.

Теперь, если мы попытаемся внести изменения, не переходя к следующей записи, появится окно предупреждения "Нет записей для изменения". Более того, если связывание с базой данных окажется невозможным, — например, она заблокирована, — то в окне "Неудачное обновление" выйдет сообщение с кодом ошибки. Для того чтобы пользователю не приходилось задумываться о работе CurrencyManager, добавим в код кнопки "Обновить" отключение остальных кнопок на время редактирования записи:

private void btnAdd_Click(object sender, System.EventArgs e)
{
	cmRecords.AddNew();
	btnFirst.Enabled = false;
	btnLast.Enabled = false;
	btnNext.Enabled = false;
	btnPrevious.Enabled = false;
	btnRemove.Enabled = false;
	btnUpdate.Enabled = false;
		
}

Для возврата к обычному режиму добавим на форму кнопку "Сохранить", которая будет возвращать позицию на первую запись и включать остальные кнопки:

private void btnSave_Click(object sender, System.EventArgs e)

{
	cmRecords.Position = 0;
	btnFirst.Enabled = true;
	btnLast.Enabled = true;
	btnNext.Enabled = true;
	btnPrevious.Enabled = true;
	btnRemove.Enabled = true;
	btnUpdate.Enabled = true;
}

Теперь наше приложение может просматривать, изменять и сохранять записи в базе данных (рис. 4.56).

Готовое приложение проекта Change_Data

Рис. 4.56.  Готовое приложение проекта Change_Data

На диске, прилагаемом к книге, вы найдете проект Change_Data (Code\Glava4 Change_Data).

  1)   Вы можете также писать запросы, используя заглавные буквы, — SQL нечувствителен к регистру.
  2)   Перед выполнением этого запроса убедитесь в том, что вы работаете с базой данных NorthwindCS. Или используйте команду use NorthwindCS.
  3)   База данных Microsoft Access расположена по адресу: С(имя диска):\Program Files\Microsoft Visual Studio .NET 2003\Crystal Reports\Samples\Database\xtreme.mdb
  4)   Разумеется, на вашем компьютере должен быть установлен Microsoft SQL Server 2000 и запущено приложение Service Manager.
  5)   В MS Access таблица называется Customer, в SQL — Customers.
  6)   Можно также создать объект DataSet, перетащив его на форму из вкладки Data панели инструментов ToolBox
  7)   Имя компьютера, расположение базы данных, разумеется, могут отличаться — эти значения верны только для моего компьютера.
  8)   Здесь и далее листинг указан без области Windows Form Designer generated code — когда весь пользовательский интерфейс состоит из единственного элемента DataGrid, нет особого смысла описывать его.
© INTUIT.ru, 2003-2010. Все права защищены.