Книга
Книга
3-е издание
ДжеффриI П. Мак-Манус
ДжекиДжекиГолдштейн
Джеки
Джеки
Кевин Т. Прайс
Обработка баз данных на Visual Basic* .NET
3-е издание
Database Access with
Visual Basic®. NET
Third Edition
Jeffrey P. McManus
Jackie Goldstein
Kevin T. Price
Database Access with
Visual Basic®. NET
Third Edition
Jeffrey p. McManus
Jackie Goldstein
Kevin T. Price
Обработка баз данныхна Visual Basic® .NET
3-е издание
Обработка баз данных на Visual Basic® .NET
3-е издание
Джеффри П. Мак-Манус Джеки Голдштейн Кевин Т. Прайс
Издательский дом "Вильяме" Москва ♦ Санкт-Петербург ♦ Киев 2003
ББК 32.973.26-018.2.75
М15
УДК 681.3.07
Издательский дом "Вильяме"
Зав. редакцией С. Н. Тригуб
Руководитель проекта В. В. Александров
Перевод с английского канд. физ.-мат. наук Ю.Г. Гордиенко
Под редакцией канд. физ.-мат. наук Ю.Г. Гордиенко, В.В. Александрова
По общим вопросам обращайтесь в Издательский дом "Вильяме" по адресу:info@williamspublishing.com, http://www.williamspublishing.com
Мак-Манус, Джеффри, П., Голдштейн, Джеки, Прайс, Кевин, Т.
М15Обработка баз данных на Visual Basic .NET, 3-е издание.: Пер. с англ. — М.:
Издательский дом "Вильяме", 2003. — 416 с.: ил. — Парал. тит. англ.
ISBN 5-8459-0512-5 (рус.)
Это практическое руководство разработчика программного обеспечения на Visual Basic .NET и ADO.NET, предназначенное для создания приложений баз данных на основе WinForms, Web-форм и Web-служб. В книге описываются практические способы решения задач доступа к данным, с которыми сталкиваются разработчики на Visual Basic .NET в своей повседневной деятельности. Книга начинается с основных сведений о создании баз данных, использовании языка структурированных запросов SQL и системы управления базами данных Microsoft SQL Server 2000. Затем рассматриваются способы использования основных объектов модели ADO.NET для доступа к данным в реляционных базах данных. Благодаря подробным примерам, читатели могут изучить способы использования основных свойств и методов, а также узнать о более сложных компонентах и технологиях. Многочисленные листинги с кодом на языке Visual Basic .NET иллюстрируют используемые концепции, а бизнес-ситуации показывают практическую область их применения.
ББК 32.973.26-018.2.75
Все названия программных продуктов являются зарегистрированными торговыми марками соответствующих фирм.
Никакая часть настоящего издания ни в каких целях не может быть воспроизведена в какой бы то ни было форме и какими бы то ни было средствами, будь то электронные или механические, включая фотокопирование и запись на магнитный носитель, если на это нет письменного разрешения издательства Addison-Wesley Publishing Company, Inc.
Authorized translation from the English language edition published by Pearson Esucation, Inc., Copyright © 2003
All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from the Publisher.
Russian language edition published by Williams Publishing House according to the Agreement with R&I Enterprises International, Copyright © 2003
ISBN 5-8459-0512-5 (рус.)ISBN 0-672-32343-5 (англ.)ISBN 5-8459-0512-5 (рус.)ISBN 0-672-32343-5 (англ.)© Издательский дом "Вильяме", 2003
ISBN 5-8459-0512-5 (рус.)
ISBN 0-672-32343-5 (англ.)
ISBN 5-8459-0512-5 (рус.)
ISBN 0-672-32343-5 (англ.)
© Pearson Esucation, Inc., 2003
Оглавление
Предисловие12
ГЛАВА 1. Основы построения баз данных19
ГЛАВА 2. Запросы и команды на языке SQL65
ГЛАВА 3. Знакомство с SQL Server 200097
ГЛАВА 4. Модель ADO.NET: провайдеры данных163
ГЛАВА 5. ADO.NET: объектDataSet199
ГЛАВА 6.ADO.NET: объектDataAdapter231
ГЛАВА 7. ADO.NET: дополнительные компоненты261
ГЛАВА 8. Работа с проектом базы данных
в среде Visual Studio .NET287
ГЛАВА 9. XML и .NET307
ГЛАВА 10. ADO.NET иXML329
ГЛАВА 11.Web-формы: приложения на основе ASP.NET для
работы с базами данных367
387387ГЛАВА 12. Web-службы и технологии промежуточного уровня
387
387
Содержание
Предисловие
Для кого предназначена эта книга
Структура книги
Используемое программное обеспечение
Об авторах
О соавторе
О рецензентах
Благодарности
12
12
13
13
16
16
17
18
ГЛАВА 1. Основы построения баз данных
Что представляет собой база данных
Что такое платформа базы данных
Бизнес-ситуации
Бизнес-ситуация 1.1: основные сведения о компании Jones Novelties Incorporated
Таблицы и поля
Манипулирование данными с помощью объектов
Типы данных
Схема базы данных
Использование инструментов Visual Studio для создания базы данных
Определение индексов и первичного ключа
Создание схемы базы данных
Использование программы Microsoft Visio для просмотра и изменения схемы базы данных
Отношения
Использование ссылочной целостности для поддержания непротиворечивости данных
Проверка ограничений ссылочной целостности с помощью Server Explorer
Каскадные обновления и каскадные удаления
Нормализация
Отношения типа один-к-одному
Отношения типаодин-ко-многим
Отношениятипамногие-ко-многим
Создание пользовательского интерфейса на основе Windows Forms
Подключение к базе данных и работа с записями
Создание приложения для просмотра данных
Программный способ связывания данных
Элементы управления, взаимодействующие с данными
Обновление записей в приложении просмотра данных
19
20
21
21
21
22
26
27
28
28
31
33
37
43
45
46
47
48
51
51
52
52
53
54
57
58
58
Содержание 7
Создание новых записей в форме, связанной с данными60
Удаление записей из связанной с данными формы61
Проверка введенных данных в форме, связанной с данными61
Резюме63
Вопросы и ответы63
ГЛАВА 2. Запросы и команды на языке SQL65
Что такое запрос66
Тестирование запросов с помощью компонента Server Explorer67
Отбор записей с помощью предложения SELECT69
Указание источника записей с помощью предложения FROM70
Формирование критериев с использованием предложенияWHERE71
Операторы, используемые в предложении WHERE72
Сортировка результатов с помощью предложения ORDER BY74
Сортировка в убывающей последовательности74
Сортировка по нескольким полям75
Отображение первых или последних записей диапазона с помощью
предложения ТОР75
Создание запросов TOP PERCENT76
Объединение связанных таблиц в запросе77
Выражение объединения в SQL78
Использование конструктора представлений для создания объединений78
Использование внешних объединений79
Выполнение вычислений в запросах81
Определение псевдонимов с использованием предложения AS82
Запросы, которые группируют данные и подводят итоги83
Применение предложения HAVING для группирования данных в запросах84
Функция SUM85
Перечень итоговых функций86
Запросы на объединение87
Подзапросы88
Манипулирование данными с помощью SQL88
Запросы на обновление89
Запросы наудаление90
Запрос на добавление записей90
Запросы на основе команды SELECT INTO91
Использование языка определения данных92
Создание элементов базы данных с помощью предложения CREATE92
Добавление ограничений в таблицу93
Назначение внешнего ключа94
Создание индексов с помощью команды CREATE INDEX94
Удаление таблиц и индексов с помощью предложения DROP95
Модификация структуры таблицы с помощью предложения ALTER95
Резюме96
Вопросы и ответы96
ГЛАВА 3. Знакомство с SQL Server 200097
Установка и запуск Microsoft SQL Server99
Требования для инсталляции SQL Server 2000101
Установка SQL Server 2000102
Запуск и остановка SQL Server104
Управление способом запуска SQL Server105
Основы работы с SQL Server 2000105
Запуск программы SQL Server Enterprise Manager106
Создание базы данных с помощью программы SQL Server Enterprise Manager108
Создание таблиц в базе данных SQL ServerПО
Использование программы SQL Query Analyzer для доступа к базе данных116
Использование представлений для управления доступом к данным121
Создание и запуск хранимых процедур126
Отображение текста существующих представлений или хранимых процедур130
Создание триггеров131
Бизнес-ситуация 3.1: создание триггера для поиска созвучных слов132
Управление пользователями и средства безопасности с помощью программы SQL Server Enterprise Manager134
Применение ограничений безопасности в программе SQL Query Analyzer141
Определение подключенныхпользователей143
Удаление объектов базы данных144
Бизнес-ситуация 3.2: SQL-сценарий для создания базы данных144
Резюме161
Вопросы и ответы161
ГЛАВА 4. Модель ADO.NET: провайдеры данных163
Обзор технологии ADO.NET164
Мотивация и философия164
ADO.NET и ADO 2.Х166
Место ADO.NET в архитектуре .NET Framework166
Прикладные интерфейсы167
Провайдеры данных ADO.NET167
Провайдер данных SqlClient168
Провайдер данных Oledb168
Провайдер данных Odbc168
Основные объекты169
Объект Connection170
Объект Command173
Применение объекта Command с параметрами и хранимыми процедурами176
Выполнение команд179
Объект DataReader186
Использование объектов Connection и Command во время создания приложения189
Другие провайдеры данных190
Бизнес-ситуация 4.1: создание процедуры для архивирования старых заказов по годам191
Резюме196
Вопросы и ответы197
ГЛАВА 5. ADO.NET: объект DataSet199
Компоненты объекта DataSet200
Ввод данных в объект DataSet202
Определение схемы объекта DataTable202
Вставка данных в объект DataTable206
Обновление данных в объекте DataSet207
Доступ к данным с помощью объекта DataTable211
Применение объекта DataSet223
Резюме229
Вопросы и ответы229
ГЛАВА 6. ADO.NET: объект DataAdapter231
Передача данных из источника данных в объект DataSet233
Обновление источника данных238
Указание команд обновления240
Резюме259
Вопросы и ответы259
ГЛАВА 7. ADO.NET: дополнительные компоненты
Обнаружение конфликтов при параллельном доступе к данным
Отображения таблиц и полей
Объект DataView
Бизнес-ситуация 7.1: просмотр данных из разных источников
Строго типизированные наборы данных
Резюме
Вопросы и ответы
261
262
266
268
275
280
285
285
ГЛАВА 8. Работа с проектом базы данных
в среде Visual Studio .NET
287
Создание проекта базы данных
288
Ссылки на базы данных
290
Сценарии
290
Сценарии создания данных
291
Сценарии изменения данных
293
Запуск сценария
296
Командные файлы
298
Запросы
303
Резюме
306
Вопросы и ответы
306
ГЛАВА 9. XML и .NET
307
ОбзорXML
Семейство технологий XML XML и доступ к данным
308
310
312
10
Классы XML на платформе .NET313
Применение модели Document Object Model313
Применение технологии ХРАТН315
Утилита SQLXML318
Инсталляция и конфигурирование утилиты SQLXML318
Результаты конфигурирования325
Применение XML, XSLT и SQLXML для создания отчета325
Резюме327
Вопросы и ответы327
ГЛАВА 10. ADO.NET и XML329
Основные принципы чтения и записи XML-данных330
Чтение XML-данных330
Запись XML-данных334
Формат DiffGram340
Бизнес-ситуация 10.1: подготовка XML-файлов для бизнес-партнеров347
Создание объекта XmlReader с помощью объекта Command359
Объект XmlDataDocument361
Резюме364
Вопросы и ответы364
ГЛАВА 11. Web-формы: приложения на основе ASP.NET
для работы с базами данных367
Обзор технологии ASP.NET368
HTML-элементы управления и серверные элементы управления369
Дополнительные преимущества технологии ASP.NET370
Доступ к базе данных с помощью ASP.NET371
Включение учетной записи ASPNET в состав учетных записей SQL Server371
Применение параметра TRUSTED_CONNECTION376
Применение элемента управления DataGrid379
Повышение производительности приложений с помощью хранимых процедур382
Резюме385
Вопросы и ответы385
ГЛАВА 12. Web-службы и технологии промежуточного уровня387
Применение промежуточного уровня для презентационной логики388
Обработка данных на промежуточном уровне391
Создание повторно используемых компонентов промежуточного уровня392
Использование компонента в другом приложении395
Доступ к объектам с помощью Web-служб397
Публикация существующего компонента с помощьюWeb-службы399
Доступ к Web-службе программными средствами400
Заключительные замечания403
Резюме403
Вопросы и ответы404
Предметный указатель405
Посвящается моим родителямАнн и Майку Голдштейн, наставлявшим и поддерживавшим меня на протяжении жизни.
-ДжекиГолдштейн, октябрь 2002 года
Моей жене.
- Кевин Прайс, октябрь 2002 года
Предисловие
В этой книге рассматриваются способы создания приложений для работы с базами данных на основе Visual Basic .NET и ADO.NET.
Хотя в ней описываются в основном провайдеры данных OLEDB и ODBC для .NET (глава 4, "Модель ADO.NET: провайдеры данных"), практически все примеры основаны на провайдере данных для сервера баз данных Microsoft SQL Server, которая используется большинством наших читателей. Более того, описываемые здесь подходы и концепции обычно можно очень просто применить для использования других источников данных. Все наиболее значительные отличия в способах использования этих технологий сопровождаются подробными комментариями.
Предполагая, что большинство читателей уже имеют опыт работы с сервером SQL Server, мы все же постарались сделать книгу доступной и для тех, кто не имеет большого опыта работы с ним. Некоторые читатели имеют ограниченный опыт работы с приложениями для баз данных или работали с другими типами баз данных, например с Microsoft Access или Oracle. Поэтому в главе 3, "Знакомство с SQL Server 2000", предлагается краткий обзор основных приемов работы с сервером Microsoft SQL Server. Опытные специалисты SQL Server могут без ущерба для понимания остальных глав пропустить эту вводную главу. Тем не менее следует учесть, что она покрывает широкий круг тем, а потому даже опытный читатель сможет найти в ней несколько полезных советов, ради которых стоит хотя бы бегло просмотреть всю главу.
Помимо основных сведений об SQL Server, здесь описываются язык XML и способы интеграции XML с ADO.NET. Этот очень важный аспект создания приложений баз данных с помощью Visual Basic .NET часто недооценивается или описывается недостаточно полно. Поскольку XML играет очень важную роль в создании современных приложений для работы с данными, эта тема излагается здесь очень подробно.
В книге содержится множество листингов и примеров, которые, несомненно, помогут наилучшим образом понять предлагаемый материал. Обычно описание основных принципов иллюстрируется очень простыми примерами, а затем для описания реального контекста предлагаются типичные бизнес-ситуации.
Для кого предназначена эта книга
Предполагается, что читатель уже знаком с основами Visual Basic .NET. Хотя здесь предлагаются очень подробные и последовательные инструкции по созданию кода, все же в этой книге не ставилась цель обучить читателя основам синтаксиса Visual Basic .NET. Мы считаем, что читатель уже владеет основными навыками работы с Visual Basic .NET и Visual Studio .NET, а потому решили не тратить времени на повторение общеизвестных базовых сведений. В частности, за исключением только первого примера, здесь не рассматриваются такие стандартные операции, как открытие и сохранение проектов. Наряду с этим, выполнение одной и той же операции нередко описывается несколькими разными способами, что позволит читателю расширить свои знания Visual Basic .NET без лишнего повторения одних и тех же базовых сведений. Поэтому часто приводятся разные стили кодирования и копии экранов для разных версий Windows, которые иллюстрируют гибкость Visual Basic .NET.
Большинство примеров этой книги демонстрируются с помощью типа проекта Windows Application на основе Windows Forms. Дело в том, что многие программисты на Visual Basic очень хорошо знают и часто используют этот тип проекта. Это позволяет нам сфокусироваться на способах доступа к базе данных, а не на особенностях использования разных типов проектов .NET. В последних главах книги описываются приложения на основе ASP.NET и Web-службы, поэтому в рассматриваемых там примерах представлены другие технологии и типы проектов.
Структура книги
Книга содержит три части. Первая часть состоит из глав 1-3 с предварительными сведениями о базах данных, языке SQL и сервере баз данных SQL Server. Эти фундаментальные сведения позволят читателю познакомиться с основными концепциями и понятиями, используемыми в книге. Кроме того, эти главы содержат краткие обзоры важных тем, которые будут полезны даже очень опытным специалистам в этих областях.
Вторая часть содержит основной материал книги. В главах 4-7 предлагаются подробные объяснения и многочисленные примеры основных объектов ADO.NET, a также способы использования их свойств и методов. В главе 7, “ADO.NET: дополнительные компоненты", рассматриваются более сложные компоненты и способы работы с объектами ADO.NET.
В третьей части книги приводятся фундаментальные способы применения ADO.NET для создания профессиональных приложений, а также рассматриваются методы использования проектов Visual Studio Data Projects для работы со сценариями SQL (глава 8, "Работа с проектом базы данных в среде Visual Studio .NET”), язык XML (глава 9, "XML и .NET”) и вопросы интеграции XML с ADO.NET (глава 10, "ADO.NET и XML”). Наконец, в последних главах описываются дополнительные типы проектов на основе ADO.NET: Web-приложения на основе ASP.NET (глава 11, "Web-формы: приложения на основе ASP.NET для работы с базами данных”), а также Web-службы и объекты промежуточного уровня (глава 12, "Web-службы и технологии промежуточного уровня”).
Используемое программное обеспечение
Предполагается, что читатель уже установил или способен самостоятельно установить интегрированную среду разработки приложений Visual Studio .NET. Следует учесть перечисленные различия, которые существуют между разными редакциями среды Visual Studio .NET в отношении функциональных возможностей и типа используемой базы данных.
РедакцияВозможности
Visual Studio .NET
StandardПросмотр таблиц и данных, выполнение хранимых процедур с помо
щью SQL Server Desktop Engine и Access
14
Редакция
Visual Studio .NET
Возможности
Professional
Просмотр таблиц и данных, выполнение хранимых процедур с помощью любой базы данных с провайдером данных OLEDB или драйвером ODBC.
Проектирование (создание или изменение) таблиц или представлений с помощью SQL Server Desktop Engine
Enterprise Developer,
Просмотр таблиц и данных, выполнение хранимых процедур с помо-
Enterprise Architect
щыо любой базы данных с провайдером данных OLEDB или драйвером ODBC.
Проектирование (создание или изменение) таблиц, представлений и хранимых процедур с помощью SQL Server Desktop Engine, SQL Server или Oracle
В некоторых местах глав 1, 2 и 8 используются компоненты, которые предусмотрены только в редакциях Enterprise Developer или Enterprise Architect среды Visual Studio.
В главе 3, "Знакомство с SQL Server 2000", предлагается подробное описание процесса инсталляции SQL Server 2000, которое будет особенно полезно для читателей, не имеющих опыта инсталляции этого программного продукта. Для тестовой базы данных pubs, которая поставляется вместе с SQL Server, рекомендуется создать резервную копию, потому что она используется во многих примерах этой книги и ее данные часто редактируются.
В предлагаемых бизнес-ситуациях и многих примерах используется специально созданная для этой книги база данных Novelty. В главах 3 и 8 подробно описаны сценарии создания этой базы данных. Для использования примеров нужно сначала создать базу данных Novelty на компьютере с сервером SQL Server 2000 и наполнить ее данными. При описании этих операций предполагается, что читатель обладает всеми необходимыми правами для создания базы данных на этом сервере. Учтите, что некоторые пользователи применяют термин "база данных" к понятию "приложение для работы с базами данных", но в этой книге термин "база данных" означает "структурированный набор реляционных данных, которые хранятся в SQL Server 2000".
Коды всех примеров вместе со сценариями создания и наполнения данными базы данных Novelty, которая используется в этой книге, можно найти на Web-сервере Издательского дома "Вильяме" по адресу: http://www.williamspublishing.com. Для создания этой базы данных на компьютере с SQL Server 2000 выполните ряд действий.
Откройте программу Query Analyzer с помощью команды Start^Programs^ Microsoft SQL Server^ Query Analyzer и подключитесь к нужному серверу SQL Server.
Откройте файл NoveltyDB. sql, выбирая команду меню File^Open (ФайлФ Открыть) и указывая место расположения этого файла.
После открытия этого файла его содержимое будет отображено в диалоговом
окне.
Для выполнения сценария выберите команду Execute^Query (Выполнить1^ Запрос) или щелкните на кнопке с изображением зеленой стрелки.
После выполнения сценария будет создана база данных Novelty, которую можно наполнить данными, повторяя действия, описанные в пп. 2-4, для других сценариев с суффиксом Data в имени; например, сценарий Orders- Data . sql вставляет данные в таблицу с заказами.
Учтите, что исходная версия 1.0 платформы .NET Framework и среда Visual Studio .NET не содержат провайдера данных для ODBC. Он входит в следующие версии и его можно скопировать отдельно с Web-узла компании Microsoft по адресу: http://www.microsoft.com. Кроме того, там же можно скопировать провайдер данных Microsoft .NET для Oracle, если вы используете базу данных Oracle, однако учтите, что в этой книге она не описывается.
16
Об авторах
Джеффри П. Мак-Манус (Jeffrey P. McManus) — разработчик и автор, специализирующийся на программных продуктах Microsoft. Как разработчик, он занимался созданием оперативных приложений на основе Internet-ориентированных и клиент- серверных технологий. Он автор четырех книг, включая предыдущее издание этой книги — бестселлер Обработка баз данных на Visual Basic® 6, а также две книги, посвященные .NET-ориентированным технологиям. Джеффри регулярно участвует в конференциях VBITS/VSLive, European DevWeek и VBConnections.
Джеки Голдштейн (Jackie Goldstein) — президент компании Renaissance Computer Systems, которая специализируется на создании приложений и предоставлении консультаций в области программных продуктов и технологий Microsoft. Более 18 лет он участвует в создании и сопровождении программных продуктов в США и Израиле, а также обладает огромным опытом оказания консультационных услуг и интеграции новых технологий. Джеки является региональным директором MSDN в Израиле, основателем израильской группы пользователей Visual Basic (VB User Group). Он регулярно выступает на международных конференциях разработчиков, включая VSLive!, TechEd, VBITS, Microsoft Developer Days и SQL2TheMax. Он также работал с компанией Microsoft в качестве эксперта по основным вопросам рецензирования, усовершенствования и завершения технических материалов и презентаций для всемирных конференций Microsoft Developer Days.
О соавторе
Кевин Т. Прайс (Kevin T. Price) — старший технолог, который специализируется на вопросах обеспечения безопасности и масштабирования (штат Вирджиния). В течение многих лет он сумел охватить все аспекты создания приложений с помощью набора инструментальных средств компании Microsoft. Он написал несколько глав для разных книг об XML, системе безопасности и технологиях .NET. Помимо своей основной работы, Кевин увлекается кулинарией и пейнтболом. С ним можно связаться по адресу: kpcrash@patriot. net.
17
О рецензентах
Энжани Читаджаллу (Anjani Chittajallu) получила диплом мастера в области систем управления в Индийском технологическом институте (Indian Institute of Technology) в Мадрасе. Она специализируется в проектировании и разработке корпоративных систем на основе технологий Microsoft. Энжани имеет сертификат разработчика MSCD и с ней можно связаться по адресу: srianjani@hotmail.com.
Эндрю Дж. Индовина (Andrew J. Indovina) в настоящее время является старшим разработчиком программного обеспечения в Рочестере, штат Нью-Йорк (Rochester, New York). Помимо ученой степени по информатике, он обладает глубокими знаниями языков ассемблера, C/C++, Visual Basic, Java, XML, ASP и навыками программирования на них. Кроме того, он является соавтором двух книг о языках Visual Basic и C++, а также техническим редактором многочисленных книг по информатике. В последнее время Эндрю активно занимается созданием приложений на основе Microsoft .NET.
18
Благодарности
Мы хотим поблагодарить тех, кто помогал нам создать эту книгу, в частности: Сондру Скотт (Sondra Scott), ответственного редактора, которая решила множество проблем в процессе создания книги;
Лори Мак-Гвайр (Laurie McGuire), нашего наиболее терпеливого редактора, сделавшего очень много полезного;
Кевина Т. Прайса (Kevin T. Price), который согласился принять участие в создании этой книги и пополнить главы своими материалами, несмотря на сложные обстоятельства;
Энжани Читаджаллу (Anjani Chittajallu) и Эндрю Дж. Индовину (AndrewJ.
Indovina), технических рецензентов, которые не только проверили нашу работу, но и предложили множество полезных идей;
Майкла Пизо (Michael Pizzo) из компании Microsoft, который всегда быстро отвечал на наши вопросы или рекомендовал других более опытных специалистов;
наших жен, детей, родственников и друзей, которые поддерживали нас во время создания этой книги.
Основы построения баз данныхОсновы построения баз данныхГЛАВАГЛАВА
Основы построения баз данных
Основы построения баз данных
ГЛАВА
ГЛАВА
В ЭТОЙ ГЛАВЕ...
» Что представляет собой база данных
• Бизнес-ситуации
* Манипулирование данными с помощью объектов
• Типы данных
* Схема базы данных
» Отношения
* Нормализация
Создание пользовательского интерфейса на основе Windows Forms
Резюме
Вопросы и ответы
Сердцем многих приложений, работающих в сфере бизнеса, являются базы данных. Своим широким распространением они обязаны возможности централизованного доступа к информации, который характеризуется последовательностью, эффективностью и относительной простотой создания и поддержки. В этой главе рассматриваются основы создания и поддержки баз данных, предназначенных для ведения бизнеса, т.е. здесь вы узнаете, что собой представляет база данных и как ее можно использовать для принятия решений в сфере бизнеса.
Если вы уже работали с языком Visual Basic и программировали доступ к базам данных, то материал этой главы может показаться вам довольно тривиальным. Однако не стоит пропускать ее, поскольку здесь вы найдете профессиональные термины, которые могут меняться при переходе от одной системы управления базами данных (СУБД) к другой.
Несмотря на относительное постоянство концепций, лежащих в основе различных СУБД, одни и те же вещи имеют обыкновение называться по-разному при переходе от одной конкретной реализации к следующей. Например, многие программисты, разрабатывающие приложения клиент/сервер, называют запросы (^«У), хранимые в контейнере базы данных, представлениями (view), а программисты, работающие в среде Visual Basic или Access, называют их запросами или объектами QueryDef. По своей сути это одно и то же.
Если вы переходите к Visual Basic .NET от предыдущей версии Visual Basic, то вам следует познакомиться с некоторыми новыми возможностями программирования баз данных с помощью Visual Basic .NET. Дело в том, что в ней используется фундаментально другой способ доступа к данным, который отличается от способов доступа к данным в прежних версиях Visual Basic. Он основан на стандартах Internet с прицелом на создание приложений с возможностями удаленного доступа к данным. Интегрированная среда разработки Visual Studio .NET содержит огромный набор визуальных и интуитивно понятных инструментов, которые упрощают и ускоряют процесс создания баз данных и обеспечивают более высокую степень взаимодействия. Ранее для создания и поддержки баз данных необходимо было иметь глубокие знания многих инструментов. С помощью Visual Studio .NET разработчик может обратиться к многочисленным программам-мастерам, что позволяет избежать создания рутинного кода и повысить гибкость создаваемых приложений.
Если же вы не понаслышке знакомы с разработкой баз данных в прежней версии Visual Basic 6.0, возможно, вам имеет смысл забежать вперед и перейти к главе 4, "Модель ADO.NET: провайдеры данных", посвященной новым способам доступа к данным в Visual Basic .NET.
Что представляет собой база данных
База данных (database) — это своего рода камера хранения информации. Поскольку существуют различные типы баз данных, необходимо отметить, что в данной книге рассматриваются реляционные базы данных, как самый распространенный в настоящее время тип баз данных. Реляционные базы данных обладают следующими возможностями:
сохраняют данные в таблицах, которые, в свою очередь, состоят из строк, называемых здесь записями, и столбцов, называемых здесь полями;
позволяют считывать подмножества данных из таблиц (или создавать для них запросы);
позволяют связывать таблицы друг с другом (или создавать объединения) для выборки связанных записей, хранимых в различных таблицах.
Что такое платформа базы данных
Основные функции базы данных обеспечиваются платформой баз данных (database platform), т.е. программной системой, "отвечающей" за способ хранения данных и их выборку.
Вместе с Visual Basic .NET можно использовать множество различных платформ баз данных, но в этой книге в основном рассматривается платформа Microsoft SQL Server 2000. (Более подробно она рассматривается в главе 3, "Знакомство с SQL Server 2000”.) Процессором базы данных (database engine) называется сам механизм, лежащий в основе платформы базы данных и непосредственно "отвечающий" за выполнение функций и управление данными.
Бизнес-ситуации
Многие книги, посвященные компьютерному обеспечению, состоят из длинных списков программных средств с кратким описанием особенностей их работы. Если вам повезет, вы найдете описание того, как данный продукт связан с реальным миром.
Цель настоящей книги — представить программное обеспечение в терминах бизнес-решений. Поэтому каждая глава содержит несколько бизнес-вариантов, в которых некая фиктивная компания, столкнувшись с реальными проблемами в сфере бизнеса, пытается автоматизировать работу офиса. В приведенных бизнес-ситуациях используются наработки компании Jones Novelties Incorporated, которая занимается так называемым малым бизнесом — распространением сувениров, мелких дешевых товаров и организацией вечеринок.
Бизнес-ситуация 1.1: основные сведения о компании Jones Novelties Incorporated
Исполнительный директор компании Брэд Джонс сознает, что для процветания компании нужно автоматизировать большую часть проводимых операций. Джонс понимает, что в первую очередь необходимо реализовать такие подсистемы, которые отвечают за контакты с покупателями, складское хозяйство и выписку счетов, причем реализация проекта автоматизации должна максимально отражать специфику этого бизнеса и в то же время отличаться достаточной гибкостью, позволяя вносить потенциальные изменения, продиктованные временем.
Джонс полностью сознает, что успех работы компании во многом зависит от характера организации доступа к информации, и принимает решение использовать для управления информацией компании систему реляционных баз данных. Поэтому последующий материал главы посвящен описанию структуры и особенностей функционирования этой базы данных.
22
Таблицы и поля
Базы данных состоят из таблиц,, которые представляют широкий диапазон категорий данных. Если когда-либо вам приходилось создавать базу данных, например для обработки отчетных материалов в бизнесе, то вы могли создать одну таблицу для хранения информации о клиентах, другую — о счетах, третью — о сотрудниках. Таблицы имеют заранее определенную структуру, и данные, хранящиеся в них, соответствуют этой структуре.
Таблицы содержат записи— отдельные частицы данных внутри широкой категории, которую они представляют. Например, таблица с клиентами содержит информацию обо всех потребителях товаров и услуг данной компании. Записи могут содержать данные практически любого типа. Они могут редактироваться, извлекаться и удаляться с помощью хранимых процедур и/или запросов на языке структурированных запросов (Structured Query Language — SQL).
Записи, в свою очередь, содержат поля. Поле — это некоторый раздел данных в записи. Например, запись, которая представляет некий элемент в адресной книге, может состоять из полей имени и фамилии, адреса, названия города, почтового индекса и номера телефона.
Для доступа к базам данных, таблицам, записям и полям можно использовать код Visual Basic .NET. Одна из новинок программирования баз данных в Visual Basic .NET заключается в строгой проверке типов данных. Например, в Visual Basic .NET предусмотрены новые методы getString() и getlnt(), которые позволяют сократить объем вводимого программистом кода и автоматически форматируют извлекаемые данные согласно их типу.
Проектирование базы данных
Для создания базы данных в первую очередь нужно определить, какого рода информацию ей предстоит отслеживать. Затем можно приступать к проектированию, создавая таблицы, состоящие из полей, которые определяют типы хранимых данных. После создания структуры базы данных можно сохранять данные в виде записей.
Однако невозможно добавлять данные в базу данных, которая не имеет таблиц или определений полей, поскольку в этом случае негде хранить данные. Отсюда следует, что проектирование базы данных имеет решающее значение для эффективности ее работы, в частности потому, что структура базы данных после ее реализации порой тяжело поддается изменениям.
В этой книге таблицы представлены в стандартном схематичном формате. В верхней части схемы приводится имя таблицы, а под ним — список названий полей.
tblMy Table
ID
FirstName
LastName
Многоточие, использованное вместо последнего имени поля, означает, что эта таблица имеет одно или несколько полей, которые для краткости изложения опущены.
Если вы новичок в мире программирования баз данных, но раньше использовали другие компьютерные приложения, вас, возможно, удивит, что приложение базы данных заставляет решать массу проблем еще до того, как вы приступите ко вводу данных. Например, приложение обработки текстов позволяет просто набирать и редактировать текст, а подробности, связанные с сохранением файла, вас не касаются — они решаются самим приложением. Однако с базами данных все обстоит по-другому, потому что заблаговременное проектирование структуры баз данных значительно повышает эффективность работы приложения. Если приложению будет известен точный объем и типы данных, подлежащих хранению, то процесс их сохранения и выборки может быть организован оптимальным образом. Создав свою первую многопользовательскую базу данных на 100тыс. записей, вы узнаете, что первостепенным фактором в работе базы данных является скорость выборки. Поэтому особого внимания заслуживают любые усилия, направленные на ускорение процесса добавления информации в базу данных и выборки из нее.
Эффективность работы баз данных зависит от продуманности структуры таблиц, т.е. одна и та же таблица должна включать поля, относящиеся к одной и той же категории данных. Это значит, что все записи с данными о клиентах должны храниться в таблице Customer, записи о заказах, оформляемых этими клиентами,— в таблице Orders и т.д.
Хотя эти наборы данных входят в различные таблицы, это вовсе не означает, что вы не можете использовать их вместе. Совсем наоборот. Если необходимые данные расположены в двух или нескольких таблицах реляционной базы данных, вы можете получить доступ к этим данным, используя отношения между таблицами. Отношения будут рассмотрены ниже, а пока остановимся на структуре таблиц.
Бизнес-ситуация 1.2: проектирование таблиц и отношений
Брэд Джонс понял, что компании Jones Novelties Incorporated необходим способ сохранения информации о клиентах. Он совершенно уверен в том, что большинство его деловых контактов не будут однократными, и поэтому хочет иметь возможность связываться с постоянными клиентами, чтобы отсылать им каталоги два раза в год.
Размышляя таким образом за коктейлем, Брэд начертил на салфетке первую схему базы данных. "Итак, — подумал он, — чтобы поддерживать контакты с клиентами, мне нужно хранить следующие данные:
имя клиента, его адрес, город, штат, почтовый индекс и телефонный номер;
территориальный регион страны (северо-запад, юго-запад, средний запад, северо-восток, юг или юго-восток);
дату последней покупки клиента".
Брэд предположил, что всю эту информацию можно поместить в одну таблицу и тогда его база данных будет отличаться изяществом и простотой. Однако в команде разработчиков нашлись отважные люди, которые осмелились сказать своему шефу,
что, возможно, он и прав, но построенная таким образом база данных будет неэффективной, неорганизованной и чрезвычайно негибкой.
Информация, которую Брэд хочет включить в таблицу, не преобразуется напрямую в ее поля. Например, поскольку регион является функцией от штата, в котором проживает данный клиент, то вряд ли имеет смысл помещать поля State и Region в одну таблицу. Если все-таки пойти на это, то оператору, занимающемуся вводом данных, придется дважды вводить аналогичную информацию о клиенте. Логичнее хранить поле State в таблице Customer, а остальные сведения, относящиеся к регионам, — в таблице Region. Если по таблице Region всегда можно определить, в каком регионе находится тот или иной штат, то оператору не придется вводить регион для каждого клиента. Вместо этого достаточно ввести только название штата, а затем после обработки данных из таблиц Customer и Region автоматически будет определен регион проживания данного клиента.
Точно так же критически следует посмотреть на поле Name. Если вместо поля Name использовать два поля: FirstName и LastName, то это облегчит сортировку по фамилии клиентов, если таковая потребуется. Подобный аспект проектирования может показаться тривиальным, но остается лишь удивляться тому, сколько проектов баз данных не учитывают таких простых вещей, как эти. Важно понять, что все это гораздо легче предусмотреть на этапе проектирования базы данных, чем пытаться исправить дефекты, вызванные недальновидностью разработчиков, когда база данных уже заполнена информацией.
Поэтому Брэд и его сотрудники решили, что информация о клиентах компании Jones Novelties Incorporated будет храниться в таблице tblCustomer, которая содержит перечисленные ниже поля.
tblCustomer
ID
FirstName LastName Company Address City State PostalCode Phone Fax Email
Данные, относящиеся к региону страны, в котором проживает клиент, следует хранить в таблице tblRegion.
tblRegion
ID
State
RegionName
Между двумя этими таблицами существует отношение по полю State. Обратите внимание на то, что это поле присутствует в обеих таблицах. Отношение между таблицами Region и Customer является отношением один-ко-многим, поскольку для каждой записи в таблице tblRegion может существовать или одна, или ни одной, или много записей в таблице tblCustomer, совпадающих по полю State. (Далее в главе подробно описывается, как воспользоваться преимуществами такого отношения при выборке записей.)
Обратите внимание на то, какие имена были присвоены таблицам и полям этой базы данных. Во-первых, имя каждой таблицы начинается с префикса tbL Этот префикс позволяет с первого взгляда определить, что вы имеете дело именно с таблицей, а не с другим типом объектов базы данных, в котором могут храниться записи. Заметьте, что каждое имя поля состоит из полных слов (а не сокращений), но не включает пробелов или таких специальных символов, как символы подчеркивания.
Несмотря на то что механизм управления базами данных Microsoft SQL Server позволяет использовать в именах объектов баз данных пробелы, символы подчеркивания и другие символы, не входящие в число алфавитно-цифровых, все-таки стоит избегать их использования, поскольку это затрудняет запоминание точного написания имени поля. (В этом случае вам не придется, например, гадать, как называется поле, содержащее имя: FirstNameили FIRST_NAME.) Эта директива может показаться сейчас незначительной, но, когда вы начнете писать код для базы данных, содержащей 50 таблиц и 300 полей, вы по достоинству оцените существующие соглашения о присвоении имен, особенно на первом этапе разработки.
Рассмотрим последний пункт из предварительного списка Брэда, предусматривающий регистрацию ответа на вопрос: "Когда данный клиент в последний раз делал у нас покупку?”. Разработчик базы данных решил, что эта информация может храниться в таблице, которая предназначена для хранения данных, относящихся к заказам клиентов. Ниже приведена структура этой таблицы.
tblOrder
ID
CustomerlD
OrderDate
Amount
В этой таблице поле ID уникальным образом идентифицирует каждый заказ. Поле CustomerlD, с другой стороны, связывает заказ с клиентом. Чтобы установить эту связь, идентификатор клиента копируется в поле CustomerlD таблицы Order. Таким образом, совсем нетрудно отыскать все заказы для конкретного клиента (как будет продемонстрировано, ниже).
Манипулирование данными с помощью объектов
После создания таблиц можно приступить к манипуляциям с данными: вводить данные в таблицы, извлекать их из таблиц, проверять и изменять структуру таблиц. Для манипулирования структурой таблиц используются команды определения данных (более подробно они описываются в главе 2, "Запросы и команды на языке SQL”), а для манипулирования данными — объекты DataSet или DataReader платформы .NET.
Объект DataSet обычно представляет подмножество записей, которые извлекаются из базы данных. Оно концептуально аналогично таблице (а в некоторых случаях— группе связанных полей), но также содержит несколько важных собственных свойств. Объекты DataSet можно легко представить в виде XML-данных и использовать для передачи удаленных данных (как, например, при передаче результатов выполнения запроса от сервера к клиенту или при обмене данными между двумя серверами). В Visual Basic .NET объекты DataSet не ограничены только сохранением извлеченных данных. Например, объект DataSet может использоваться для управления статическими данными в XML-документе или файле конфигурации, либо для управления динамическими данными, созданными на основе пользовательских данных в более сложных ситуациях.
Как и при работе с технологией ADO, в Visual Basic .NET и ADO.NET можно использовать подключенные и неподключенные объекты DataSet. Неподключенный объект DataSet с данными передается приложению, соединение с базой данных закрывается, а базе данных ничего не известно о манипуляциях с этими данными до тех пор, пока приложение вновь не обратится к базе данных. Допустим, что пользователь открывает форму и щелкает на кнопке для обновления данных. В таком случае приложение должно снова соединиться с базой данных и выполнить код изменения данных. В то же время с помощью подключенного объекта DataSet используемые данные "блокируются" и все изменения данных мгновенно воспроизводятся в базе данных. Эта технология более подробно рассматривается в главе 5, "ADO.NET: объект DataSet".
Объект DataReader работает аналогично объекту DataSet, но обладает другими возможностями и характеристиками производительности. Одно из отличий отражено в его названии: объект DataReader считывает данные, т.е. он предоставляет доступ к данным только для чтения. Для объекта DataReader также не предусмотрен простой способ представления данных в формате XML.
Для простоты (что также рекомендуется делать на практике) в данной книге объ-' ект DataReader используется для выполнения базовых операций доступа к данным, а объект DataSet — только в случае крайней необходимости, например для создания Web-служб (более подробно они рассматриваются в главе 12, "Web-службы и технологии промежуточного уровня”).
Объект DataSet устроен так же, как и объект ADODB.Recordset в прежних верСИЯХ языка Visual Basic. Аналогично другим типам объектов языка Visual Basic, объект DataSet имеет свойства и методы. Более подробно он рассматривается в других главах книги, а здесь достаточно отметить, что на платформе .NET объекты используются для организации в приложении ясного, согласованного и относительно простого способа работы с базами данных.
27
Типы данных
Один из этапов проектирования базы данных заключается в объявлении типа каждого поля, что позволяет процессору базы данных эффективно сохранять и извлекать данные. В SQL Server предусмотрено использование 21 типа данных, которые перечислены в табл.
Таблица 1.1. Типы данных в SQL Server
Тип данных
Описание
bigint
Восьмибайтовое целое число в диапазоне от -9 223 372 036 854 775 808
до 9 223 372 036 854 775 807
binary
Двоичные данные фиксированного размера до 8 Кбайт
char
Символьное поле фиксированного размера до 8000 символов
datetime
Время и дата между 1 января 1753года и 31 декабря 9999 года
decimal
Десятичное число с фиксированной точностью и размером от 5 до 17 байт. Во время создания поля можно указать число десятичных знаков
float
Десятичное число размером от 4 до 8 байт и не более 53 десятичных знаков после запятой
image
Двоичные данные переменного размера до 2 147 483 647 байт
int
Четырехбайтовое целое число в диапазоне от -2 147 483 648 до
2 147 483 647
money
Числовое поле со специальными свойствами для сохранения денежных значений
nchar
Символьное поле фиксированного размера до 4000 символов Unicode
ntext
Символьное поле произвольного размера до 1 073 741 823 символов Unicode
nvarchar
Символьное поле произвольного размера до 4000 символов Unicode
real
Десятичное число размером 4 байта и не более 24 десятичных знаков после запятой
smalldatetime
Время и дата между 1 января 1900 года и 6 июня 2079
smallint
Двухбайтовое целое число в диапазоне от -32 768 до 32 767
text
Символьное поле произвольного размера до 2 147 483 647 символов (в базе данных Microsoft Access есть аналогичное поле типа Memo)
tinyint
Однобайтовое целое число в диапазоне от 0 до 255
uniqueidentifier
Целое число, которое также называется глобально уникальным идентификатором и используется для уникальной идентификации записи (часто применяется для репликации данных)
varbinary
Двоичные данные переменного размера до 8000 байт
varchar
Символьные данные переменного размера до 8000 символов
28
Хотя типы данных Visual Basic .NET более близки к типам данных полей SQL Server, чем типы данных Visual Basic 6, между ними все равно нет однозначного соответствия. Например, тип данных int в SQL Server соответствует типу integer в Visual Basic .NET, потому что оба они являются 32-битовыми целыми числами. Однако в SQL Server нельзя создать поле с определенным пользователем типом или типом Ob j ect языка Visual Basic .NET.
Схема базы данных
Для создания структуры базы данных рекомендуется не только подготовить список таблиц и полей, но и представить таблицы и поля в графическом виде. После этого вы не только сможете сказать, какие таблицы и поля доступны для вас, но и как они связаны друг с другом. Именно для этого и предусмотрена схема базы данных.
Схему можно представить как карту дорог для вашей базы данных. На схеме все таблицы, поля и отношения в базе данных изображаются графически. Схему базы данных важно рассматривать как часть процесса проектирования программного продукта, поскольку с ее помощью можно быстро понять, что происходит в базе данных.
Схемы не теряют своей актуальности и после завершения процесса проектирования базы данных. Без такой схемы вам будет трудно выполнять многотабличные запросы. Толково составленная графическая схема поможет ответить на вопросы типа: "Какие таблицы мне нужно объединить, чтобы составить список всех заказов с объемом, превышающим $50,00, поступивших от клиентов из штата Миннесота в течение последних 24 часов?" (За дополнительной информацией по созданию запросов, включающих более одной таблицы, обращайтесь к главе 2, "Запросы и команды на языке SQL’’.)
Официальных способов создания схем баз данных не существует, но есть много средств, которыми можно воспользоваться при их создании. Например, графический редактор Visio отличается гибкостью, быстротой и простотой применения. Более того, он хорошо интегрируется с другими приложениями Windows, в частности с Microsoft Office. Этот редактор распространяется отдельно, а также входит в состав Visual Studio .NET Enterprise Architect.
Все сказанное в пользу Visio не означает, что при создании графической схемы базы данных вы должны использовать только программу Visio. Можно применять любые другие инструменты рисования, с которыми вы знакомы. Приемлемый вариант — программа Microsoft Windows Paint, можно также воспользоваться средствами рисования, предусмотренными в программе Microsoft Word.
Использование инструментов Visual Studio для создания базы данных
Существует несколько способов создания баз данных в SQL Server. С помощью набора инструментов SQL Enterprise Manager базы данных можно создавать графически или программно (с помощью команд на языке SQL). Помимо него, существует множество других внешних инструментов для создания баз данных, например Visio, который описывается далее в главе.
Visual Studioтакже содержит очень удобный инструмент для работы с базами
данных SQL Server. Он входит в состав нового компонента Server Explorer, который предназначен для централизованного управления всеми видами серверного программного обеспечения. Для создания базы данных SQL Server с помощью компонента Server Explorer выполните перечисленные ниже действия.
Запустите интегрированную среду разработки Visual Studioвыбрав команду
Visual StudioVisual Studio
В левойчастиокна VisualStudioоткройтеокноServerExplorer,выбрав
команду менюExplorer (учтите, что эта вкладка может иметь гори
зонтальную или вертикальную ориентацию).
В этом окне раскройте узел Servers, найдите ваш компьютер, а затем раскройте узел SQL Servers и найдите в нем экземпляр SQL Server, который установлен на вашем компьютере, как показано на рис.
РИС. 1.1. Вкладка Server Explorer интегрированной средыVisual Studio скоторой можносерверными
процессами
Для создания новой базы данных щелкните правой кнопкой мыши на имени экземпляра SQL Server, который установлен на вашем компьютере. На рис. 1.1 показан компьютер ROCKO, хотя ваш компьютер может иметь совершенно другое имя. В контекстном меню выберите команду New Database (Создать новую базу
На экране появится диалоговое окно Create Database (Создать базу данных). Введите в нем имя базы данных Novelty и щелкните на кнопке ОК.
После этого в окне Server Explorer появится новая база данных Novelty.
При раскрытии узла этой базы данных будут отображены следующие категории объектов базы данных:
Database Diagrams (Диаграммы базы данных);
Tables (Таблицы);
Views (Представления);
30
Stored Procedures (Хранимые процедуры);
Functions (Функции).
Для использования базы данных нужно создать в ней хотя бы одну таблицу. Для этого выполните перечисленные ниже действия.
в окне Server Explorer щелкните правой кнопкой мыши на узле Tables базы данных Novelty, а затем из контекстного меню выберите команду New Table (Создать таблицу).
После этого в окне Visual Studio .NET появится диалоговое окно для создания структуры новой таблицы. Создайте таблицу tblCustomer с перечисленными ниже определениями полей.
Имя поля
Тип данных
Длина
Наличие неопределенных значений
ID
int*
4
Нет
FirstName
varchar
20
Да
LastName
varchar
30
Да
Company
varchar
50
Да
Address
varchar
50
Да
City
varchar
30
Да
State
char
2
Да
PostalCode
varchar
9
Да
Phone
varchar
15
Да
Fax
varchar
15
Да
varchar
100
Да
Учтите, что поле ID используется для идентификаторов, т.е., оно содержит уникальное целое число для каждой строки данной таблицы.
После ввода этих определений таблица будет иметь вид, показанный на рис. 1.2.
Щелкните на поле ID и выберите команду меню Diagram^Set Primary Key (Диаг- рамма^Создать первичный ключ). Благодаря этому все значения данного поля будут уникальны, т.е. все клиенты будут иметь разные идентификаторы. (Более подробные сведения о первичных ключах приводятся в следующем разделе.)
Далее нужно указать, что поле ID используется в SQL Server в целях автоматической генерации идентификационных номеров для клиентов. Для этого щелкните правой кнопкой мыши на окне с определением таблицы и выберите в контекстном окне Indexes/Keys (Индексы/ключи).
После этого на экране появится диалоговое окно Property Pages (Страницы свойств) со вкладкой Indexes/Keys. Выберите вкладку Tables (Таблицы) со свойствами таблицы.
В списке Table Identity Column (Поле таблицы с идентификаторами) выберите поле ID.
Щелкните на кнопке Close (Закрыть).
31
РИС.Создание определениясVisual Studio
Выберите команду меню таблицу Tablel).
В диалоговом окне Choose Name (Выбрать имя) введите имя и щелкните на кнопке ОК. Обратите внимание, что после сохранения таблицы ее имя появится в списке таблиц базы данных Novelty в окне компонента Server Explorer.
Определениеиндексовипервичногоключа
Теперь, когда вы создали базовую таблицу, осталось определить индексы. Индекс (index) — это атрибут, который можно присвоить полю, чтобы облегчить для процессора баз данных выборку данных на основе информации, хранимой в этом поле. Например, в базе данных, содержащей сведения о сотрудниках, вероятно, будет реализована функция поиска клиента по фамилии, отделу или идентификационному номеру. Поэтому для каждого из этих полей имеет смысл создать индексы, чтобы ускорить процесс выборки записей на их основе.
Если вы поняли, какая польза от применения индексов в структуре базы данных, у вас может возникнуть вопрос: если наличие индексов значительно ускоряет поиск, почему бы не создать индексы для каждого поля каждой таблицы? Ответ прост: индексы — это не только плюс, но и минус. При увеличении количества индексов физически увеличивается размер базы данных, а значит, и объем занимаемой памяти и дискового пространства, в результате чего компьютер работает медленнее. В этом случае польза от применения индексов сводится к нулю. Не существует жесткого правила насчет оптимального количества индексов для каждой таблицы, но основная рекомендация состоит в создании индексов только по таким полям, которые, по вашему мнению, будут чаще всего использованы в запросах. (За дополнительной информацией о том, как использовать содержимое поля в качестве критерия запроса для выборки наборов записей, обращайтесь к главе 2, "Запросы и команды на языке SQL”.)
Первичный ключ (primary key) — это специальный тип индекса. Поле, которое определено в качестве первичного ключа таблицы, служит для уникальной идентификации записей. Поэтому, в отличие от других типов индексов, никакие две записи в одной и той же таблице не могут иметь одинакового значения в поле их первичного ключа. Кроме того, при определении поля в качестве первичного ключа никакие две записи в этом поле не могут содержать пустое или неопределенное значение (null). Определив некоторое поле таблицы как первичный ключ, вы можете создать в своей базе данных отношения между этой и другими таблицами.
Каждая создаваемая вами таблица должна иметь по крайней мере один первичный ключ и должна быть проиндексирована по тем полям, которые чаще всего будут участвовать в запросах. В случае с таблицей tblCustomer, как и с многими другими таблицами баз данных, первичный ключ создается по полю ID. (В предыдущем разделе это поле уже было определено как первичное.) Вторичными индексами могут быть поля FirstName и LastName.
Попробуем создать еще два индекса для полей FirstName и LastName, для этого выполните перечисленные ниже действия.
Щелкнитеправойкнопкоймышинаокнес определениемтаблицыtblCus-
tomer в окне компонента Server Explorer и выберите в контекстном меню команду Indexes/Keys.
После этого на экране появится страница свойств со списком существующих индексов, в котором уже присутствует индекс первичного ключа PK_tblCustomer. Щелкните на кнопке New (Создать) для создания нового индекса для поля FirstName.
В списке полей выберите поле FirstName, как показано на рис. 1.3, а затем щелкните на кнопке Close.
Повторите действия из пп. 1-3, чтобы создать индекс для поля FirstName.
ВНИМАНИЕ!
В нижней части диалогового окна Property Pages находится параметр Create UNIQUE (Создать уникальный индекс). He устанавливайте флажок для этого параметра, потому что в таком случае в таблицу можно будет вводить только разные имена клиентов! Уникальные индексы следует создавать только для того, чтобы гарантировать уникальность значений данного поля.
33
РИС. 1.З. Диалоговое окно Property Pages после определения индекса для поля
Для сохранения внесенных изменений в базу данных выберите команду меню таблицуПосле ус
пешного сохранения внесенных изменений закройте окно создания схемы базы данных Visual Studio
Создав структуру данных для таблицы, можно приступить к вводу данных в нее. В окне Server Explorer предусмотрены удобные средства ввода данных в таблицу. Для этого нужно щелкнуть правой кнопкой мыши на таблице в окне Server Explorer и выбрать из контекстного меню команду Retrieve Data from Table (Извлечь данные из таблицы). В результате в окне Server Explorer появится сетка с полями ввода данных (рис. 1.4).
Данные вводятся непосредственно в каждую ячейку сетки, а при переходе к следующей строке данные из предыдущей строки сохраняются в базе данных. Учтите, что в поле данные можно не вводить— они будут вводиться в него автоматически, поскольку оно было создано как поле с идентификаторами. Процессор базы данных автоматически будет заполнять это поле значениями при переходе к следующей строке сетки.
Получив эти базовые знания о создании таблицы с помощью Visual Studioвы
сможете создавать базы данных практически любого вида. Однако для создания сложных баз данных с несколькими таблицами часто требуется установить отношения (связи) между ними. Чтобы упростить эту задачу, используется схема базы данных.
Создание схемы базы данных
Схема базы данных (database diagram) — это визуальное представление таблиц в базе данных. Для создания таблиц и отношений между ними можно использовать инструменты создания схемы баз данных, которые предусмотрены в SQL Server. А для создания схемы баз данных с помощью окна Server Explorer среды Visual Studioвы
полните перечисленные ниже действия.
РИС. 1.4. Ввод данных в таблицу с помог^ъю команды контекстного меню Retrieve Data from Table
Разверните узел базы данных Novelty в окне Server Explorer, щелкните правой кнопкой мыши на узле Database Diagrams и выберите в контекстном меню команду New Diagram (Создать схему).
В диалоговом окне Add Table (Добавить таблицу) будет приведен список таблиц базы данных. Выберите созданную ранее таблицу tblCustomer, щелкните на кнопке Add (Добавить), а затем на кнопке Close.
В результате будет создана новая схема базы данных с таблицей tblCustomer (рис. 1.5).
Для добавления второй таблицы в эту схему щелкните правой кнопкой мыши на пустом пространстве возле таблицы tblCustomer и выберите в контекстном меню команду New Table (Создать таблицу).
На экране появится диалоговое окно Choose Name, в которое следует ввести имя новойтаблицыtblOrder.
После этого в окне схемы появится сетка для определения полей новой таблицы. Создайте в ней поля, показанные на рис. 1.6.
Выберите команду меню File^Save для сохранения схемы базы данных, и на экране появится диалоговое окно с просьбой подтвердить создание новой таблицы. Щелкните на кнопке Yes, и в окне Server Explorer появится вновь созданная таблица.
35
РИС. 1.5. Схема базы данных Novelty, которая содержит все таблицы, выбранные в диалоговом Add Table
РИС. 1.6. Схема базы данных Novelty, которая содержит все таблицы, выбранные в диалоговом Add Table
После создания таблиц для клиентов и заказов следует установить связь (отношение) между ними. Например, при создании заказа идентификатор клиента ID из таблицы tblCustomer копируется из записи клиента в полетаблицы
Для указания этого отношения между таблицами на схеме базы данных выполните перечисленные ниже действия.
Щелкните на поле ID в таблице tblCustomer и перетащите его к полю Cus-
таблицы tblOrder.
На экране появится диалоговое окно Create Relationship (Создать отношение), в котором можно указать свойства отношения между двумя таблицами. После этого уже нельзя создавать заказы для клиентов с идентификаторами, которых нет в таблице клиентов. Это ограничение имеет большое практическое значение и потому с ним следует согласиться.
Схема базы данных обновляется для отражения новогокак показа
но на рис. 1.7.
РИС. 1.7. Схема базы данных с обозначением отношения между tblCustomer и tblOrder
Для сохранения созданной схемы базы данныхвыберите
командуВ диалоговом окне Save New Database Diagram
(Сохранить имя новой схемы базы данных) введите имя Relationships (Отношения) для новой схемы базы данных. При этом возможно появление диалогового окна с просьбой подтвердить создание новой таблицы. Щелкните на кнопке Yes для сохранения вновьсозданнойтаблицы
SQL Server может сохранять схему базы данных вместе с самой базой данных. Таким образом, всегда можно получить доступ к схеме, даже с помощью других струментов, например с помощью программы Enterprise Manager или среды Visual Studio
Использование программы Microsoft Visio для просмотра и изменения схемы базы данных
Помимо инструментов среды Visual Studioдля создания, просмотра и изме
нения схем базы данных могут использоваться другие очень удобные средства. Программа Microsoft Visio обладает всеми необходимыми возможностями автоматического создания схемы для уже имеющейся базы данных, т.е. реинжиниринга базы данных. Эта функциональная возможность особенно полезна для работы с унаследованными базами данных, которые создавались очень давно и с использованием совсем других инструментов.
НА ЗАМЕТКУ ——:
Для создания базы данных SQL Server совсем необязательно знать особенности боты с программой Visio. Это всего лишь еще один способ создания и
вания схемы базы с помощью одного набора операций. Если вы предпочитаете пользовать компонент Server Explorer среды Visual Studio (или инструмент Enterprise Manager) либо у вас нет программы Visio, то в таком случае можно пропустить дан- раздел без ущерба для понимания остального материала.
Реинжиниринг (reverse engineering) базы данных заключается в проверке схемы существующей базы данных и созданииотношений между объектами базы данных
(Entity Relationship Diagram —ERD-диаграмма — это способ символьного представ
ления базы данных, основанный на широких категориях данных, или сущностях которые обычно хранятся в базе данных в виде таблиц.
Для реинжиниринга схемы базы данных с помощью программы Visio выполните перечисленные ниже действия.
Запустите программу Microsoft Visio 2002 for Enterprise Architects, выбрав командуVisio. В панели Choose Drawing Type (Выбрать
тип рисования) выберите категорию Database (База данных).
Затем в панели Template (Шаблон) выберите параметр Database Model Diagram (Схема базы данных), и на экране появится основное окно программы Visio (рис. 1.8).
Выберите команду менюReverse Engineer (База
ринг) для запуска программы-мастера Reverse Engineer Wizard.
Из списка Installed Visio drivers (Инсталлированные драйверы Visio) выберите драйверSQL Server.
Затем нужно определить источник данных, который позволит получить доступ к базе данныхДля этого щелкните на кнопке New.
На экране появится диалоговое окно Create New Data Source (Создать новый источник данных) с предложением указать тип создаваемого источника данных. Выберите источник данных System Data Source и щелкните на кнопке Next.
РИС. 1.8. Основное окно программы Visio: слева показан шаблон, а справа - область рисования. Элементы схемы создаются с помощью перетаскивания элементов шаблона в области рисования
В следующем окне снова предлагается выбрать драйвер базы данных. Прокрутите список драйверов и выберите SQL Server. Щелкните на кнопке Next, a затем на кнопке Finish.
На экране появится новое диалоговое окно Create a New Data Source to SQL Server (Создать новый источник данных для SQL Server) с предложением указать источник данных. Введите имя базы данных Novelty в поле Name, а затем в списке с надписью Which SQL Server do you want to connect to? (К какому серверу SQL Server нужно присоединиться?) выберите (local). После этого щелкните на кнопке Next.
Укажите режим аутентификации на сервере SQL Server. (Более подробно этот вопрос рассматривается в главе 3, "Знакомство с SQL Server 2000”.) Затем щелкните на кнопке Next.
о. В следующем диалоговом окне установите флажок Change the default database to: (Заменить используемую по умолчанию базу данных:) и выберите в списке базу данных Novelty. Щелкните на кнопке Next, а затем на кнопке Finish.
39
В последнем диалоговом окне ODBC Microsoft SQL Server Setup (Установки параметров драйвера ODBC Microsoft SQL Server) можно протестировать соединение с базой данных с помощью известных параметров соединения. Щелкните на кнопке Test Data Source (Проверка источника данных), чтобы убедиться в работоспособности соединения. После успешной проверки соединения щелкните на кнопке ОК.
После этого источник данных Novelty будет автоматически выбран и представлен в диалоговом окне программы-мастера Reverse Engineer Wizard. Дважды щелкните на кнопке Next, чтобы пропустить экран для выбора типов объекта.
На следующем экране выберите таблицы tblCustomer и tblOrder для выполнения реинжиниринга. Затем щелкните на кнопке Finish. После этого программа Visio самостоятельно создаст схему вашей базы данных, включая отношение между определенными ранее таблицами tblCustomer и tblOrder (рис. 1.9).
РИС. 1.9. Схема, созданная с помогимо программы-мастера Reverse Engineer Wizard, с двумя таблицами базы данных Novel ty и отношениями между ними
В результате такого трудоемкого и рутинного процесса программа-мастер Reverse Engineer Wizard устраняет необходимость использования отдельной программы- мастера для создания источника данных ODBC — устаревшей технологии компании Microsoft, предназначенной для обеспечения взаимодействия приложений с реляционными базами данных. (Более подробно технология ODBC описывалась в прежнем издании книги, но она не очень широко используется в среде Visual Studio .NET, а потому эта тема опущена в данном издании.)
Важной особенностью технологии ODBC является то, что после создания именованного источника данных ODBC его не нужно создавать повторно. Для следующих попыток доступа к базе данных Novelty используется уже созданный источник данных ODBC.
Что нужно сделать для добавления в схему другой таблицы с помощью программы Visio? Напомним, что исходная версия схемы базы данных, созданная Брэдом Джонсом на клочке салфетки, включала возможность отбирать клиентов по региону. Поэтому в данную схему нужно включить таблицу с регионами, выполнив перечисленные ниже действия.
В окне Entity Relationship (Отношения между объектами) в левой части окна программы Visio щелкните на компоненте Entity (Объект) и перетащите его в область рисования. При этом будет создан новый объект (таблица), который по умолчанию называется Table 1.
Щелкните правой кнопкой мыши на созданном объекте и выберите в контекстном меню команду Database Properties (Свойства базы данных). На экране появится страница свойств базы данных Database Properties.
ВведитеновоеимятаблицыtblRegionв текстовомполеPhysicalname(Физи
ческое имя).
В списке Categories (Категории) страницы свойств базы данных Database Properties щелкните на категории Columns (Поля) и создайте три поля в сетке с определением таблицы, как показано на рис. 1.10. Обратите внимание, что для длины поля типа char или varchar нужно выбрать поле и щелкнуть на кнопке Edit (Редактировать) с правой стороны страницы свойств.
После выполнения этих действий схема базы данных будет выглядеть, как показано на рис. 1.10.
НА ЗАМЕТКУ
Здесь продемонстрирован очень простой способ создания схемы базы данных. Уч-
I тите, что в программе Visio есть много других более сложных специализированных I шаблонов для создания схем базы данных.
Между таблицами tblRegion и tblCustomer существует отношение на основе связи между их полями State. Для отражения этого отношения в схеме нужно использовать компонент Relationship так, как описано ниже.
41
РИС. 1.10. ERD-диаграмма с определением новой таблицы tblRegion
В окне Entity Relationship (Отношения между объектами) в левой части окна программы Visio щелкните на компоненте Relationship и перетащите его в об. ласть рисования. Он представляет собой линию со стрелкой и с зелеными квадратиками (метка-манипулятор) на концах линии.
Щелкните и перетащите одну из зеленых меток на таблицу tblRegion. При этом цвет метки станет красным, что означает незавершенность выполняемых действий с данным отношением.
Щелкните и перетащите другую зеленую метку на таблицу tblCustomer.
В странице свойств в нижней части окна Visio выберите поля State в обеих а затем щелкните на кнопке Associate (Связать). Теперь ERD- диаграмма будет выглядеть, как показано на рис. 1.11. Обратите внимание, что кнопка между двумя списками полей из таблиц tblRegion илибо
будет неактивной, либо будет содержать надпись Disconnect (Разорвать) или AssociateНапример при выборе по одному полю в каждом списке эта
кнопка будет иметь надпись Associate.
с отображением отношения между таблицами tblCus- tomer
После создания таблицы в схеме базы данных можно использовать программу Visio для создания таблицы в базе данных. Для этого нужно выбрать команду меню Database^Update (База данных1^ Обновить). На экране появится диалоговое окно программы-мастера Database Update Wizard с предложением выполнить обновление. Для обновления базы данных можно создать сценарий на языке определения данных (Data Definition Language — DDL), который внесет все необходимые изменения в базу данных. Этот способ позволяет задокументировать все вносимые изменения и использовать их для репликации. (Более подробно DDL рассматривается в главе 2, "Запросы и команды на языке SQL’’.) Для обновления базы данных программа Visio может просто внести их в базу данных без создания сценария на DDL. Программа-мастер Database Update Wizard позволяет использовать любой из этих вариантов либо оба вместе.
Часто создание графической модели позволяет обнаружить недостатки схемы базы данных. Например, созданная ранее схема позволяет сохранять информацию о клиентах и заказах, но заказы состоят из товаров, взятых со склада компании и проданных клиенту. Однако в данной схеме базы данных не предусмотрена возможность просмотра товаров, заказанных клиентом.
Для решения этой проблемы нужно создать таблицу для хранения сведений о товарах заказа, которая имеет приведенную ниже структуру.
tblOrderltem
ID
OrderlD
ItemID Quantity Cost
Теперь между таблицами tblOrder и tblOrderltem существует отношение типа один-ко-многим, как показано на рис. 1.12.
Полностью схему всей базы данных Novelty можно скопировать в виде файла для программы Visio с Web-страницы этой книги на Web-сервере Издательского дома "Вильяме"поадресу:www .williamspublishing.com.
НА ЗАМЕТКУ :
Не следует путать процесс создания схемы базы данных с процессом создания npo- граммного обеспечения. В большинстве компаний по созданию программного обес- ; печения используется методология, которая регламентирует решаемые бизнес- задачи, внешний вид программного обеспечения и способ его создания. Их нужно учитывать при разработке базы данных.
Отношения
Отношение— это способ формального определения ТОГО, как две таблицы связаны друг с другом. При определении отношения необходимо сообщить процессору баз данных, через какие два поля связываются две таблицы, участвующие в создании отношения.
РИС.Схема базы данных с отношениями междутаблицами
Полями, создающими отношение, являются первичный ключ (представленный выше в этой главе) и внешний (foreign) ключ. Внешний ключ — это ключ в связанной таблице, который хранит копию первичного ключа из основной таблицы.
Предположим, у вас есть таблицы с характеристиками отделов и сотрудников компании. Отношение между отделом и группой сотрудников можно определить типом один-ко-многим. Каждому отделу присваивается собственный идентификационный номер (ID), и каждый сотрудник имеет свой ID. Но, чтобы указать, в каком отделе работает каждый сотрудник, необходимо сделать копию номера ID отдела в каждой записи, содержащей данные о сотруднике. Итак, чтобы идентифицировать каждого со-
трудника как члена некоторого отдела, в таблице Employees (Сотрудники) должно быть предусмотрено поле (именуемое, допустим, Department ID) для хранения ID отдела, в котором работает данный сотрудник. Поле Department ID в таблице Employees служит внешним ключом таблицы Employees, поскольку хранит копию первичного ключа таблицы Departments (Отделы).
Благодаря отношению процессор баз данных "знает", какие две таблицы участвуют в этом отношении и какой внешний ключ связан с первичным ключом. Для прежнего процессора Jet базы данных Access явное объявление отношений не обязательно, но это в ваших же интересах, поскольку при таком объявлении упрощается задача выборки данных из записей двух или нескольких связанных таблиц (подробнее об этом в главе 2, "Запросы и команды на языке SQL"). Отсутствие такого объявления — один из основных недостатков технологии Jet, который можно устранить с помощью переноса унаследованных приложений на основе технологии Jet на платформу ADO.NET. Помимо соответствия связанных записей в отдельных таблицах, отношение определяется для того, чтобы воспользоваться преимуществами ссылочной целостности, под которой понимают свойство процессора базы данных, обеспечивающее непротиворечивость информации, хранимой в многотабличной базе данных. Когда ссылочная целостность имеет место в базе данных, то процессор баз данных препятствует удалению записи в случае, если в базе данных существуют другие записи, связанные с ней.
После того как вы определите отношение в базе данных, это определение сохраняется до тех пор, пока вы не удалите его. Отношения можно создавать графически, используя инструменты Visual Studio .NET, SQL Enterprise Manager, Visio, или с помощью сценариев на языке DDL.
Использование ссылочной целостности для поддержания непротиворечивости данных
Когда таблицы связаны между собой посредством отношений, данные в каждой из связанных таблиц должны оставаться согласованными друг с другом. Ссылочная целостность справляется с этой задачей, отслеживая отношения между таблицами и запрещая выполнение определенных типов операций над записями.
Допустим, у вас есть таблицы tblCustomer и tblOrder. Эти две таблицы связаны через общее поле ID.
Предполагается, что сначала вы регистрируете клиентов в таблице tblCustomer, а затем создаете записи с информацией о заказах в таблице tblOrder. Но что произойдет, если запустить процесс, удаляющий запись с данными о клиенте, который оформил заказы, зарегистрированные в таблице tblOrder? А если создать заказ, для которого не существует действительного значения поля CustomerlD? Любой заказ без значения поля CustomerlD не будет отгружен, поскольку адрес отгрузки представляет собой функцию от записи в таблице tblCustomer. Когда данные в связанных таблицах страдают от такого рода проблемы, их называют несогласованными или противоречивыми.
Поскольку очень важно, чтобы база данных не стала противоречивой, во многих процессорах баз данных (включая SQL Server) предусмотрен способ определения
формальных отношений между таблицами. При формальном определении отношения между двумя таблицами процессор баз данных отслеживает это отношение и препятствует выполнению любой операции, которая "покушается" на ссылочную целостность.
Ссылочная целостность обеспечивается путем генерирования ошибок при выполнении действия, которое могло бы оставить данные в противоречивом состоянии. Например, в базе данных с активизированной ссылочной целостностью при попытке создать заказ, содержащий идентификационный номер (ID) клиента, которого на самом деле не существует, вы получите сообщение об ошибке и "подозрительный" заказ не будет создан.
Проверка ограничений ссылочной целостности с помощью Server Explorer
Для проверки отношения между таблицами tblCustomer и tblOrder попробуем использовать окно Server Explorer среды Visual Studio .NET. Для этого выполните перечисленные ниже действия.
Откройте схему базы данных Novelty с двумя таблицами — tblCustomer и tblOrder. Обратите внимание на то, что эта схема содержит и другие таблицы, но в данном случае нас интересует отношение между этими таблицами.
Щелкните правой кнопкой мыши на линии отношения между двумя этими таблицами и из контекстного меню выберите команду Property Pages.
После появления на экране страницы свойств данного отношения выберите вкладку Relationships, в которой указаны поле ID таблицы tblCustomer, поле CustomerID таблицы tblOrder и ограничения ссылочной целостности в нижней части.
НА ЗАМЕТКУ j
По умолчанию при создании отношения задаются ограничения ссылочной целостности (например, нельзя создать заказ для несуществующего клиента), но не заданы условия каскадного удаления. Более подробно эти условия рассматриваются далее.
Установите флажок Enforce Relationship for INSERTS and UPDATES (Применять каскадное обновление и удаление), а затем щелкните на кнопке Close.
Для сохранения внесенных изменений выберите команду File^Save Relationships.
Для проверки заданного ограничения выполните перечисленные ниже действия.
В окне Server Explorer щелкните правой кнопкой мыши на таблице tblOrder и выберите из контекстного меню команду Retrieve Data from Table.
Введите заказ для клиента. с идентификатором 9999, которого на самом деле нет в таблице с данными о клиентах.
Перейдите в другую строку для автоматического сохранения введенного заказа.
В результате на экране появится диалоговое окно с предупреждением: INSERT
47
statement conflicted with COLUMN FOREIGN KEY constraint ' FK_ tblOrder_tblCustomer'. Conflict occurred in database 'Novelty', table 'tblCustomer', column 'ID'. The statement has been terminated. (Команда INSERT конфликтует с ОГРАНИЧЕНИЕМ ПО ВНЕШНЕМУ КЛЮЧУ ‘FK_tblOrder_tblCustomer’. Конфликт произошел в базе данных ‘Novelty’, таблице ‘tblCustomer’, поле ‘ID’. Выполнение команды прекращено.)
Щелкните на кнопке ОК в окне с предупреждением и отмените команду вставки новой записи с помощью клавиши <Esc>.
В данном случае наша цель заключалась не во вводе нового заказа, а в демонстрации сообщения об ошибке. Однако если вам действительно нужно создать заказ, то в таком случае следует создать запись для клиента, получить его идентификатор ID и использовать его в поле CustomerlD при создании заказа.
В рабочем приложении эта проблема обычно решается автоматически с помощью специально созданного пользовательского интерфейса. Далее в книге рассматривается несколько стратегий согласованного управления связанными данными.
Каскадные обновления и каскадные удаления
Каскадные обновления и каскадные удаления — весьма полезные свойства процессора баз данных SQL Server. И вот почему.
Каскадные обновления. При изменении значения первичного ключа таблицы связанные данные во внешних ключах, относящихся к этой таблице, также изменяются, отражая изменения в первичном ключе. Следовательно, если вы измените идентификатор ID клиента Хокки Марта (Hockey Mart) в таблице tblCustomer с 48 на 72, то значение поля CustomerlD всех заказов, сгенерированных этим Хокки Мартом в таблице tblOrder, автоматически изменится с 48 на 72. В рабочем приложении редко приходится изменять значения ключа (основная концепция заключается в том, что ключ должен быть уникальным и неизменным), но если это все-таки приходится делать, то все каскадные обновления этого ключа во внешних ключах будут выполнены автоматически.
Каскадные удаления. При удалении записи в таблице все записи, связанные с этой записью в соответствующих таблицах, автоматически удаляются. Следовательно, если вы удалите запись для Хокки Марта в таблице tblCustomer, все записи в таблице tblOrder для клиента Хокки Марта автоматически удаляются.
НА ЗАМЕТКУ
Устанавливая отношения, выполняющие каскадные обновления и удаления в базе | данных, следует проявлять определенную осторожность. При недостаточной бдительности можно допустить удаление (или обновление) большего объема данных, чем ожидалось. Некоторые разработчики базы данных вообще отказываются от каскадного обновления и удаления, предпочитая явное управление ссылочной целост- i ностью данных среди связанных таблиц. Однако, более внимательно изучив особенности каскадного обновления и удаления, можно убедиться в том, что они довольно легко программируются.
Каскадные обновления и удаления работают только в том случае, если вы установили отношение между двумя таблицами. Если вы всегда создаете таблицы с первичным ключом типа AutoNumber (Счетчик), (или первичными ключами типа AutoIncrement в терминах SQL Server), то каскадные удаления для вас окажутся более полезными, чем каскадные обновления, поскольку вы не в силах изменить значение поля типа AutoNumber или поля Autolncrement (т.е. нет обновлений — нечего и тиражировать).
Для проверки возможностей каскадного удаления с помощью окна Server Explorer выполните перечисленные ниже действия.
Убедитесь в том, что между таблицами tblCustomer и tblOrder задано отношение с каскадным удалением. (Для проверки или указания этого ограничения воспользуйтесь схемой базы данных.)
Создайте запись о новом клиенте, щелкнув правой кнопкой мыши на таблице tblCustomer узла Tables в окне Server Explorer, выбрав в контекстном меню команду Retrieve Data from Table и введя необходимые данные. Запомните идентификатор ID, присвоенный процессором базы данных новому клиенту, потому что он потребуется для создания заказов данного клиента. Пусть эта таблица остается открытой, потому что она нам еще понадобится чуть позже.
Откройте таблицу tblOrder и создайте 2-3 заказа для нового клиента. Для этого укажите в поле CustomerlD идентификатор ID, присвоенный процессором базы данных новому клиенту. Пусть эта таблица также остается открытой.
Вернитесь к таблице tblCustomer и попытайтесь удалить запись с данными этого клиента, щелкнув правой кнопкой мыши на левом конце записи и выбрав из контекстного меню команду Delete.
После этого на экране появится диалоговое окно с предупреждением и просьбой подтвердить удаление данных. Щелкните на кнопке Yes.
Вернитесь к таблице tblOrder, и вы обнаружите, что заказы этого клиента не удалены. Что произошло? На самом деле они удалены, но дают устаревшее представление данных. Для его обновления нужно выбрать команду меню QueryORun (Запрос^Запуск). После этого вид таблицы будет обновлен и связанные заказы данного клиента будут удалены благодаря параметрам каскадного удаления.
Нормализация
Нормализация— это тесно связанное с отношениями понятие, которое означает устранение противоречий и повышение эффективности базы данных.
Базы данных считаются противоречивыми, если данные одной таблицы не соответствуют данным другой. Например, если ряд ваших сотрудников считают, что Арканзас находится на западе, а остальные — что на юге, при этом те и другие выполняют ввод данных, опираясь только на свои знания, то отчеты о состоянии дел на западе будут недостоверными.
Неэффективная база данных, как правило, не позволяет выделять именно те данные, которые вам требуются. Если база данных хранит всю информацию в одной таблице, вы можете просто потерять самообладание, пока найдете в ней нужный теле-
фонный номер. С другой стороны, полностью нормализованная база данных хранит каждую частицу информации в отдельной таблице и уникальным образом идентифицирует ее собственным первичным ключом. Нормализованные базы данных позволяют ссылаться на любую частицу информации в любой таблице, используя первичный ключ.
При проектировании и инициализации базы данных нужно решить, как ее нормализовать. Обычно все, что связано с приложением базы данных (от структуры таблиц до структуры запросов, от пользовательского интерфейса до поведения отчетов), вытекает из характера нормализации вашей базы данных.
НА ЗАМЕТКУ ;;
Как разработчику баз данных, вам еще придется столкнуться с базами данных, кото! рые не нормализованы по той или иной причине. Недостаток нормализации может : бЫТЬ намеренным (например, для достижения более высокой производительности) или оказаться результатом неопытности либо небрежности разработчика базы дан-
I ных. В любом случае, если вы собираетесь нормализовать существующую базу дан- 1 ных, вам нужно сделать это как можно раньше (поскольку все остальное в разработке базы данных зависит от структуры ее таблиц). Кроме того, для приведения в порядок базы данных с недостаточно продуманной структурой можно использовать команды языка определения данных. Они позволяют переносить Данные из одной таблицы в
I другую, а также добавлять, обновлять и удалять из таблиц записи, отвечающие заданному критерию.
В качестве примера выбора варианта нормализации можно создать проект базы данных и рассмотреть требование, выдвинутое Брэдом Джонсом в бизнес- ситуации 1.2. Ему требуется способ хранения названия штата, в котором проживает клиент, а также информации о регионе страны, к которому относится этот штат. Начинающий разработчик может создать одно поле для хранения названия штата, а второе — для названия региона страны.
tblCustomer
ID FirstName LastName Address Company City State PostalCode Phone Fax Email Region
Эта структура первоначально может показаться рациональной, однако посмотрим, что произойдет, если кто-нибудь постарается ввести данные в приложение, основанное на этой таблице.
Если бы вы вводили обычную информацию о клиенте — имя, адрес и т.д., то после ввода названия штата вам бы пришлось задуматься, чтобы определить регион проживания данного клиента. Где же находится этот Арканзас — на западе или на юге? Где находится клиент с Виргинских островов? Если существует большая вероятность ошибки, то такого рода решения не стоит оставлять в руках операторов, занимающихся вводом данных, даже несмотря на их высокую квалификацию. Если же полагаться только на человеческую память, то рано или поздно ваши данные станут противоречивыми. А чтобы защитить данные от противоречивости, и следует обращаться к нормализации.
Вместо того чтобы при регистрации каждого нового клиента возлагать процесс принятия решения на операторов ввода, лучше хранить информацию, связанную с регионами, в отдельной таблице. Эту таблицу можно было бы назвать tblRegion; структура ее совсем проста.
tblRegion
ID
State
Region
Данные в этой таблице выглядели бы следующим образом:
State
Region
АК
Север
AL
Юг
AR
Юг
AZ
Запад
В этой усовершенствованной версии структуры базы данных для выборки информации о регионе вам придется выполнить двухтабличный запрос с объединением двух таблиц— tblCustomer и tblRegion, причем из одной таблицы вы получите информацию о штате, а из другой — о регионе (на основании информации о штате). В объединениях сопоставляются записи, имеющие общие поля, в отдельных таблицах. (Более подробно объединения описываются в главе 2, "Запросы и команды на языке SQL”.) Хранение информации о регионах в отдельной таблице имеет ряд преимуществ.
Если вы решили выделить новый регион на основе существующего, то для отражения рождения нового региона проще изменить всего несколько записей в таблице tblRegion, чем тысячи записей в таблице tblCustomer.
Если вы расширите свой бизнес за пределы 50 штатов, то для отраженияизмене- ний в бизнесе достаточно опять-таки добавить новый регион. Для этого вам понадобится внести в таблицу tblRegion всего по одной записи для каждой новой области, и эта новая запись немедленно станет доступной для всей системы.
Если обнаружится необходимость использования принципов регионального
деления в других задачах, решаемых на основе этой базы данных (например, для обозначения того, что офис по продажам, расположенный В определенном штате, обслуживает определенный регион), то вы могли бы с успехом использовать таблицу tblRegion без модификаций.
Отсюда вытекает, что для отдельных категорий информации следует всегда ориентироваться на создание отдельных таблиц. Во время разработки структуры базы данных (еще до реального построения самой базы данных) необходимо хорошо продумать, какие таблицы вам нужны и как они будут связаны друг с другом. Создание схемы базы данных (см. раздел о создании схемы базы данных выше в этой главе) является частью этого важного процесса.
Отношения типа один-к-одному
Предположим, в вашей базе данных есть таблицы, в которых хранится информация о сотрудниках и видах работ. Если каждому служащему назначается один вид работы, то отношение между сотрудниками и видами работ можно определить типом один-к-одному, поскольку для каждого сотрудника в базе данных существует только один вид работы. Это простейший тип отношений как для понимания, так и для реализации, поскольку в таких отношениях таблица обычно занимает место поля в другой таблице, причем поля, участвующие в отношении, легко идентифицировать.
Однако это не самый распространенный тип отношений в функционирующих приложениях ведения баз данных. Тому есть две причины.
Почти всегда можно выразить отношение типа один-к-одному без использования двух таблиц. При этом быстродействие только повысится, хотя будет утрачена гибкость, предоставляемая хранением связанных данных в отдельной таблице. В предыдущем примере вместо создания отдельной таблицы с данными о видах работ можно поместить все поля, связанные с работой, в таблицу, предназначенную для хранения данных о сотрудниках.
Выражение отношения один-ко-многим почти такое же простое для понимания (но гораздо более гибкое), как выражение отношения один-к-одному, поэтому сразу же переходим к следующему разделу.
Отношения типа один-ко-многим
Гораздо чаще, чем отношения типа один-к-одному, в базах данных используются отношения типа один-ко-многгш, в которых каждая запись таблицы связана с одной или несколькими записями в другой таблице (или вообще не связана ни с какими записями). В созданной ранее схеме базы данных между клиентами и заказами задано отношение один-ко-многим. Каждый клиент может иметь один или несколько заказов (или вообще не иметь заказов), поэтому между таблицами tblCustomers и tblOrder существует отношение один-ко-многим.
Напомним, что для реализации такого отношения в базе данных копируется первичный ключ из таблицы на стороне "один" в таблицу на стороне "многие". В пользовательском интерфейсе для ввода данных этот тип отношения обычно имеет вид формы с основной (master) и подчиненной (slave) частями, в которой одна основная
запись отображается со своими подчиненными записями в отдельной части формы. В пользовательском интерфейсе первичный ключ из одной таблицы обычно связывается с внешним ключом из связанной таблицы с помощью списка или поля со списком.
Отношения типа многие-ко-многим
Отношение типа многие-ко-многим по сравнению с отношением один-ко-многим идет еще дальше. В качестве классического примера отношения типа многие-ко-многим можно привести отношение между студентами и классами. Каждый студент может иметь много классов, а каждый класс — много студентов. (Конечно же, возможны варианты, когда класс будет состоять из одного студента или в нем вовсе не будет ни одного учащегося, а также вполне возможно для студента иметь только один класс или ни одного.)
В данном бизнес-примере отношение задается между заказами и позициями заказа, т.е. каждый заказ может содержать несколько позиций, а каждая позиция может присутствовать в нескольких заказах.
Чтобы установить отношение многие-ко-многим, необходимо иметь три таблицы: две для хранения реальных данных и третью (именуемую соединительной) для хранения отношения между двумя первыми таблицами. Таблица соединения обычно состоит только из двух внешних ключей — по одному из каждой связанной таблицы, хотя иногда в таблице соединения полезно использовать собственное поле с идентификаторами для предоставления доступа к записям таблицы с помощью программных средств.
В качестве примера отношения многие-ко-многим можно модифицировать пример из предыдущего раздела таким образом, чтобы в базе данных хранилось несколько позиций, связанных с одним заказом, т.е. у каждого заказа было много позиций и каждая позиция относилась к неограниченному количеству заказов. В этом случае таблицы могут выглядеть так, как показано на рис. 1.13.
РК•Name Price DescriptionРК•Name Price DescriptiontbIOrderltemPKID.FK1FK2OrderlD ItemID Quantity CosttbIOrderltemPKID.FK1FK2OrderlD ItemID Quantity CosttbIOrderPKШCustomerlD OrderDate AmounttbIOrderPKШCustomerlD OrderDate Amount
РК
•
Name Price Description
РК
•
Name Price Description
tbIOrderltem
PK
ID.
FK1
FK2
OrderlD ItemID Quantity Cost
tbIOrderltem
PK
ID.
FK1
FK2
OrderlD ItemID Quantity Cost
tbIOrder
PK
Ш
CustomerlD OrderDate Amount
tbIOrder
PK
Ш
CustomerlD OrderDate Amount
РИС. 1.13. В этой группе тайлии,, участвующих в отношении многие-ко-многим, tbIOrderltem является таблицей соединения
Создание пользовательского интерфейса на основе Windows Forms
Разработчики предыдущих версий Visual Basic первыми предложили концепцию связывания данных, согласно которой связанный с данными объект или элемент управления данными (data control) позволяет программистам с минимальными усилиями
53
создавать простые, связанные с данными пользовательские интерфейсы. В Visual Basic .NET эта концепция также поддерживается, а многие недостатки прежней версии устранены.
В прошлом разработчик мог установить связь между формой Visual Basic и базой данных с помощью элементов управления данными. Они предоставляют основные функции просмотра данных, позволяя приложению манипулировать наборами данных, вводить и обновлять их.
На платформе .NET операциями подключения к базе данных и извлечения данных управляет автоматически созданный код, что позволяет добиться ряда преимуществ.
Автоматически созданный код, в отличие от абстрактного элемента управления данными, можно просматривать, поэтому он позволяет в большей степени контролировать способы доступа к данным.
Изучая автоматически созданный код, программист может познакомиться с классами платформы .NET, предназначенными для доступа к данным, что особенно полезно для тех, кто не имеет опыта работы на платформе .NET.
Основное назначение элементов управления данными в прежней версии Visual Basic — это подключение к базе данных, создание запроса к ней и управление данными. Теперь эти функции распределены между несколькими объектами, каждый из которых можно отдельно конфигурировать и использовать.
В приведенных ранее примерах создана база данных, которая вполне подходит для ознакомления с основными принципами создания связанного с данными пользовательского интерфейса. В следующих разделах демонстрируются способы создания связанных с базой данных приложений на основе Windows Forms.
Подключение к базе данных и работа
с записями
Нет ничего проще, чем создать приложение на основе Windows Forms. И в этом заявлении нет никакого преувеличения; более того, если вас интересует лишь просмотр содержимого базы данных, вам вообще не придется писать ни единой строки кода. Весь процесс состоит из двух этапов: подключение к базе данных и связывание последнего пользовательского интерфейса с источником данных, генерированным Visual Studio .NET. Для этого выполните перечисленные ниже действия.
В Visual Studio .NET создайте новый проект на основе Windows Forms и откройте новую форму Forml.
В окне Server Explorer найдите созданную ранее таблицу tblCustomer и перетащите ее из окна Server Explorer в форму Forml.
После этого в нижней части окна с формой Forml появятся объекты SqlCon- nectionlиSqlDataAdapterl.
Для извлечения и отображения данных используются три объекта: объект SqlConnectionl создает подключение к базе данных, объект-адаптер SqlDataAdapterl извлекает данные, а объект DataSet сохраняет данные, извлеченные адаптером SqlDataAdapterl. Для создания объекта DataSet выполните следующее.
Выберите команду меню Datao Generate Dataset (Данные1^ Генерация набора данных), и на экране появится диалоговое окно Generate Dataset.
Воспользуйтесь всеми заданными по умолчанию параметрами и щелкните на кнопке ОК. В результате будет создан новый объект DataSetll, который будет располагаться в нижней части окна под формой Formi возле объектов SqlCon- nectionlиSqlDataAdapterl.
Для просмотра данных в форме создайте в форме элемент управления пользовательского интерфейса и свяжите его с только что созданным объектом DataSetll, выполнив перечисленные ниже действия.
Откройте панель элементов управления Toolbox с помощью команды меню View^ToolboX (ПросмотрФПанель инструментов управления), перейдите во вкладку Windows Forms и найдите элемент управления DataGrid (Сетка данных). Перетащите его в форму Forml, и в ней появится экземпляр DataGridl объекта DataGrid.
Свяжите этот элемент управления с источником данных. Для этого с помощью команды меню View^ Properties Window (Просмотри Окно свойств) откройте окно свойств Properties и выберите для свойства DataSource (Источник данных) сетки DataGridl источник данных DataSetll. Затем выберите для свойства DataMember (Элемент данных) сетки DataGridl таблицу tblCustomer.
Наконец, создайте код извлечения данных из базы данных и вставки их в сетку DataGridl. Для этого дважды щелкните на форме, и в окне просмотра кода автоматически появится процедура Forml_Load. Введите в ней следующий код: Private Sub Forml_Load(ByVai sender As System.Object,_
В у Vai e As System.EventArgs) Handles MyBase.Load
SqlDataAdapterl.Fill(DataSetll)
End Sub
Запустите полученное приложение с помощью команды меню Debugs Start (Отладка1^ Запуск), и в окне приложения будут отображены данные из таблицы tblCustomer.
Здесь следует отметить одну особенность данного приложения. Все внесенные в нем изменения данных не будут отражены и сохранены в базе данных. Для их сохранения нужно создать дополнительный код вызова метода объекта DataAdapter. Эта тема рассматривается далее, в разделе об обновлении записей.
Создание приложения для просмотра данных
В предыдущем примере показан простейший способ связывания данных на основе извлечения всей таблицы и отображения ее в элементе управления DataGrid. А как отобразить только одну запись? Для этого потребуется использовать элементы управления TextBox и Button, а также создать дополнительный код.
Чтобы создать приложение для просмотра данных по одной записи из таблицы tblCustomer, выполните ряд действий.
В Visual Studio .NET создайте новый проект на основе Windows Forms и откройте новую форму Forml. Создайте в ней два текстовых поля, txtFirstName и txtLastName, на основе элемента управления TextBox.
Создайте объекты SqlConnection, SqlDat Adapter и DataSet для извлечения данных о клиентах из таблицы tblCustomer. (Необходимые для этого действия аналогичны действиям из предыдущего примера.) Как и прежде, не забудьте вызвать метод Fill объекта SqlDataAdapter в коде для инициализации объекта DataSet. (В данном примере объект DataSet имеет имя DsCustomerl. — Прим. ред.)
Теперь нужно создать связь между двумя текстовыми полями (txtFirstName и txtLastName) и соответствующими полями в базе данных. Для этого щелкните на текстовом поле txtFirstName и в группе свойств Data выберите подгруппу свойств (DataBindings). Это свойство содержит несколько СВОЙСТВ, которые следует установить для связывания данных таблицы с текстовым полем.
Выберите поле FirstName таблицы tblCustomer для свойства Text текстового поля txtFirstName. Для этого щелкните в правой части поля со списком возле свойства Text. Выберите набор данных DsCustomerl, таблицу tblCustomer и поле FirstName, как показано на рис. 1.14.
РИС. 1.14.Создание связи между данными из поля базы данных и текстовым полем с помог^ъю свойств (DataBindings)
Аналогично свяжите текстовое поле txtLastName с полем LastName таблицы tblCustomer.
Запустите приложение, в текстовых полях которого будут отображены имя и фамилия первого клиента.
Возможности этого приложения весьма ограниченны, потому что в нем можно просматривать только по одной записи и нельзя редактировать данные. Однако оно является базовым приложением, на основе которого будут созданы несколько других примеров с более широкими возможностями рабочего приложения для полномасштабной работы с базами данных.
Даже в таком ограниченном примере очевидны преимущества способов связывания данных на платформе .NET: они более гибки, чем аналогичные способы в Visual Basic 6. Например, упомянутая гибкость достигается за счет способности управлять процессом связывания с помощью кода.
Попробуем теперь создать код для перехода от одной записи к другой с помощью перечисленных ниже действий.
Создайте две кнопки, btnNext и btnPrevious, для перехода к следующей и предыдущейзаписям.
Дважды щелкните на кнопке btnNext и в автоматически появившемся окне редактирования кода с определением процедуры btnNext_Click вставьте сле- дующийкод:
Private Sub btnNext—Click(ByVai sender As System.Object, _ ByVai e As System.EventArgs) Handles btnNext.Click
Me.Bindingcontext(DsCustomerl, "tblCustomer").Position += 1 End Sub
Дважды щелкните на кнопке btnPrevious и в автоматически появившемся окне редактирования кода с определением процедуры btnPrevious_Click вставьте код
Private Sub btnPrevious_Click(ByVai sender As System.Object, _ ByVal e As System.EventArgs) Handles btnPrevious.Click
Me.Bindingcontext(DsCustomerl, "tblCustomer").Position —= \ End Sub
Снова запустите приложение и убедитесь в том, что с помощью созданных кнопок можно переходить к следующей и предыдущей записям. (Учтите, что эта программа будет работать только при наличии нескольких записей в таблице.)
Объект BindingContext предоставляет средства организации перехода к другим записям в приложении для работы с данными. При создании таких приложений в предыдущих версиях Visual Basic для организации переходов к другим записям требовалось использовать элемент управления Data. На платформе .NET выделен специальный объект BindingContext, который отвечает за связывание данных. (Иными словами, выделение небольшого специализированного объекта из более крупного общего объекта позволяет распределить специализированные функции среди нескольких объектов меньшего размера.) В объектно-ориентированном программировании разработчик стремится создавать специализированные объекты, чтобы упростить структуру программы и сделать ее более гибкой.
Таким образом, при создании приложения, рассчитанного на работу с базами данных, для составления запросов, обновления данных, связывания элементов управления пользовательского интерфейса с данными и перехода к полям таблицы не рекомендуется использовать один громоздкий объект Data. Вместо него в Windows Forms и ADO.NET предусмотрено несколько отдельных специализированных объектов. Выделение функций доступа к данным — ключевое достоинство платформы .NET Framework (этот вопрос подробно рассматривается в следующих главах).
Объект BindingContext является членом семейства объектов Windows Forms (а точнее, членом пространства имен System.Windows. Forms платформы .NET Framework) и содержит множество полезных свойств и методов. Например, объект
57
BindingContext можно использовать для определения количества записей в источнике данных так, как описано ниже.
Создайте ярлык IblDataStatus с помощью элемента управления Label с пустой строкой в свойстве Text.
В коде формы создайте подпрограмму ShowDataStatus с указанным ниже кодом, которая будет отображать текущее расположение записи и общее количество записей в ярлыке IblDataStatus.
Private Sub ShowDataStatus()
With Me.bindingcontext(DsCustomerl, "tblCustomer")
IblDataStatus.Text = "Record "s . Position + 1 & " of " & .Count End With
End Sub
Поместите вызов этой подпрограммы ShowDataStatus () в подпрограммы обработки событий загрузки формы (Forml_Load) и щелчков мыши на обеих кнопках (btnNext_Click и btnPrevious_Click). Это позволит отображать обновленную информацию о текущем количестве записей и текущей записи при загрузке формы и после каждого перемещения к другой записи. Учтите, что отсчет текущего номера записи (свойство Position объекта DataBind- ings) начинается с нуля (как и во всех коллекциях на платформе .NET). Поэтому для получения реального номера записи следует прибавить к нему 1.
Запустите приложение и попробуйте перейти к разным записям таблицы. Тогда в ярлыке будет отображено общее количество записей в таблице и номер текущей записи.
Программный способ связывания данных
С помощью Windows Forms связывание данных можно организовать программно. Это позволяет добиться более высокой гибкости в ситуациях, когда расположение полей неизвестно во время создания приложения либо требуется явно выразить связь между элементами управления и полями другим способом, чем предлагается в интегрированной среде разработки.
Чтобы организовать связь с элементами управления пользовательского интерфейса, следует создать метод Add объекта DataBindings элемента управления Windows Forms. В листинге 1.1 показан типичный способ создания связи с данными в приложении для работы с базами данных.
Листинг 1.1. Программный способ очистки и установления связи с данными
Private Sub Forml_Load(ByVai sender As System.Object, ByVai e As
System.EventArgs) Handles MyBase.Load
txtFirstName.DataBindings.Clear()
txtLastName.DataBindings.Clear()
txtFirstName.DataBindings.Add("Text", DsCustomerl,
"tblCustomer.LastName")
txtLastName.DataBindings.Add("Text", DsCustomerl,
"tblCustomer.LastName")
sqlAdapterl.Fill(DsCustomerl)
ShowDataStatus()
End Sub
(Убедитесь в том, что для свойства ConnectionString объекта SqlConnectionl задана верная строка подключения с используемым вами сервером SQL Server. Дело в том, что в коде этого примера, который можно скопировать по адресу: http:// www.williamspublishing.com, указана строка подключения к серверу SQL Server, установленному на компьютере ROCKO автора книги. — Прим. ред.)
Обратите внимание, что вызовы метода Clear элементов управления коллекции DataBindings не обязательно создавать в разрабатываемом приложении. Они нужны в этом случае, потому что связь с данными ранее задана с помощью окна Properties.
Метод Add коллекции DataBindings принимает три параметра: свойство элемента управления, с которым связываются данные; объект источника данных (обычно, но не обязательно объект DataSet), ссылка на член источника данных, который предоставляет данные. После запуска приложения с приведенным выше кодом для метода Load в поле с именем клиента будет отображена его фамилия, а в поле с фамилией — его имя.
Элементы управления, взаимодействующие с данными
Элементом управления, взаимодействующим с данными (data-aware control), может быть любой элемент управления, имеющий свойство-коллекцию DataBindings. С помощью этого свойства можно ссылаться на любой тип данных, включая реляционные источник данных.
Свойство DataBindings соединяет элемент управления пользовательского интерфейса с элементом управления данными (т.е. именно так происходит связывание пользо вательского интерфейса с базой данных). Поэтому говорят, что элемент управления пользовательского интерфейса связан с базой данных через элемент управления данными.
В предыдущих версиях Visual Basic с источником данных можно было связать относительно небольшое количество элементов управления пользовательского интерфейса. Возможности манипулирования связанными с данными элементами управления были довольно ограниченными: пользователь мог связать их с теми источниками данных, для которых существует провайдер данных ADO. Для взаимодействующих с данными элементов управления разработчику приходилось создавать рутинный код большого размера для выполнения вручную всех операций связывания данных. На платформе .NET практически каждый элемент управления Windows Forms может быть связан с данными, включая сложные элементы управления, например TreeView. Более того, разработчик не ограничен только реляционными источниками данных или известными среде Visual Studio .NET или ADO.NET. Любой объект, реализующий интерфейс IList, может быть связан с данными, включая наборы данных DataSet и более сложные конструкции, например массивы и коллекции.
Обновление записей в приложении просмотра данных
До сих пор в приведенных ранее примерах нам удавалось только извлекать и просматривать данные. А изменять данные можно было только в элементах пользовательского интерфейса, но их нельзя было сохранить (зафиксировать) в базе данных.
59
Интуитивно понятно, что изменения в связанном с данными элементе управления пользовательского интерфейса должны автоматически сохраняться в базе данных. Именно так работают различные связанные с данными элементы управления пользовательского интерфейса в прежних версиях Visual Basic. Почему же в Windows Forms на платформе .NET связывание с данными организовано иначе?
Принудительная фиксация обновлений в источнике данных с помощью дополнительной строки кода продиктована требованиями гибкости и более высокой производительности. Рассмотрим принцип работы объекта DataSet на платформе .NET.
На рис. 1.15 показана схема взаимосвязи между формой, объектом DataSet и базой данных в приложении на основе Windows Forms.
РИС. 1.15. Схема взаимосвязи между связанной формой, объектом Da taSet и базой данных
В созданном ранее приложении все данные в исходном состоянии находятся в базе данных. Затем они извлекаются и сохраняются в памяти в объекте DataSet. Форма содержит элементы управления, связанные с полями таблицы в объекте DataSet. Форма обнаруживает появление новых данных и автоматически отображает содержимое полей в связанных с данными элементах управления.
В связанном с данными приложении изменение содержимого связанного с данными элемента управления влияет только на объект DataSet, т.е. изменение содержимого текстового поля приводит к изменению содержимого записи в таблице, которая находится в объекте DataSet. Но изменение объекта DataSet не копируется в базу данных, а сохраняется до тех пор, пока не поступит явное указание скопировать их в базу данных (с помощью метода Update набора данных DataSet). Хотя явное включение этой инструкции может показаться излишним (ведь в прежних версиях Visual Basic этого делать было не нужно), но на самом деле оно позволяет добиться более высокой производительности. Дело в том, что в таком случае приложению не нужно постоянно поддерживать соединение с базой данных во время редактирования данных пользователем.
В листинге 1.2 показан пример модифицированных обработчиков событий, которые позволяют редактировать данные в созданном ранее приложении.
Листинг 1.2. Сохранение данных с помощью явного обновления объекта DataSet при перемещении пользователя к другим записям
Private Sub btnNext_Click(ByVai sender As System.Object, _
ByVai e As System.EventArgs) Handles b^nNext.Click
Me.Bindingcontext(DsCustomerl, "tblCustomer") .Position += 1
SqlDataAdapter1.Update(DsCustomer1)
ShowDataStatus()
End Sub
Private Sub btnPrevious_Click(ByVai sender As System.Object, _
ByVai e As System.EventArgs) Handles btnPrevious.Click
Me.Bindingcontext(DsCustomerl, "tblCustomer").Position—= 1
SqlDataAdapterl.Update(DsCustomerl)
ShowDataStatus() End Sub
Конечно, обновлять каждую запись при перемещении пользователя к другим записям совсем не обязательно. Поскольку разработчик может контролировать способ обновления объекта DataSet, можно было бы организовать обновление внесенных изменений с помощью специальной кнопки или команды меню Save. Можно также отложить фиксацию обновлений до окончания редактирования группы строк, т.е. использовать пакетное обновление. BADO.NET для пакетного обновления не нужно создавать какой-либо иной специализированный код, потому что оно выполняется автоматически объектами DataSet (который сохраняет данные в памяти) и SqlDa- taAdapter (который отвечает за выполнение необходимых команд управления базой данных для гарантированного корректного представления, вставки, обновления и удаления данных). Более подробно связь между этими объектами описывается в главах 5, “ADO.NET: объект DataSet", и 6, “ADO.NET: объект DataAdapter”.
Создание новых записей в форме, связанной с данными
Для создания новой записи в связанном с данными приложении на основе Windows Forms нужно использовать метод AddNew объекта BindingContext. При выполнении этого метода любые связанные с данными элементы управления очищаются для ввода новых данных. После ввода новых данных они фиксируются в базе данных с помощью метода Update объекта DataAdapter (как в предыдущем примере).
Для создания новых записей в связанном с данными приложении выполните перечисленные ниже действия.
Создайте в форме новую кнопку с именем btnNew и укажите значение New (Ввести новые данные) для ее свойства Text.
Щелкните дважды на кнопке и введите приведенный ниже код обработки события щелчка на этой кнопке.
Private Sub btnNew_Click(ByVai sender As System.Object, _ByVal e As System.EventArgs) Handles btnNew.Click
Me.Bindingcontext(DsCustomerl, "tblCustomer").AddNewf) txtFirstName.Focus() ShowDataStatus() End Sub
Запустите приложение и щелкните на кнопке New. После очистки текстового поля пользователь сможет ввести в форме новую запись. Для сохранения новой записи нужно перейти к другой записи с помощью кнопок Next или Previous.
Учтите, что кнопки Next или Previous фиксируют обновления объекта DataSet, поэтому в данном примере не нужно использовать явные инструкции обновления объекта DataSet после создания новой записи. В данном случае достаточно просто перейти к другой записи. Но если пользователь закроет приложение до фиксации новых дан-
ных в базе данных (либо неявно с помощью перехода к другой записи, либо явно с помощью метода Update объекта DataAdapter), то новые данные будут утрачены.
Кроме того, пользователю обычно предоставляют возможность отмены внесенных изменений с помощью метода CancelCurrentEdit объекта BindingContext.
Удаление записей из связанной с данными формы
Для удаления записей из связанной с данными формы на основе Windows Forms нужно использовать метод RemoveAt объекта BindingContext. Этот метод принимает один параметр — индекс удаляемой записи. Для организации удаления текущей записи нужно использовать свойство Position в качестве параметра метода Re- moveAt объекта BindingContext, как показано в листинге 1.3.
Листинг 1.3. Удаление данных в приложении для работы с данными с
ПОМОЩЬЮ Метода RemoveAt Объекта BindingContext
Private Sub btnDelete_Click(ByVai sender As System.Object, _
В у Vai e As System.EventArgs) Handles btnDelete.Click
If MsgBox("Whoa bubba, you sure?", MsgBoxStyle.YesNo, _
"Delete record") = MsgBoxResult.Yes Then
With Me.Bindingcontext(DsCustomerl, "tblCustomer") .RemoveAt(.Position)
End With
End If
End Sub
Этот код основан на созданной перед этим кнопке btnDelete. Учтите, что эта процедура запрашивает пользователей, действительно ли они хотят удалить запись. Этот запрос позволяет избежать неприятных последствий в случае, если пользовательский интерфейс создан так, что пользователь может случайно удалить запись, щелкая на кнопке Delete. (Обратите внимание, что, кроме этого способа на основе диалогового окна с предупреждением об удалении записи, можно применять более сложные методы отката ошибочных изменений данных. Однако описание таких сложных конструкций выходит за рамки данной главы.)
Учтите, что метод RemoveAt способен определять и обрабатывать стандартные исключительные ситуации, например при отсутствии данных или после очистки данных в элементе управления пользовательского интерфейса при вызове метода AddNew. Эта возможность позволяет значительно усовершенствовать методы контроля над связанными с данными элементами управления в прежних версиях Visual Basic, для которых требовалось создавать громоздкий код обработки исключительных ситуаций, возникающих при выполнении пользователями непредсказуемых действий.
Проверка введенных данных в форме, связанной с данными
В программировании баз данных проверка введенных данных (validation) гарантирует, что эти данные отвечают правилам, определенным при проектировании приложе-
ния. Эти правила называются правилами проверки данных (validation rules). Один из способов проверки данных при программировании приложения на основе Windows Forms состоит в написании кода для события RowUpdating объекта DataAdapter. Событие RowUpdating возникает как раз перед обновлением записи, а событие Row- Updated — сразу после обновления записи. Размещая код проверки введенных данных в событие RowUpdating, можно быть уверенным в том, что будут обрабатываться любые изменения данных в любой части приложения.
Ключевым фактором эффективного использования события RowUpdating является использование свойств и методов аргумента события, который представлен в виде экземпляра объекта System.Data.SqlClient.SqlRowUpdatingEventArgs. Кроме проверки команды обновления записи (с помощью свойства Command объекта), можно проинформировать адаптер данных DataAdapter об отказе от обновления и откате внесенных изменений. В листинге 1.4 этот подход иллюстрирует рассмотренный ранее пример приложения для просмотра данных.
Листинг 1.4.Построчная проверка введенных данных с помощью события RowUpdating Объекта DataAdapter
Private Sub SqlDataAdapterl_RowUpdating(ByVai sender As Object,
В у Vai e As System.Data.SqlClient.SqlRowUpdatingEventArgs) _ Handles SqlDataAdapterl.Rowupdating
If e.Row.Item("FirstName") = Or=
Then
MsgBox("Change not saved;
customer must have a first and last name.")
e.Status =
e.Row.Rejectchanges()
End If
End Sub
Передача значения UpdateStatus . SkipCurrentRow свойству Status аргумента события позволяет сообщить адаптеру данных о прекращении операции, т.е. отмене обновления данных, потому что оно не прошло проверку. Но недостаточно просто прекратить выполнение операции, потому что в этом случае пользователь получит пустое текстовое поле (и пустое поле в объекте DataSet). Для решения этой проблемы следует вызвать метод Re j ectChanges объекта Row, который содержится в аргументе события. Он обновит содержимое пользовательского интерфейса и сообщит объекту DataSet о том, что больше не нужно согласовывать эту строку с базой данных. После этого можно продолжать редактирование данных, не беспокоясь об их безопасности.
Проверка введенных данных на уровне процессора баз данных
Помимо проверки данных во время ввода информации, следует знать о том, что можно также выполнять проверку и на уровне процессора баз данных. Такая проверка обычно более надежна, поскольку применяется независимо от причины изменения данных. При этом вам не нужно заботиться о реализации правил проверки введенных данных в каждом приложении, которое получает доступ к какой-нибудь таблице. Однако проверка введенных данных на уровне процессора баз данных отличается
меньшей гибкостью, поскольку его практически невозможно переопределить, и часто имеет примитивную форму (обычно ограничивается тем, что не допускает ввод в поля пустых значений). Кроме того, проверку введенных данных на уровне процессора баз данных можно выполнять только на уровне поля, и вы не сможете сделать так, чтобы правила проверки введенных данных, реализуемые процессором баз данных, были основаны на сравнении значений двух полей (если только проверка не реализована на основе ограничения первичный/внешний ключ или реализована в серверной процедуре в виде триггера).
Контроль на уровне процессора баз данных является функцией схемы базы данных. Предположим, вы хотите быть уверены в том, что ни одна запись о клиенте не будет введена в таблицу tblCustomer без указания его имени и фамилии. Тогда установите правило проверки введенных данных на уровне процессора баз данных, выполнив перечисленные ниже действия.
В окне Server Explorer среды Visual Studio .NET откройте схему таблицы tblCustomer.
В столбце Allow Nulls (Допускаются неопределенные значения) снимите флажки FirstName и LastName.
Сохраните схему таблицы tblCustomer с помощью команды меню File^Save tblCustomer.
Теперь никакое программное обеспечение, использующее эту базу данных, не сможет ввести запись о клиенте без указания его имени и фамилии. (Любая попытка приведет к возникновению исключительной ситуации.)
Резюме
Эта глава посвящена основам баз данных в целом, а также простейшим способам соединения приложений Visual Basic .NETдля работы с данными, хранящимися в базе данных SQL Server. Следует учитывать, что правильно составленная схема базы данных может значительно повысить производительность и практичность приложения. Нормализация, ссылочная целостность и индексирование могут оказаться весьма эффективными для достижения этих целей. Однако помните, что чрезмерное индексирование может привести к обратному эффекту и замедлить работу приложения. В следующих главах приводятся примеры бизнес-ситуаций, в которых следует учитывать эти особенности.
Вопросы и ответы
Существует ли в Visual Studio .NET элемент управления Data, который в Visual Basic б можно было успешно использовать для быстрого создания прототипов данных?
Нет. Все функции элемента управления Data, который использовался в Visual Basic 6 и более старых версиях, теперь распределены среди разных объектов данных. Например, соединение с базой данных теперь создается с помощью отдельного объекта SqlConnection, операции извлечения, обновления и удаления данных— с по-
64
мощью объектов BindingContext и DataAdapter, а операции перемещения по записям — с помощью объекта BindingContext. В отличие от прежних объектов для работы с данными, новые объекты не имеют никакого визуального представления во время выполнения, что позволяет разработчику создавать практически любые виды пользовательского интерфейса для работы с данными.
Можно ли первичный ключ составить из нескольких полей?
Да, хотя такие ключи встречаются нечасто. Они называются конкатенированными ключами. Например, если вы составляете конкатенированный первичный ключ из полей, содержащих имя и фамилию, то это значит, что в такой базе данных нельзя зарегистрировать полных "тезок", поскольку каждое сочетание имени и фамилии должно образовывать уникальное значение.
ГЛАВАЗапросы и команды наязыкеSQLОГЛАВАЗапросы и команды наязыкеSQLО
ГЛАВА
Запросы и команды наязыкеSQLО
ГЛАВА
Запросы и команды наязыкеSQLО
В ЭТОЙ ГЛАВЕ...
Что такое запрос
Тестирование запросов с помощью компонента Server Explorer
Отбор записей с помощью предложения SELECT
Указание источника записей с помощью предложения FROM
Формирование критериев
с использованием предложения WHERE
Сортировка результатов с помощью предложения ORDER BY
Отображение первых или последних записей диапазона с помощью предложения ТОР
Объединение связанных таблиц в запросе
Выполнение вычислений в запросах
Определение псевдонимов
с использованием предложения AS
Запросы, которые группируют данные и подводят итоги
Запросы на объединение
Подзапросы
Манипулирование данными с помощью SQL
Использование языка определения данных
Резюме
Вопросы иответы
66
В главе 1, "Основы построения баз данных", где демонстрируется создание базы данных с помощью Visual Studio .NET и SQL Server, вы познакомились со структурой базы данных и ее таблиц. В настоящей главе основное внимание уделяется манипулированию данными в таблицах, а также созданию и модификации структуры таблиц с помощью языка структурированных запросов (Structured Query Language — SQL).
Благодаря запросам SQL пользователь может выбирать записи из таблицы базы данных, находить связанные с ними данные в других таблицах и манипулировать структурой баз данных. Кроме того, запросы SQL вполне применимы для манипулирования базами данных в программах.
Язык SQL представляет собой стандартный способ управления базами данных. Он реализован в различных формах многими производителями в системах реляционных баз данных, включая Microsoft Access и SQL Server, а также системы, созданные другими поставщиками программного обеспечения, например Oracle и IBM. (Язык SQL обязан своим происхождением разработчикам компании IBM.) Как правило, SQL используется для создания запросов, которые извлекают данные из баз данных, хотя множество команд SQL выполняют и другие действия, например создание таблиц и полей.
Команды SQL делятся на две категории:
команды языка определения данных (Data Definition Language - DDL), которые no- зволяют использовать запросы SQL для создания таких компонентов баз данных, как таблицы, поля и индексы;
команды языка манипулирования данными (Data Manipulation Language- DML), которые позволяют извлекать, создавать, удалять и обновлять записи в базе данных.
В этой главе рассматриваются способы использования обеих категорий команд SQL.
Что такое запрос
Запрос (query) — это команда базы данных, осуществляющая выборку записей. Используя запросы, можно получить данные из одного или нескольких полей, принадлежащих одной или нескольким таблицам. При этом данные можно отбирать в соответствии с определенными условиями, называемыми критериями, которые служат для ограничения общего объема отбираемых данных.
Запросы в Visual Basic .NET обычно основаны на SQL. Это стандартный язык для осуществления выборки информации и других операций над базами данных. Он прост в освоении и реализован во многих различных базах данных, поэтому при желании преобразовать свое приложение управления базой данных SQL Server, например, в Sybase или Oracle вам не придется заново изучать совершенно новый язык запросов.
Однако все это теория. А на практике каждый производитель базы данных имеет собственный способ реализации стандарта (так называемый промышленный стандарт), и Microsoft в этом смысле не является исключением. Хотя реализация SQL в СУБД SQL Server радикально не отличается от реализаций прочих производителей, вы должны знать, что существуют и другие диалекты языка SQL. В частности, раз-
работнику с опытом работы с Microsoft Access при знакомстве с СУБД SQL Server придется столкнуться с множеством различий в синтаксисе SQL, которые подробно рассматриваются далее.
Тестирование запросов с помощью компонента Server Explorer
Компонент Server Explorer среды Visual Studio .NET — это полезный инструмент для опробования концепций, описанных в этой главе. С помощью перечисленных ниже действий создайте тестовое представление данных в окне компонента Server Explorer, которое можно будет использовать для тестирования запросов SQL, предлагаемых далее в главе.
Для выполнения примеров данной главы необходимо иметь доступ к SQL Server. (Подробно процесс инсталляции и запуска SQL Server описан в главе 3, "Знакомство с SQL Server 2000”.) Далее в этой главе предполагается, что вы уже установили SQL Server и включили его в окно Server Explorer, как описано в главе 1, Основы построения баз данных".
Для создания тестового представления данных в окне Server Explorer среды Visual Studio .NET выполните ряд действий.
В среде Visual Studio .NET создайте новый проект на основе Windows Forms.
В окне Server Explorer найдите SQL Server и разверните папку созданной ранее базы данных Novelty. Эта папка содержит несколько объектов, например схем баз данных, таблиц и представлений.
Щелкните правой кнопкой мыши на папке с представлениями Views и выберите в контекстном меню команду New View (Новое представление).
В диалоговом окне Add Table (Создать таблицу) выберите таблицу tblCus- tome г и щелкните на кнопке Add (Создать). После этого структура созданной таблицы появится в окне конструктора представления.
Щелкните на кнопке Close для закрытия диалогового окна Add Table. На экране появится окно конструктора представления, состоящего из четырех панелей: структур, полей, запросов SQL и результатов (рис. 2.1).
Отметьте поля FirstName, LastName и Address в таблице tblCustomer. По мере выбора полей для создания запроса будут изменяться панели полей и запросов SQL.
Выберите команду меню Query^Run (Запрос^ Запуск), и нижняя панель результатов выполнения запроса будет иметь вид как на рис. 2.2.
Созданный запрос можно сохранить для последующего использования. Сохраненные в базе данных запросы называются представлениями (views). Их можно использовать точно так же, как обычные таблицы базы данных. Они позволяют упростить работу со сложными базами данных, особенно если запросы включают несколько соединенных таблиц (как будет показано далее в главе).
РИС. 2.1. Диалоговое окно режима создания представленияРИС. 2.1. Диалоговое окно режима создания представленияColumnColumnFirstNameFirstNameLastNameAddressLastNameAddressAliasAlias|Tatfe[Output [Sort Type"tblCustomervtblCustomerVtblCustomerV|Tatfe[Output [Sort Type"tblCustomervtblCustomerVtblCustomerVSELECT FirstName. LastName, Address
РИС. 2.1. Диалоговое окно режима создания представления
РИС. 2.1. Диалоговое окно режима создания представления
Column
Column
FirstName
FirstName
LastName
Address
LastName
Address
Alias
Alias
|Tatfe[Output [Sort Type"
tblCustomerv
tblCustomerV
tblCustomerV
|Tatfe[Output [Sort Type"
tblCustomerv
tblCustomerV
tblCustomerV
FROM novelty.dbo.tblCustomer
■FirstName iLastName |«*№
JohnSmith123 Main Street
JilAzala456 Elm Lane
BradZones789 27th Street
Daisy<leh1184Centre Street
DaveMartin9904 Anywhere Place
Bettytieh4475 Elm Street
ThurstonRyan8339Agate Street
РИС. 2.2. Диалоговое окно режима создания представления после запуска запросаРИС. 2.2. Диалоговое окно режима создания представления после запуска запросаJaneWinters4905 Brown Valley Lane
РИС. 2.2. Диалоговое окно режима создания представления после запуска запроса
РИС. 2.2. Диалоговое окно режима создания представления после запуска запроса
69
Для сохранения представления в среде Visual Studio .NET выберите команду меню File^Save View-!, и Visual Studio .NET предложит ввести новое имя для данного представления. Укажите для него имя qryCustomerList. После этого представление будет сохранено в базе данных и его могут использовать другие разработчики, которым нужно получить доступ к базе данных.
НА ЗАМЕТКУ
j Вы уже, наверное, заметили, что для таблиц и представлений используется простое соглашение об именах, основанное на присвоении префиксов tbl и qry именам j
| объектов базы данных. Тому есть две причины. Во-первых, это упрощает определе- ! ние типа объекта базы данных, потому что в некоторых ситуациях это может быть не • совсем очевидно. Во-вторых, такое соглашение об именах использовалось в преж- i них изданиях этой книги и поддерживается здесь для преемственности.
Наше соглашение об именах уже знакомо программистам, которые имеют опыт ра- | боты с Microsoft Access. Хотя представленные здесь действия выполняются несколь- | ко иначе, чем к тому привыкли программисты, имеющие опыт работы с SQL Server, I мы считаем, что наличие какого-либо соглашения об именах все же лучше, чем его | отсутствие. Конечно, в своей работе вы можете использовать какое-то другое со- j глашение об именах. . ......,
В следующих разделах диалоговое окно режима создания представления будет использовано для создания запросов на выборку данных.
Отбор записей с помощью предложения SELECT
Предложение SELECT является основой каждого запроса, предназначенного для выборки данных. Оно указывает процессору баз данных, какие поля требуется возвратить.
Общая форма предложения SELECT имеет следующий вид:
SELECT *
А его содержание таково: "Вернуть значения всех полей, найденных в указанном источнике записей". Эта форма команды удобна тем, что вам не обязательно знать имена извлекаемых полей таблицы. Однако выборка всех полей таблицы может быть не рациональной, особенно в том случае, когда требуется только два поля, в то время как такой запрос осуществляет выборку двух десятков полей.
Кроме команды, передаваемой процессору базы данных на возврат всех полей источника записей, можно задать конкретный перечень необходимых при отборе полей. Такое ограничение может улучшить эффективность выполнения запроса, особенно для больших таблиц, содержащих много полей, так как в этом случае отбираются только нужные поля.
Ниже приводится пример предложения SELECT, которое извлекает из базы данных значения только двух полей таблицы— FirstName и LastName, содержащих имя и фамилию клиента.
SELECT [FirstName], [LastName]
Обратите внимание также на то, что предложение SELECT не готово к
нию без предложения FROM (поэтому примеры предложения SELECT, приведенные в этом разделе, выполнить в таком виде нельзя). Чтобы полнее ознакомиться с предложениями SELECT, просмотрите примеры использования предложения FROM, приведенные в следующем разделе.
Указание источника записей с помощью предложения FROM
Предложение FROM указывает на источник записей, из которого запрос извлекает записи. Этим источником может быть как таблица, так и другой хранимый запрос. У вас также есть возможность отбора записей из нескольких таблиц, которая подробно рассматривается далее, в разделе об объединении нескольких таблиц в
одном запросе.Предложения FROM работают совместно с предложениями SELECT. Например, чтобы отобрать все записи в таблице tblCustomer, используйте приведенную ниже команду SQL.SELECT *FROM tblCustomerПри выполнении этого запроса считываются все записи и все поля в таблице tblCustomer (без какого-либо упорядочения записей).Чтобы отобрать только имя и фамилию клиентов, воспользуйтесь приведенной ниже командой SQL.SELECT FirstName, LastName FROM tblCustomerПосле изменения запроса в режиме конструктора представления выберите команду для обновления результатов выполнения запроса, которые теперь будут иметь такой вид, как на рис. 2.3.РИС. 2.3. Результаты выполнения запроса на борку данных из полей FirstName и LastName tblCustomerодном запросе.Предложения FROM работают совместно с предложениями SELECT. Например, чтобы отобрать все записи в таблице tblCustomer, используйте приведенную ниже команду SQL.SELECT *FROM tblCustomerПри выполнении этого запроса считываются все записи и все поля в таблице tblCustomer (без какого-либо упорядочения записей).Чтобы отобрать только имя и фамилию клиентов, воспользуйтесь приведенной ниже командой SQL.SELECT FirstName, LastName FROM tblCustomerПосле изменения запроса в режиме конструктора представления выберите команду для обновления результатов выполнения запроса, которые теперь будут иметь такой вид, как на рис. 2.3.РИС. 2.3. Результаты выполнения запроса на борку данных из полей FirstName и LastName tblCustomerFirstName1 LastNameFirstName1 LastNameЯкЯк[Table | Output jsort Type |tbICustomer v tblCustomer[Table | Output jsort Type |tbICustomer v tblCustomerFtfrom novelty. dbo.tbICustomerFtfrom novelty. dbo.tbICustomerFirstNameJohnSmithFirstNameJohnSmithJillBrad Daisy Dave BettyJillBrad Daisy Dave BettyJones Klein• MartinKleinJones Klein• MartinKleinThurston RyanThurston RyanWintersWinters
одном запросе.
Предложения FROM работают совместно с предложениями SELECT. Например, чтобы отобрать все записи в таблице tblCustomer, используйте приведенную ниже команду SQL.
SELECT *
FROM tblCustomer
При выполнении этого запроса считываются все записи и все поля в таблице tblCustomer (без какого-либо упорядочения записей).
Чтобы отобрать только имя и фамилию клиентов, воспользуйтесь приведенной ниже командой SQL.
SELECT FirstName, LastName FROM tblCustomer
После изменения запроса в режиме конструктора представления выберите команду для обновления результатов выполнения запроса, которые теперь будут иметь такой вид, как на рис. 2.3.
РИС. 2.3. Результаты выполнения запроса на борку данных из полей FirstName и LastName tblCustomer
одном запросе.
Предложения FROM работают совместно с предложениями SELECT. Например, чтобы отобрать все записи в таблице tblCustomer, используйте приведенную ниже команду SQL.
SELECT *
FROM tblCustomer
При выполнении этого запроса считываются все записи и все поля в таблице tblCustomer (без какого-либо упорядочения записей).
Чтобы отобрать только имя и фамилию клиентов, воспользуйтесь приведенной ниже командой SQL.
SELECT FirstName, LastName FROM tblCustomer
После изменения запроса в режиме конструктора представления выберите команду для обновления результатов выполнения запроса, которые теперь будут иметь такой вид, как на рис. 2.3.
РИС. 2.3. Результаты выполнения запроса на борку данных из полей FirstName и LastName tblCustomer
FirstName
1 LastName
FirstName
1 LastName
Як
Як
[Table | Output jsort Type |
tbICustomer v tblCustomer
[Table | Output jsort Type |
tbICustomer v tblCustomer
Ft
from novelty. dbo.tbICustomer
Ft
from novelty. dbo.tbICustomer
FirstName
JohnSmith
FirstName
JohnSmith
Jill
Brad Daisy Dave Betty
Jill
Brad Daisy Dave Betty
Jones Klein
• Martin
Klein
Jones Klein
• Martin
Klein
Thurston Ryan
Thurston Ryan
Winters
Winters
Из соображений эффективности всегда ограничивайте число полей в предложении SELECT только теми полями, которые могут потребоваться вашему приложению. Обратите внимание, что записи, отобранные запросом SELECT FROM, в результирующем наборе не упорядочены. Если не задать порядок сортировки (использование предложения ORDER BY рассматривается ниже в этой главе), записи всегда возвращаются в неопределенном порядке.
71
Формирование критериев
с использованием предложения WHERE
Предложение WHERE указывает процессору базы данных на необходимость ограничения количества отбираемых записей согласно одному или нескольким заданным критериям. Критерий — это логическое выражение, результатом оценки которого является либо "истина" (true), либо "ложь" (false). В языке SQL существует много аналогичных выражений эквивалентности, знакомых пользователям Visual Basic (например: >0 и = 1 Smith').
Предположим, вам нужно отобрать только тех заказчиков, которые проживают в Калифорнии (СА). Для этого можно использовать приведенный ниже запрос SQL.
SELECT FirstName, LastName, State FROM tblCustomer WHERE State = 'CA'
В результате выполнения этого запроса будет извлечена запись с данными о клиенте с именем Daisy Klein.
Обратите внимание также на то, что для обозначения текстовой строки в предложении WHERE используется одиночная кавычка. Подобное обозначение удобно тем, что отличается от обозначения текстовой строки в Visual Basic двойной кавычкой, а инструкции SQL иногда встраиваются в код Visual Basic.
Можно создавать более сложные предложения WHERE, связывая два и более критерия с помощью логических операторов AND или OR. Например, необходимо отобрать всех клиентов, проживающих в городе Денвер (Denver) штата Колорадо (СО), т.е. вас не интересуют те клиенты, которые проживают в других городах этого штата. Для этого нужно задать два критерия и связать их оператором AND, как показано в приведенном ниже примере.
SELECT FirstName, LastName, City, State
FROM tblCustomer
WHERE (State = 'CO' ) AND (City = 'Denver')
В результате выполнения этого запроса будет извлечена запись с данными о клиенте с именем Thurston Ryan, который живет в городе Денвер, штат Колорадо. Если в этом городе живут другие клиенты, то в результате выполнения данного запроса будут извлечены записи с информацией о них. Однако при этом не будут извлекаться сведения о клиентах, которые проживают в городах с тем же названием, но в других штатах.
Если вас интересует информация о клиентах в штатах Вашингтон (WA) и Калифорния (СА), воспользуйтесь оператором OR, чтобы связать два критерия, как показано ниже.
SELECT FirstName, LastName, City, State
FROM tblCustomer
WHERE State = 'CO' OR State = 'CA'
В результате выполнения этого запроса будут извлечены три записи с данными о клиентах в штатах Вашингтон и Калифорния. Из сказанного выше следует, что для OT-
бора данных из таблицы с помощью условий AND и OR можно составить практически любую комбинацию критериев в предложении WHERE.
НА ЗАМЕТКУ
Ключ к успешной разработке приложений клиент/сервер — выработкатакой тактики, согласно которой приложение-клиент не заказывало бы слишком много записей в I одной выборке. Это гарантирует, что приложения будут выполняться быстро и компьютеры ваших пользователей не зависнут. Залог успеха вашей работы — рациональное использование предложения WHERE.
Операторы, используемые
в предложении WHERE
При построении предложения WHERE можно использовать операторы, перечисленные в табл. 2.1.
Таблица 2.1. операторы, используемые в предложении WHERE
Оператор
Функция
Меньше
>
Меньше или равно Больше
>=
Больше или равно
о
Равно
Не равно
BETWEEN
Внутри диапазона значений
LIKE
Соответствует образцу
IN
Входит в список значений
Оператор BETWEEN
Этот оператор возвращает все записи, значения которых лежат внутри определенных вами границ. Например, для того чтобы отобрать все заказы, оформленные за период с 4 января по 5 июня 2001 года, необходимо написать приведенную ниже инструкцию SQL.
SELECT *
FROM tblOrder
WHERE OrderDate BETWEEN '1/4/2001' AND '6/5/2001'
В результате выполнения этого запроса будут извлечены записи, показанные на рис. 2.4.
Обратите внимание, что значения даты в SQL Server обозначаются символом одиночной кавычки. Разработчикам, которые имеют опыт работы с датами в Microsoft
Access и привыкли использовать для этого символ #, придется учесть это новшество и
заменить символы # одиночными кавычками.
11/4/20012I1/9/2001321/14/2001442/18/2001. 543/21/200164■4/4/2001'j|7!5' 6№ooi11/4/20012I1/9/2001321/14/2001442/18/2001. 543/21/200164■4/4/2001'j|7!5' 6№ooiI
11/4/2001
2I1/9/2001
321/14/2001
442/18/2001
. 543/21/2001
64■4/4/2001'
j|7!5' 6№ooi
11/4/2001
2I1/9/2001
321/14/2001
442/18/2001
. 543/21/2001
64■4/4/2001'
j|7!5' 6№ooi
РИС. 2.4. Результаты выполнения запроса для таблицы tblOrder с использованием предложения SELECT и оператора BETWEEN
Границы оператора BETWEEN являются включающими; это значит, что если вы запрашиваете информацию обо всех заказах, оформленных за период с 4 января по 5 июня 2001 года, то в результирующий набор включаются заказы, которые были оформлены как 4 января, так и 5 июня.
Оператор LIKE и символы шаблона
С помощью оператора LIKE отбираются записи, соответствующие заданному шаблону. Этот шаблон обычно состоит из так называемых подстановочных символов (wildcard characters) * или ?, с которыми вы, возможно, уже знакомы по работе с файловыми системами MS DOS или Windows.
Символ процента (%) означает частичное соответствие. Например, чтобы отобрать в таблице tblCustomer все записи, в которых фамилия начинается с буквы J, можно воспользоваться приведенным ниже запросом.
SELECT ID, FirstName, LastName, Address, City, State FROM tblCustomer
WHERE [LastName] LIKE 1 J%'
В результате выполнения этого запроса будут извлечены записи для тех клиентов, фамилии которых начинаются с буквы J.
При создании шаблонов можно также использовать символ подчеркивания. Он занимает место только одного символа в шаблоне. Например, чтобы отобрать всех клиентов, у которых почтовый индекс состоит из пяти цифр и начинается с числа 80, воспользуйтесь следующей командой SQL:
SELECT ID, FirstName, LastName, Address, PostalCode FROM tblCustomer
WHERE PostalCode LIKE '80'
В результате выполнения этого запроса будут извлечены записи для тех клиентов, почтовый индекс которых начинается с числа 80.
Оператор LIKE можно использовать также для отбора записей на основе вхождения в диапазон определенных алфавитных или числовых значений. Например, чтобы возвратить список клиентов, фамилии которых начинаются с букв в диапазоне от А до М, используйте приведенную ниже команду SQL.
SELECT ID, FirstName, LastName
FROM tblCustomer
WHERE LastName LIKE '[A-M]%'
В результате выполнения этого запроса будут извлечены пять записей для тех клиентов, фамилии которых начинаются с букв в диапазоне от А до М.
НА ЗАМЕТКУ ;
Действие символов подстановки в Microsoft Access отличается от действия этих же j символовстандартном SQL. В Microsoft Access символ звездочки используется j
вместо символа процента для подбора любого числа произвольных символов, а сим- - j вол знака вопроса — вместо символа подчеркивания для подбора любого одиночно- I го символа. В стандартном SQL символ подчеркивания используется для подбора i любого числа произвольных символов, а символ процента — для подбора любого одиночного символа.
Оператор IN
Этот оператор используется для отбора записей, которые соответствуют элементам из заданного списка значений. Например, чтобы отобрать всех клиентов в штатах лорадо (СО) или Висконсинвоспользуйтесь приведенной ниже инструкцией SQL.
SELECT FirstName, LastName, State FROM
WHERE State IN
В результате выполнения этого запроса будут извлечены три записи для тех клиентов, которые живут в штатах Колорадо или Висконсин. Как видите, с помощью оператора IN можно получить те же результаты, что и с помощью оператора OR. Некоторые разработчики предпочитают применять оператор IN при использовании нескольких критериев, поскольку в таком случае команда SQL выглядит более аккуратно.
Сортировка результатов с помощью предложения ORDER BY
ПредложениеORDERBYформируетдляпроцессорабазданныхкомандуна
сортировку отобранных записей. Можно сортировать по любому полю или нескольким полям, причем как в возрастающей, так и в убывающей последовательности. Для чтобы задать порядок сортировки, добавьте в конец обычного запроса SELECT предложение ORDER BY, а за ним укажите поле или поля, по которым нужно выполнить сортировку. Например, чтобы отобрать список клиентов, отсортированный по фамилии, воспользуйтесь приведенной ниже инструкцией SQL.
SELECT ID, FirstName, LastName
FROM tblCustomer
ORDER BY LastName
В результате выполнения этого запроса из таблицы tblCustomer будут извлечены все записи, упорядоченные по фамилиям клиентов.
Сортировка в убывающей последовательности
Для сортировки в убывающей последовательности задайте ключевое слово после имени поля, по которому проводится сортировка. Например, если нужно ото-
75
брать из таблицы tblOrder записи так, чтобы сначала располагались самые последние заказы, воспользуйтесь следующей командой SQL:
SELECT *
FROM tblOrder
ORDER BY OrderDate DESC
В результате выполнения этого запроса из таблицы tblOrder будут извлечены все записи, причем сначала будут располагаться самые последние заказы.
Сортировка по нескольким полям
Для того чтобы отсортировать записи по нескольким полям, после предложения ORDER BY перечислите поля друг за другом через запятую. Например, чтобы отсортировать записи в таблице tblCustomer по фамилии, а затем по имени, воспользуйтесь приведенной ниже командой SQL.
SELECT FirstName, LastName, City, State
FROM tblCustomer
ORDER BY LastName, FirstName
В результате выполнения этого запроса из таблицы tblCustomer будут извлечены все записи, отсортированные по фамилиям, а затем по именам (например, за клиентом Betty Klein будет располагаться клиент Daisy Klein).
Отображение первых или последних записей диапазона с помощью предложения ТОР
Ключевое слово ТОР используется для отображения некоторого количества начальных или конечных записей из большого результирующего набора. Для ограничения числа записей в результирующем наборе ключевое слово ТОР в запросах сочетается с предложением, указывающим порядок сортировки. Причем ключевое слово ТОР можно комбинировать как с числом, означающим количество записей, так и с числом, означающим процентное содержание отображаемых записей.
Например, необходимо отобрать три наиболее крупных заказа из числа последних заказов в таблице tblOrder. Для этого воспользуйтесь приведенной ниже инструкцией SQL.
SELECT ID, OrderDate, CustomerlD
FROM tblOrder
ORDER BY OrderDate DESC
Обратите внимание, что ключевое слово DESC сортирует результирующий набор по убыванию. В результате выполнения этого запроса из таблицы tblOrder будут извлечены сведения о заказах каждого клиента, причем сначала будут располагаться самые последние заказы. Все просто прекрасно, за исключением ТОГО, что в базе данных, которая хранит информацию обо всех когда-либо выполненных заказах, придется просмотреть тысячи записей, в то время как вас интересуют только три наиболее
крупных заказа. Поэтому вместо предыдущей команды попробуйте выполнить приведенную ниже инструкцию SQL.
SELECT TOP 3 * FROM
ORDER BYDESC
В результате выполнения этого запроса из таблицы tblOrder будут извлечены три записи для наиболее крупных заказов.
Почему же запрос вернул четыре записи вместо запрошенных трех? Такой запрос (ТОР 3) вовсе не гарантирует, что будут возвращены только три записи. Возможно, что результирующий набор будет содержать одну или две записи (или даже ни если в таблице содержится только такое количество записей. А если на последнее место в результирующем наборе претендуют две и более записи, то вполне возможно, что будут возвращены четыре или даже большее количество записей.
В синтаксисе SQL нет понятия BOTTOM N, но зато есть возможность возвратить заданное количество последних записей в таблице. Для того чтобы создать такой запрос, достаточно отсортировать записи в возрастающей последовательности (т.е. от самого малого значения к самомукак показано ниже.
SELECT TOP 3 * FROM tblOrder ORDER BY OrderDate
Этот запрос показывает три самых "древних" заказа в базе данных.
Сортировка данных в порядке возрастания в SQL принимается по умолчанию. Поэтому нет необходимости использовать ключевое слово для задания порядка сортировки по возрастанию.
Создание запросов TOP PERCENT
Можно писать запросы, возвращающие записи, количество которых определяется заданным процентом от общего количества записей в таблице. Например, если у вас есть таблица с 1 000 записей и необходимо возвратить один процент первых записей, то, как правило, будет отображено 10 записей. (Возможно, будет отображено более 10 записей, если несколько записей имеют одинаковое значение. Такой же случай рассматривался и для запроса ТОР N.)
Для возврата первых записей в результирующий набор, количество которых задано процентным отношением к общему количеству записей в таблице,
ся предложение TOP N PERCENT. Например, чтобы отобрать первые 20% от неоплаченных заказов в таблице tblOrder, воспользуйтесь приведенной ниже командой SQL.
SELECT TOP 20 PERCENT * FROM tblOrder
ORDER BY OrderDate DESC
В результате выполнения этого запроса из таблицы tblOrder будут извлечены две записи для самых последних заказов, которые составляют 20% от 10 строк таблицы tblOrder.
Объединение связанных таблиц
в запросе
Для выборки связанной информации из нескольких таблиц используется объединение (join). Чтобы создать объединение в запросе, необходимо определить первичные (primary) и внешние (foreign) ключи в таблицах, участвующих в объединении (эти понятия обсуждаются в главе 1, "Основы построения баз данных"). Например, рассмотрим две связанные таблицы с показанными ниже структурами.
tblCustomer
ID
FirstName
LastName
Address
City
State
PostalCode
Phone
Fax
tblOrder
ID
CustomerlD
OrderDate
OrderAmount
Хотя в таблице tblOrder хранится информация о заказах, а в таблице tblCus- tomeг — информация о клиентах, вполне вероятно, что вам потребуется, например, отобрать такую информацию о заказах клиентов, как показано ниже.
FirstNameLastName
Order Date
JaneWinters
9/10/2001
JaneWinters
8/16/2001
ThurstonRyan
7/2/2001
DaveMartin
6/5/2001
DaisyKlein
4/4/2001
Такой результирующий набор нетрудно получить, используя объединение, несмотря на то что необходимые данные хранятся в разных таблицах. Можно надеяться на получение нужных данных, если сообщить процессору баз данных о том, что первичный ключ таблицы tblCustomer (ID) связан с внешним ключом (CustomerlD) таблицы tblOrder.
НА ЗАМЕТКУ
Обратите внимание, что в этом результате объединения один и тот же клиент ото- ;
| бражен дважды, хотя его имя было введено в базу данных один раз. Это значит, что
I клиент имеет несколько заказов. Иными словами, при нескольких заказах одного и i того же клиента вам не пришлось вводить в базу данных информацию об этом клиен- i те несколько раз. Вместе с тем в результате выполнения запроса иногда можно по-
| лучить избыточную информацию. Для исправления такой ситуации можно применять j несколько разных методов.
Выражение объединения в SQL
В SQL Server объединение можно установить с помощью выражения эквивалентности между двумя полями, например:
SELECT FirstName, LastName, OrderDate
FROM tblOrder INNER JOIN tblCustomer ON tblOrder.CustomerlD =
Этот запрос SQL возвращает информацию обо всех клиентах, которые имеют заказы в таблице tblOrder. В результате выполнения запроса возвращаются три столбца данных: поля FirstName и LastName из таблицы tblCustomer, а также поле OrderDate из таблицы tblOrder.
Обратите внимание, что в запросе с объединением таблиц при использовании полей, имеющих одинаковые имена, но принадлежащих разным таблицам, необходимо перед именем поля вставлять ссылку на соответствующую таблицу (например, tblOrder. ID вместо ID). В большинстве случаев при использовании конструктора представлений в среде Visual Studio .NET для создания запроса интегрированная среда разработки способна определить выполняемые действия и дополнить выражение недостающими частями. Как уже сообщалось ранее, в данной книге примеры инструкций SQL приводятся в наиболее кратком виде, а необязательные части применяются только в случае необходимости.
Использование конструктора представлений для создания объединений
Поскольку создание объединений может составлять самую сложную часть запросов, особенно когда задействовано более двух таблиц, неплохо было бы при создании таких запросов иметь некоторое подспорье. К счастью, в Visual Basic предусмотрен конструктор представлений (View Designer), благодаря которому создание запроса с объединением нескольких таблиц значительно упрощается. При использовании конструктора представлений нет необходимости запоминать сложный синтаксис
79
объединения в SQL. Вместо этого можно создать объединение графическим путем, выполнив приведенные ниже действия.
В окне Server Explorer создайте новое представление для базы данных Novelty.
После этого появится диалоговое окно Add Table (Создать таблицу), в котором следует указать таблицы tblCustomer и tblOrder, а затем щелкнуть на кнопке Close. Схема представления в окне конструктора представлений показана на рис. 2.5.
E tbtCutiomerE tbtCutiomerU* (All Columns)ID□F irstName□LastName□Company□Address□Ci tyGstate□PostalCodePhone□FaxEmai 1U* (All Columns)ID□F irstName□LastName□Company□Address□Ci tyGstate□PostalCodePhone□FaxEmai 1И tblOrderИ tblOrderввJ* (ATI Columns)ID □CustomerlD□OrderDateJ* (ATI Columns)ID □CustomerlD□OrderDate
E tbtCutiomer
E tbtCutiomer
U* (All Columns)
ID
□F irstName
□LastName
□Company
□Address
□Ci ty
Gstate
□PostalCode
Phone
□Fax
Emai 1
U* (All Columns)
ID
□F irstName
□LastName
□Company
□Address
□Ci ty
Gstate
□PostalCode
Phone
□Fax
Emai 1
И tblOrder
И tblOrder
в
в
J* (ATI Columns)
ID
□CustomerlD
□OrderDate
J* (ATI Columns)
ID
□CustomerlD
□OrderDate
РИС. 2.5. Создание объединения двух таблиц в окне конструктора представлений
Обратите внимание на то, что конструктор представлений автоматически создает объединение между двумя таблицами на основе известного ключевого поля ID в таблице tblCustomer и явно заданного ранее отношения с полем CustomerlD в табли- цеtblOrder.
После выполнения запроса на основе объединения двух таблиц в окне конструктора представлений будут отображены извлеченные данные, как показано на рис. 2.6.
Использование внешних объединений
Обычное (внутреннее) объединение (innerjoin) возвращает записи из двух таблиц, если значение первичного ключа первой таблицы соответствует значению внешнего ключа второй таблицы, связанной с первой. Предположим, необходимо получить все записи из одной таблицы, участвующей в объединении, вне зависимости от того, существуют ли связанные записи в другой таблице. В этом случае необходимо использовать внешнее объединение (outerjoin).
Например, для извлечения списка клиентов и заказов, в который включены также клиенты, не имеющие неоплаченных заказов, можно использовать приведенный ниже запрос.
SELECT FirstName, LastName, OrderDate
FROM tblCustomer LEFT OUTER JOIN
tblOrder ON tblCustomer.ID =
Обратите внимание, что в предложении LEFT JOIN используется синтаксис имя_таблиц ы. илицголя. Более длинное имя позволяет избежать неоднозначности при использовании полей с одинаковыми именами, поскольку поле ID существует как в
tblCustomer, так и в tblOrder. Фактически предложение LEFT OUTER JOIN означает, что будут отображены все данные таблицы tblCustomer, которая находится з левой стороне выражения tblCustomer. ID =
ЦП e Ы1 tj»Mi*i tЦП e Ы1 tj»Mi*i tP* (All Columns)[ DID: ®Fi rstName' BiastName1 DCompanyI 1Address: dci ty, [JState[ ElPostalCode1 DPhone: QFax□Emai 1P* (All Columns)[ DID: ®Fi rstName' BiastName1 DCompanyI 1Address: dci ty, [JState[ ElPostalCode1 DPhone: QFax□Emai 1HH IblP. cfe»HH IblP. cfe»00□ * (All Columns) DID□CustomerlDj BOrderDate□ * (All Columns) DID□CustomerlDj BOrderDate
ЦП e Ы1 tj»Mi*i t
ЦП e Ы1 tj»Mi*i t
P* (All Columns)
[ DID
: ®Fi rstName
' BiastName
1 DCompany
I 1Address
: dci ty
, [JState
[ ElPostalCode
1 DPhone
: QFax
□Emai 1
P* (All Columns)
[ DID
: ®Fi rstName
' BiastName
1 DCompany
I 1Address
: dci ty
, [JState
[ ElPostalCode
1 DPhone
: QFax
□Emai 1
HH IblP. cfe»
HH IblP. cfe»
0
0
□ * (All Columns) DID
□CustomerlD
j BOrderDate
□ * (All Columns) DID
□CustomerlD
j BOrderDate
"Column"ColumnFirstNameLastNameOrderoateFirstNameLastNameOrderoateAlias"[Ta'61e [output [sort type [Sort Order ^Cri terTa] tblCustomer tbl Customerу"
"Column
"Column
FirstName
LastName
Orderoate
FirstName
LastName
Orderoate
[tblOrderv
SELECT dbo.tblCustomer.FtrstName, dbo.tblcustomer.LastName, dbo.tblOrder.orderoate
FROM dbo.tblCustomer INNER JOIN
dbo.tblOrder ON dbo.tblCustomer.ID =
Fi rstName|LastName
1■
к
John
Smi th
1/4/2001
John
3il1
'■Smi th lAzall a
1/9/2001 ;
^Д4/20О1[.
Dai sy
Klein
i
Dai sy
iKlein
3/21/2001
Dai sy
[Klei n
4/4/2001 1
Dave
Martin
6/5/2001 i
Thurston
Ryan [vffnt'ers '
77272001 i
lane
8/16/20011
Jane
Wi nters
9/10/20011
РИС. 2.6. Выполнение запроса на основе объединения двух таблиц в окне конструктора представлений
Этот запрос возвращает приведенный ниже набор записей.
FirstName
LastName
OrderDate
John
Smith
1/4/2001
John
Smith
1/9/2001
Jill
Azalia
1/14/2001
01
FirstName
LastName
Order Date
Brad
Jones
<NULL>
Daisy
Klein
2/18/2001
Daisy
Klein
3/21/2001
Daisy
Klein
4/4/2001
Dave
Martin
6/5/2001
Betty
Klein
<NULL>
Thurston
Ryan
7/2/2001
Jane
Winters
8/16/2001
Jane
Winters
9/10/2001
В этот результирующий набор включены все клиенты базы данных, независимо от того, имеют ли они заказы или нет. Для клиентов, не имеющих заказов, в поле OrderDate появится <NULL>, что означает отсутствие данных.
Правое внешнее объединение (rightjoin) аналогично левому внешнему объединению (leftjoin), за исключением того, что оно возвращает все записи из второй таблицы, участвующей в объединении (имеется в виду таблица с правой стороны), независимо от того, есть ли соответствующие им записи в первой таблице (расположенной с левой стороны). (Левое и правое объединения являются разновидностями внешнего объединения и в определенных обстоятельствах могут возвращать идентичные результаты.)
Выполнение вычислений в запросах
В строках запроса допускается выполнение вычислений. Для этого нужно просто заменить имя поля в предложении SELECT именем арифметического выражения. Допустим, вам нужно создать запрос для вычисления налога с продаж для складских запасов (сведения о которых хранятся в таблице tblltem). В приведенном ниже запросе SQL вычисляется налог с продаж с учетной ставкой 7,5% для каждого товара.
SELECT ID, Item, Price, Price * 0.075
AS SalesTax
FROM tblltem
После выполнения этого запроса будут получен показанный ниже результат.
ID
Name
Price
SalesTax
1
Rubber Chicken
5.99
0.44925
2
Hand Buzzer
1.39
0.10425
3
Stink Bomb
1.29
0.09675
4
Disappearing Penny Magic Trick
3.99
0.29925
5
Invisible Ink
2.29
0.17175
6
Loaded Dice
3.49
0.26175
7
Whoopee Cushion
5.99
0.44925
Поскольку в этих вычислениях фигурируют деньги, конечный результат нужно округлить до двух десятичных знаков. К счастью, в SQL Server для этого предусмотрена специальная функция ROUND, которая позволяет очень легко выполнить это. Обычно ее используют с указанием двух параметров: собственно десятичного числа и точности, выраженной в виДе количества знаков после запятой. Вот как выглядит запрос с функцией ROUND:
SELECT Name, Retail Price, ROUND (Retail Price + Retail Price * 0.075, 2) AS PriceWithTax
FROM tbllnventory
Результат выполнения этого запроса приведен ниже.
Name
Retail Price
PriceWithTax
Rubber Chicken
5.99
6.44
Hand Buzzer
1.39
1.49
Stink Bomb
1.29
1.39
Disappearing Penny Magic Trick
3.99
4.29
Invisible Ink
2.29
2.46
Loaded Dice
3.49
3. 75
Whoopee Cushion
5.99
6.44
Определение псевдонимов
с использованием предложения AS
Из предыдущего примера ясно, что существует возможность определения псевдонимов (alias), т.е. переименования полей в запросе. Это может быть вызвано следующими причинами:
в основной таблице имена полей могут быть громоздкими, а в результирующем наборе должны быть понятными и простыми;
запрос создает столбец, который заполняется в результате некоторых вычислений или операций подведения итогов, а новому столбцу обязательно нужно задать имя.
Независимо от причины, это легко сделать в запросе SQL с помощью предложения AS. Например, вам нужно выполнить ряд сложных вычислений для определения суммарной стоимости отгруженных товаров ExtendedPrice. Для этого напишите следующий код SQL:
SELECT TOP 5 ItemID, Quantity, Price,
tbllnventory.ReatilPrice * tblOrderTime.Quantity AS ExtendedPrice FROM tblOrderTime INNER JOIN
tbllnventory ON tblOrderTime.ItemID =
Этот запрос возвращает приведенный ниже результирующий набор.
83
ItemID
Quantity
RetailPrice
ExtendedPrice
1
1
5.99
5.99
2
2
1.39
2.78
5
3
2.29
6.87
4
2
3.99
7.98
7
1
5.99
5.99
Обратите внимание, что данные в поле ExtendedPrice не хранятся в базе данных, они вычислены "на лету".
Запросы, которые группируют данные и подводят итоги
Часто требуется создавать запросы, подобные следующему: "Сколько заказов поступило вчера?" При этом вас не интересует, кто оформил заказ, вы только хотите знать количество вчерашних заказов. Это можно сделать, используя запросы, группирующие итоговые функции.
Запросы с группировкой подводят итоги по одному или нескольким полям. Например, если вам интересно увидеть число заказов для каждого клиента, то нужно создать приведенный ниже запрос для таблицы tblOrder с группировкой данных по полю Customer ID.
SELECT CustomerlD, COUNT(CustomerlD) AS Totalorders
FROM tblOrder
GROUP BY CustomerlD
Результат выполнения такого запроса приведен ниже.
CustomerlDTotalOrders
1
2
2
1
4
3
5
1
7
1
8
2
Обратите внимание на использование предложения AS в этом выражении SQL. Оно предназначено для присвоения имени столбцу, содержащему результат итоговой функции, поскольку этот результат вычисляется, а не хранится в базе данных.
Для отображения имен клиентов вместо их идентификаторов нужно просто объединить с результатами запроса данные из таблицы tblCustomer.
SELECT tblOrder.CustomerlD, FirstName, LastName
COUNT(dbo.tblOrder.CustomerlD) AS TotalOrders
FROM tblOrder INNER JOIN tblCustomer ON tblOrder.CustomerID =
GROUP BY FirstName, LastName, CustomerlD
После выполнения такого запроса будет получен приведенный ниже результат.
CustomerlD
FirstName
LastName
TotalOrders
1
John
Smith
2
2
Jill
Azalia
1
4
Daisy
Klein
3
5
Dave
Martin
1
7
Thurston
Ryan
1
8
Jane
Winters
2
В этом случае предложение GROUP BY содержит поле CustomerlD вместе с объединенными полями FirstName и LastName из таблицы tblCustomer. При использовании предложения GROUP BY в него необходимо включить все поля, по которым группируются извлекаемые записи. В данном случае идентификатор клиента и его имя участвуют в группировании данных и потому присутствуют в предложении GROUP BY. (К счастью, если вы забудете выполнить эту операцию, среда Visual Studio .NET автоматически предложит вам сделать это.)
Применение предложения HAVING для группирования данных в запросах
Как уже отмечалось выше, критерий запроса служит для ограничения количества извлекаемых записей. В обычных запросах для включения критериев используется предложение WHERE, в запросах с группированием — предложение HAVING. Эти предложения применяются совершенно одинаково, за исключением того, что HAVING относится к агрегированным строкам (т.е. к результату группирования), a WHERE — к OT- дельным строкам. Это довольно незначительное отличие, потому что в 9 случаях из 10 они дают совершенно одинаковый результат. Например для создания отчета о продажах клиента Jane с группированием данных можно использовать приведенный ниже запрос.
SELECT tblOrder.CustomerlD, FirstName, LastName,
COUNT(dbo.tblOrder.CustomerlD) AS TotalOrders
FROM tblOrder INNER JOIN tblCustomer
ON tblOrder.CustomerlD =
GROUP BY FirstName, LastName, CustomerlD HAVING FirstName = 'Jane'
Этот запрос возвращает одну запись для клиента Jane Winters с указанием двух сделанных ею заказов. Допустим, теперь нужно получить список активных покупателей, т.е. клиентов, сделавших более одного заказа. Поскольку агрегированное количество заказов хранится в вычисленном поле TotalOrders, можно предположить, что для определения таких клиентов допустимо использовать выражение HAVING TotalOrders > 1. К сожалению, это выражение некорректно, так как TotalOrders — это не поле базы
данных, а вычисленное поле. Вместо этого следует включить данное вычисление в предложение HAVING показанного ниже запроса.
SELECT tblOrder.CustomerID, FirstName, LastName, COUNT(dbo.tblOrder.CustomerlD) AS Totalorders
FROM tblOrder INNER JOIN tblCustomer ON tblOrder.CustomerlD =
GROUP BY FirstName, LastName, CustomerlD
HAVING (COUNT(tblOrder.CustomerlD) > 1)
После выполнения этого запроса будут получены три строки, каждая из которых содержит номер, имя, фамилию и количество заказов для каждого клиента, который сделал более одного заказа.
CustomerlD
FirstName
LastName
TotalOrders
1
John
Smith
2
4
Daisy
Klein
3
8
Jane
Winters
2
ФункцияSUM
Ваши возможности в подведении итогов не ограничены простым подсчетом записей. Используя функцию SUM, можно генерировать итоговые результаты для всех возвращаемых записей по любым числовым полям. Например, для создания запроса, который генерирует итоги по количеству заказанных товаров каждым клиентом, необходимо написать следующую команду SQL:
SELECT OrderlD, SUM(Quantity) AS Totalltems FROM tblOrderltem
GROUP BY OrderlD
Этот запрос возвращает приведенный ниже результирующий набор.
OrderlD
Totalltems
1
6
2
2
3
1
4
23
5
4
6
13
7
12
8
3
9
4
10
4
Как и в предыдущих примерах группирования, если вы захотите извлечь дополнительную связанную информацию (например, имя и фамилию клиента), следует
86
использовать объединение с другой таблицей. Помните, что для агрегирования данных потребуется сгруппировать данные по крайней мере по одному полю.
Перечень итоговых функций
В табл. 2.2 перечислены все итоговые функции, доступные в SQL.
Таблица 2.2. Итоговые функции SQL
Функция
Результат
AVG
Среднее значение от всех значений в столбце
COUNT
Общее количество отобранных записей
МАХ
Максимальное (наибольшее) значение поля
MIN
Минимальное (наименьшее) значение поля
STDEV
Среднеквадратическое отклонение
SUM
Общая сумма всех значений в поле
VAR
Дисперсия
Синтаксис этих функций, по сути, соответствует синтаксису функции COUNT, которая рассматривалась в предыдущем разделе. Например, для ежедневного вычисления среднего количества товаров в каждом заказе воспользуйтесь приведенным ниже запросом SQL.
SELECT AVG(tblOrderltem.Quantity) AS AverageLineltemQuantity
FROM tblOrder INNER JOIN
tblOrderltem ON tblOrder.ID =
Этот запрос возвращает значение 2, т.е. количество товаров в заказах всех клиентов.
Вычисления и итоговые функции можно комбинировать разными способами. Например, чтобы получить список со стоимостью всех товаров в каждом заказе, нужно определить стоимость товара (эти сведения хранятся в таблице tbl Inventory) в каждом заказе и умножить ее на количество этих товаров в заказе (эти сведения хранятся в таблице tblOrderltem), азатем сложить полученные произведения в каждом заказе.
SELECT tblOrderltem.OrderID, SUM(Quantity * Price)
AS OrderTotal
FROM tbllnventory INNER JOIN tblOrderltem ON tblltem.ID = GROUP BY OrderlD
Этот запрос возвращает приведенный ниже результирующий набор.
OrderlD
OrderTotal
1
15.64
2
7.98
3
5.99
4
99.17
87
OrderlD
OrderTotal
5
13.96
6
49.07
7
55.88
8
13.97
9
9.16
10
14.76
Запросы на объединение
Запрос на объединение (union y?i«)) выполняет объединение содержимого двух таблиц, имеющих одинаковые структуры полей. Это оказывается полезным, когда нужно отобразить в одном результирующем наборе потенциально не связанные записи из нескольких источников.
Далее в главе приводятся примеры сохранения старых заказов в архивной таблице с именем tblOrderArchive. И если вы воспользуетесь предложенной системой архивирования, то записи физически будут размещены в двух отдельных таблицах. Это может повысить эффективность работы: запрос выполняется быстрее на маленькой таблице, чем на большой. Но, возможно, в некоторых случаях понадобится просмотреть все текущие и заархивированные записи в одном общем результирующем наборе. С такой задачей прекрасно справится запрос на объединение.
Предположим, что как раз возникла необходимость в просмотре в одном результирующем наборе старых записей из таблицы tblOrderArchive и новых записей из tblOrder. Такой запрос приведен ниже.
SELECT *
FROM tblOrder
UNION
SELECT *
FROM tblOrderArchive
После выполнения этого запроса старые и новые заказы объединятся в одном результирующем наборе, причем результат будет выглядеть подобно исходной таблице до архивирования.
По умолчанию запрос на объединение не возвращает записи-дубликаты (хотя было бы неплохо, чтобы ваша система архивирования записей не удаляла их после копирования в таблицу архива). Отображение записей-дубликатов может оказаться весьма полезным, если система архивирования старых записей не удаляет записи после копирования в архивную таблицу и вам нужно просмотреть и сравнить некоторые старые и новые записи.
Однако, добавив ключевое слово ALL, можно заставить запрос на объединение отображать дублирующие записи, как показано ниже.
SELECT *
FROM tblOrder
UNION ALL
SELECT *
FROM tblOrderArchive
Подзапросы
Подзапрос ^subquer^— это запрос, результат которого служит критерием для другого запроса. Подзапросы занимают место обычного выражения WHERE. Поскольку результат, сгенерированный подзапросом, используется вместо выражения, подзапрос может возвращать только одиночное значение (в противоположность обычному запросу, который возвращает несколько значений, представленных в виде строк и столбцов).
Единственное синтаксическое различие между подзапросом и выражением любого другого типа, размещенным в предложении WHERE, состоит в том, что подзапрос должен быть заключен в круглые скобки. Например, нужно создать запрос, который отображает заказы с самыми дорогими товарами. Дорогим считается такой товар, стоимость которого превышает среднюю стоимость товаров в таблице tblltem. Поскольку среднюю стоимость товара легко определить (выполнив итоговую функцию AVG по полю UnitPrice в таблице tblltem), это значение можно использовать как подзапрос в более крупном запросе. Такой запрос SQL приведен ниже.
SELECT Name, UnitPrice
FROM tblltem
WHERE (UnitPrice > (SELECT AVG(UnitPrice) FROM tblltem)
В этом случае оказывается, что запрос и подзапрос обращаются к одной и той же таблице, но это не принципиально. Подзапросы могут делать запросы к любой таблице в базе данных, главное — чтобы они возвращали одиночное значение.
Приведенная выше инструкция SQL возвращает следующий результирующий набор:
Name
UnitPrice
Rubber Chicken
5.99
Disappearing Penny Magic Trick
3.99
Loaded Dice
3.49
Whoopee Cushion
5.99
Манипулирование данными
с помощью SQL
Команда манипулирования данными (data manipulation command) — это команда SQL, которая изменяет записи. Такие команды создаются на языке манипулирования данными DML, который является подмножеством языка SQL. Эти команды не возвращают записи, а только изменяют их в базе данных.
DML-команды SQL обычно применяются для изменения большого объема данных на основе заданного критерия. Например, для повышения на 10% цены всех товаров следует использовать запрос на обновление, который автоматически выполнит такие изменения для всех товаров.
В среде Visual Studio .NET предусмотрен очень мощный интерфейс для выполнения DML-команд. Действительно, инструменты среды Visual Studio .NET могут пре-
доставить полезную информацию (например, правильную строку подключения для соединения с базой данных) или генерировать в окне конструктора основные DML- команды при извлечении данных из таблицы или изменении типа запроса.
НА ЗАМЕТКУ ——'—"|
Примеры в этом разделе демонстрируют способы изменения данных в базе данных i | Novelty. Если после многочисленных попыток изменить данные вы хотите вернуть I базу данныхNovelty в ее прежнее состояние, то ее можно переустановить, запуская i описанный во введении к этой книге сценарий.
На низком уровне (т.е. не на уровне графического интерфейса пользователя) DML-команды SQL можно использовать с помощью следующих двух инструментов:
Microsoft SQL Server Query Analyzer (или просто Query Analyzer) — инструмент с графическим интерфейсом пользователя для создания запросов и команд для SQL Server;
osql — используемый в режиме командной строки процессор запросов.
Вы можете использовать любой из этих инструментов, а в данной главе применяется Query Analyzer, который обладает более широкими возможностями и более удобен в употреблении, чем процессор запросов osql. В настоящей главе основное внимание сосредоточено на фактически выполняемых командах, а не на методах использования графического интерфейса Query Analyzer. Инструмент Query Analyzer находится в группе программ Microsoft SQL Server. (В главе 7, “ADO.NET: дополнительные компоненты", более подробно рассматриваются способы применения DML-команд в среде Visual Studio .NET.)
Запросы на обновление
Запрос на обновление может изменить сразу целую группу записей. Этот запрос состоит из трех частей:
предложение UPDATE, которое указывает на обновляемую таблицу;
предложениеSET, задающее данные для обновления;
необязательный критерий WHERE, ограничивающий число записей, на которые воздействует запрос на обновление.
Например, чтобы увеличить цену на все товары, воспользуйтесь запросом на обновление, код которого приведен ниже.
UPDATE tblltem
SET Price = Price * 1.1
SELECT * FROM tblltem
Команда SELECT, которая располагается вслед за предложением UPDATE, не обязательна и предназначена для просмотра результатов обновления.
Ниже приведены значения полей после выполнения данного запроса на об-
новление.
0Л
ID
Name
Description
UnitPrice
Price
1
Rubber Chicken
A classic laugh getter
2.0300
6.5890
2
Hand Buzzer
Shock your friends
.8600
1.5290
!
Stink Bomb
Perfect for ending boring meetings
.3400
1.4190
4
Invisible Ink
Write down your most intimate thoughts
1.4500
2.5190
5
Loaded Dice
Not for gambling purposes
1.4600
3.8390
6
Whoopee Cushion
The ultimate family gag
2.0300
6.5890
Для ограничения числа записей, подвергаемых воздействию запроса на обновление, достаточно добавить в запрос SQL предложение WHERE. Например, чтобы применить повышение цен только к дорогим товарам, стоимость которых больше $100, откорректируйте запрос так, как показано ниже.
UPDATE tblltem
SET Price = Price * 1.1
WHERE Price > 100
Эта команда увеличивает на 10% цену на товары, текущая цена которых больше $100.
Запросы на удаление
С помощью запроса на удаление {delete query) можно одним махом удалить одну или несколько записей. Например, чтобы удалить все заказы, которые были оформлены до (но не во время) последнего празднования Дня всех святых, воспользуйтесь запросом SQL, код которого приведен ниже.
DELETE *
FROM tblOrder
WHERE OrderDate < '10/31/2002'
Запрос на добавление записей
Запрос на добавление (append query) используется в двух случаях:
при добавлении одиночной записи в таблицу;
при копировании одной или нескольких записей из одной таблицы в другую.
Для создания запроса на добавление используйте предложение SQL INSERT. Точный синтаксис запроса зависит от того, добавляете ли вы одну запись или копируете несколько. Например, для добавления одной новой записи в tblOrder можно использовать приведенный ниже запрос.
INSERT INTO tblOrder(CustomerlD, OrderDate)
VALUES (119, '6/16/2001')
При выполнении этого запроса в таблице tblOrder создается новый заказ для клиента с идентификационным номером 119и датой 16 июня 2001 года.
НА ЗАМЕТКУ
В этом запросе на добавление для поля ID таблицы tblOrder не предлагается никакого значения, так как это поле идентификации. Попытка пользователя присвоить значение этому полю приведет к возникновению ошибки, поскольку его может сге- j нерировать только сам процессор баз данных.
Для создания разновидности запроса на добавление, который копирует записи из одной таблицы в другую, используйте предложение INSERT вместе с предложением SELECT. Предположим, вместо удаления старых заказов вы архивируете их, периодически копируя в архивную таблицу tblOrderArchive, которая имеет ту же структуру, что и tblOrder. Для выполнения этой работы необходимо сначала создать таблицу tblOrderArchiveсо структурой, аналогичной tblOrder.
CREATE TABLE tblOrderArchive (
ID [int] NOT NULL,
CustomerlD [int] NULL,
OrderDate [datetime] NULL
НА ЗАМЕТКУ '
i Как уже сообщалось выше, команды SQL для создания и управления структурой базы данных называются командами манипулирования данными или DML-командами. Бо-
лее подробно они рассматриваются далее в главе.
Ниже приведена инструкция SQL для копирования старых записей из tblOrder в tblOrderArchive.
INSERT INTO tblOrderArchive
SELECT * FROM tblOrder
WHERE OrderDate < ' 6/1/2001'
При выполнении этой инструкции SQL в таблицу tblOrderArchive копируются все записи, содержащие заказы, которые были оформлены до 1 июня 2001 года.
Запросы на основе команды SELECT INTO
Запрос на основе команды SELECT INTO аналогичен запросу на добавление, за исключением того, что он создает новую таблицу и сразу же копирует в нее записи. В Microsoft Access он называется запросом на создание таблии (make-table query). Так, в предыдущем примере все записи из таблицы tblOrder копировались в таблицу tblOr- derArchive, исходя из предположения, что таблица tblOrderArchive уже существует. Вместо этого запроса для копирования тех же записей в новую таблицу с такой же структурой, как и у оригинала, воспользуйтесь приведенным ниже запросом SQL. SELECT * INTO tblOrderArchive FROM tblOrder
НА ЗАМЕТКУ
Этот запрос копирует все записи из tblOrder в новую таблицу с именем tblOrder- Archive. Однако есди такая таблица уже существует, эта команда не будет выполнена. Это отличается от результата выполнения данного запроса в Microsoft Access.
; Если его выполнить в окне конструктора запросов программы Access при условии, что таблица tblOrderArchive уже существует, то процессор баз данных удалит исходную таблицу и заменит ее вновь созданной, которая будет заполнена содержи- ; мым скопированных записей. В SQL Server для удаления таблицы нужно использо- :
] вать DDL-команду DROP TABLE.
В запросе на основе команды SELECT INTO можно применить критерий отбора (с помощью предложения WHERE) точно так же, как это делалось в запросе на добавление (см. предыдущий раздел). Это дает возможность копировать подмножество записей из исходной таблицы в новую, которая формируется запросом на создание таблицы.
Использование языка определения данных
Команды языка определения данных (Data Definition Language — DDL) представляют собой инструкции SQL, которые позволяют создавать элементы структуры базы данных, манипулировать ими и удалять. Используя DDL, можно создавать и удалять таблицы, а также изменять структуру этих таблиц.
Команды DDL относятся к наиболее редко используемым инструкциям в SQL в основном потому, что существует множество прекрасных инструментов, которые позволяют легко справиться с задачами создания таблиц, полей и индексов. В среде Visual Studio .NET DDL-команды SQL используются незаметно для разработчика при создании схемы базы данных в окне Server Explorer, но в ней не предусмотрены инструменты для непосредственного выполнения команд SQL по отношению к базе данных. Для этого следует применять инструменты Query Analyzer и osql либо использовать DDL-команды непосредственно в коде.
Но если вы работаете в среде клиент/сервер, то для создания структуры базы данных удобнее использовать DDL-команды. Подобно командам манипулирования данными, DDL-команды не возвращают результирующих наборов (поэтому их и называют не запросами, а командами).
Создание элементов базы данных с помощью предложения CREATE
Новые элементы базы данных создаются с помощью предложения SQL CREATE. Чтобы создать таблицу, используйте команду CREATE TABLE, за которой введите поля и типы данных, предназначенные для добавления в таблицу. В качестве разделителей используйте запятые, а весь список заключите в круглые скобки. Например, для создания новой таблицы можно применять приведенную ниже инструкцию SQL.
CREATE TABLE tblRegion
(State char (2) ,
Region varchar (50) )
Тип данных char’ (2) означает, что процессор баз данных должен создать текстовое поле фиксированной длины для хранения максимум двух символов, а выражение varchar (50) указывает на создание поля с переменной длиной до 50 символов.
При выполнении этого запроса будет создана таблица со следующей структурой:
tblRegion
State
Region
В разделе о типах данных главы 1, "Основы построения баз данных", перечислены допустимые типы данных полей, которые можно использовать при создании полей.
Добавление ограничений в таблицу
В процессе создания таблицы можно добавить ограничения (constraints). Они аналогичны индексу, но используются для обозначения уникального, первичного или внешнего ключа.
Ограничение создается с помощью предложения SQLCONSTRAINT, которое принимает два параметра: имя индекса и имя поля или полей, в индексации которых вы заинтересованы. Можно объявить индекс с помощью ключевого слова UNIQUE или PRIMARY, и тогда этот индекс будет означать, что поле может принимать только уникальные значения или что поле (поля) служит первичным ключом таблицы.
НА ЗАМЕТКУ
Понятие именованных индексов может показаться несколько странным для тех, кто ; привык работать в Microsoft Access, поскольку Access скрывает имена индексов в своем пользовательском интерфейсе. Однако к имени индекса можно получить дос- I I туп программным путем.
Например, усовершенствовать таблицу tblRegion, созданную в предыдущем примере, можно добавлением уникального индекса к полю State, поскольку оно используется в объединении. Ниже представлена команда SQL, создающая эту таблицу с использованием предложения CONSTRAINT.
CREATE TABLE tblRegion
(State char (2) ,
Region varchar (50) ,
CONSTRAINT Stateindex UNIQUE (State))
Этот запрос создает таблицу с уникальным индексом по полю State, причем этот индекс имеет имя Statelndex.
Несмотря на то что в приведенном выше примере индексируется поле State, больше смысла было бы в TOM, чтобы сделать поле State первичным ключом таблицы. В этом случае вы получили бы гарантию того, что в поле State не было не только повторяющихся значений, но и значений NULL. Ниже приводится команда SQL, создающая таблицу tblRegionNew, в которой первичным ключом является поле State.
CREATE TABLE tblRegionNew
(State char (2) ,
Region varchar (50) ,
CONSTRAINT StatePrimary PRIMARY KEY (State))
Назначение внешнего ключа
Для того чтобы назначить поле в качестве внешнего ключа, используйте ограничение FOREIGN KEY. Например, в структуре нашей базы данных существует отношение типа один-ко-многим между полем State таблицы tblRegion и соответствующим полем State таблицы tblCustomer. Команда SQL, используемая для создания таблицы tblCustomer, может выглядеть так, как показано ниже.
CREATE TABLE tblCustomer
(ID int identity(1,1), FirstName varchar (20), LastName varchar (30), Address varchar (100), City varchar (75) , State varchar (2) , CONSTRAINT IDPrimary PRIMARY KEY (ID) , CONSTRAINT StateForeign FOREIGN KEY (State) REFERENCES tblRegionNew (State))
Обратите внимание, что внешний ключ в команде CREATE TABLE не создает индекс по этому внешнему ключу. Он только служит для создания отношения между двумя таблицами.
Создание индексов с помощью команды
CREATE INDEX
Помимо создания индексов в процессе формирования таблицы (с помощью предложения CONSTRAINT), можно также создавать индексы уже после того, как таблица сформирована (с помощью предложения CREATE INDEX). Это полезно в тех случаях, когда таблица уже существует (в то время как предложение CONSTRAINT применяется для формирования индексов только в момент создания таблицы).
Для создания индекса в существующей таблице используйте приведенную ниже команду SQL.
CREATE INDEX Stateindex ON tblCustomer (State)
Для того чтобы создать уникальный индекс, используйте ключевое слово UNIQUE, как показано ниже.
CREATE UNIQUE INDEX Statelndex ON tblRegion (State)
Чтобы создать первичный индекс в существующей таблице, используйте приведенную ниже команду SQL.
CREATE UNIQUE NONCLUSTERED INDEX Statelndex ON tblRegion (
State
) ON [PRIMARY]
95
Удаление таблиц и индексов с помощью предложения DROP
Удалять элементы базы данных можно с помощью предложения DROP. Например, чтобы удалить таблицу, используйте приведенную ниже команду SQL.
DROP TABLE tblRegion
С помощью предложения DROP можно также удалить индекс в таблице, как показано ниже.
DROP INDEX tblRegion.Stateindex.
Обратите внимание, что для удаления первичного ключа нужно знать имя этого ключа.
У вас также есть возможность удалять отдельные поля таблиц. Для этого нужно использовать предложение DROP внутри предложения ALTER TABLE, как показано в следующем разделе. А для удаления базы данных применяется команда DROP DATABASE.
Модификация структуры таблицы с помощью предложения ALTER
С помощью предложения ALTER можно изменить определения полей в таблице. Например, чтобы добавить поле CustomerType в tblCustomer, используйте приведенную ниже команду SQL.
ALTER TABLE tblCustomer
ADD CustomerType int
Для того чтобы удалить поле из базы данных, используйте предложение DROP COLUMN вместе с предложением ALTER TABLE, как показано ниже.
ALTER TABLE tblCustomer
DROP COLUMN CustomerType
Кроме того, с помощью предложения ALTER TABLE можно добавить в таблицу ограничения. Например, для создания отношения между таблицами tblCustomer и tblOrder с помощью предложения ALTER TABLE используйте приведенную ниже команду SQL.
ALTER TABLE tblOrder
ADD CONSTRAINT OrderForeignKey
FOREIGN KEY (CustomerlD)
REFERENCES tblCustomer (ID)
Помните, что добавление ограничения не создает обычного индекса по полю, оно просто делает поле уникальным, назначает поле первичным ключом или создает отношение между двумя таблицами.
Резюме
Эта глава посвящена технологии создания запросов, которые могут использоваться в приложениях доступа к базам данных, созданных в среде Visual Basic .NET. Здесь рассматривались запросы, которые возвращают необходимые записи, а также запросы, которые создают и модифицируют структуру баз данных.
Большая часть материала этой главы приводится отнюдь не ради увеличения объема книги; начав программировать с использованием Visual Studio .NET и ADO.NET, вы почувствуете реальную пользу от прочитанного.
Вопросы и ответы
Почему имена таблиц и полей иногда заключены в квадратные скобки?
Квадратные скобки часто окружают имена объектов в среде Visual Studio .NET и административных инструментах SQL Server для исключения проблем при использовании имен с пробелами и другими зарезервированными символами и словами. Например в базе данных Northwind, которая инсталлируется вместе с SQL Server 2000, есть таблица с именем Order Details. Хотя в общем случае не рекомендуется включать пробелы в имена таблиц, ее все же можно использовать в SQL Server в виде [Order Details]. Однако инструменты с графическим интерфейсом, например в среде Visual Studio .NET, всегда добавляют квадратные скобки. Но в данной книге они не используются, чтобы исключить излишнюю работу по их вводу.
Почему перед именем таблицы иногда используется приставка dbo? Что это такое?
dbo — это вспомогательный квалификатор (или моникер), который используется в инструментах с графическим интерфейсом при работе с SQL Server. Он позволяет установить соединение заданного объекта базы данных с владельцем базы данных. Объекты базы данных могут иметь разных владельцев, a dbo представляет собой сокращенную форму записи следующего высказывания: "этот объект относится к владельцу используемой базы данных". Разные объекты одной базы данных вполне могут относиться к разным владельцам, хотя, конечно, следует признать, что такая ситуация встречается редко. В базах данных, в которых все объекты относятся к владельцу dbo, этот моникер можно опустить (однако инструменты с графическим интерфейсом все равно попытаются вставить его).
ГЛАВАГЛАВА
ГЛАВА
ГЛАВА
ЗнакомствосSQL
Server 2000
В ЭТОЙ ГЛАВЕ...
Установка и запуск Microsoft SQL Server
Основы работы с SOL Server 2000
Резюме
Вопросы и ответы
В прошлом многие программисты начинали создание приложений баз данных, используя язык Visual Basic и базу данных Microsoft Access с ядром Jet. Как только база данных разрасталась до нескольких тысяч записей или к ее услугам обращались несколько пользователей, наступал "великий перелом". Конфликты при доступе к данным со стороны пользователей, уменьшение производительности и отсутствие удобных инструментов управления данными и сервером привели к тому, что разработчики стали искать способы решения этих проблем с помощью других архитектур баз данных. Одной из таких архитектур стала модель вычислений типа клиент/сервер (или модель распределенных вычислений).
Суть архитектуры клиент/сервер заключается не только в предоставлении многопользовательского режима работы с базой данных, к тому же с такой задачей вполне может справляться Jet. В многопользовательской архитектуре несколько пользователей используют одни и те же данные в сети, т.е. один или несколько файлов базы данных находятся на сервере, к которому могут осуществлять доступ клиентские компьютеры. Несмотря на то что Microsoft Access также поддерживает многопользовательский режим работы, эту программу нельзя отнести к системам клиент/сервер, поскольку все необходимые операции выполняются на отдельном компьютере клиента. Например, для извлечения с помощью команды SQL одной записи из таблицы, включающей 50 тыс. записей, нужно перенести на компьютер клиента все строки (или по крайней мере их индексы) таблицы. При этом на стороне сервера не предусмотрено никакой "интеллектуальной" части для специализированной обработки данных, например для выполнения запроса и возвращения только запрошенных данных.
В архитектуре клиент/сервер предусмотрена серверная часть (back end), т.е. специализированное программное обеспечение, которое способно извлекать и кэшировать данные, разрешать конфликты доступа к данным со стороны клиентов, а также обеспечивать безопасность. Например, СУБД SQL Server компании Microsoft получает запросы от клиентских компьютеров, выполняет их на серверном компьютере, а затем возвращает клиентскому компьютеру только запрошенные данные. Таким образом, для извлечения одной записи из таблицы, включающей 50 тыс. записей, серверу будет передана команда SELECT, серверное программное обеспечение выполнит эту команду и возвратит клиенту только искомую запись. Очевидно, что при этом сетевой трафик значительно сокращается, а общая производительность повышается еще и потому, что вычислительная мощность (т.е. быстрота работы процессора и объем оперативной памяти) компьютера-сервера гораздо выше, чем компьютера-клиента. Поэтому в такой архитектуре будут быстрее выполняться команды и извлекаться данные.
Если вы работаете с Visual Basic .NET, то очевидно, что в качестве серверной части (или сервера баз данных) можно использовать Microsoft SQL Server, поставляемый вместе с Visual Basic .NET и Visual Studio .NET. Перечень разных версий Microsoft SQL Server, которые поставляются вместе с Visual Basic .NET и Visual Studio .NET, приводится далее в главе.
СОВЕТ
Базы данных на основе ядра баз данных Jet (MDB) рекомендуется использовать только для простейших и ограниченных приложений. Благодаря появлению нового ядра SQL Server 2000 Desktop Engine (MSDE), предназначенного специально для баз данных небольшого размера, отпадает необходимость использования ядра Jet. Теперь размер базы данных SQL Server может расти, не требуя дополнительного кодирования или изменения ее структуры по мере роста системы.
В этой главе внимание фокусируется на основах использования SQL Server 2000. Сначала предлагается введение, посвященное его установке, а затем излагаются базовые сведения об использовании SQL Server 2000, необходимые для понимания материала и примеров в остальной части книги. Излагаемого здесь материала вполне достаточно даже для тех, у кого вообще нет никакого опыта работы с SQL Server. Более того, даже разработчики с большим опытом работы с этой СУБД найдут здесь для себя много полезного. Рассмотрим следующую типичную ситуацию.
Допустим, вы входите в команду разработчиков распределенного приложения с архитектурой клиент/сервер. Программисты, разрабатывающие серверную часть приложения, к моменту промежуточной сдачи проекта только на 95% подготовили функциональную версию сервера. Нужно приступать к работе, а серверная часть приложения не готова на все 100%.
Более того, в вашем распоряжении может быть только 1-2 программиста с опытом создания серверной части. Поскольку программисты серверной части обладают довольно редким набором навыков, такая ситуация возникает довольно часто при создании приложений с архитектурой клиент/сервер. Таких специалистов труднее всего найти и удержать, а потому они работают с гораздо большим напряжением, чем остальные программисты. Кроме того, их гораздо сложнее заменить в случае неудачного выполнения работы. Более того, программисты клиентской части часто не могут завершить свою работу до тех пор, пока не будут исправлены ошибки в серверной части.
Такуюситуациюназывают трагедией одаренного программиста серверной части.
Если вы принимали участие в проекте по созданию распределенного приложения с архитектурой клиент/сервер с несколькими разработчиками, то вам наверняка знакома эта ситуация. Одним из решений этой проблемы является создание прототипа клиентского приложения с использованием временного источника данных на основе Jet с последующей заменой его по окончании работы над серверной частью. Для этого обычно используется источник данных ODBC или OLEDB. Предлагаемый уровень абстракции на основе ODBC или OLEDB позволяет создавать и использовать в приложении прототип баз данных с переключением на реальную базу данных по окончании работа над проектом.
Размещение одного или нескольких уровней абстракции между клиентом и сервером также делает работу программистов клиентской части независимой от деятельности программистов серверной части. Для программистов серверной части это означает, что они должны создать представления или хранимые процедуры, которые поставляют данные клиентам. В среде Visual Basic NET эта цель достигается за счет создания компонентов. Более подробно способы создания представлений или хранимых процедур описываются в других разделах этой главы.
Установка и запуск Microsoft SQL Server
Работа с сервером баз данных значительно отличается от совместного использования файла базы данных Microsoft Jet. Для успешной работы с SQL Server нужно познакомиться с новыми понятиями и дополнительными возможностями. Однако установка и использование SQL Server 2000 организована гораздо проще, чем в предыдущих версиях, особенно в версии 6.5 и более ранних.
Ниже перечисляются основные условия для установки и работы разных редакций сервера базы данных SQL Server 2000.
SQL Server 2000 StandardEdition — это стандартная редакция, которая содержит основной сервер баз данных для рабочей группы или отдела.
SQL Server 2000 Enterprise Edition— это корпоративная редакция, которая содержит все элементы стандартной редакции и предлагает повышенную производительность и другие компоненты для крупных организаций, Web-узлов и хранилищ данных.
SQL Server 2000 Personal Edition— это персональная редакция, предназначенная для мобильных пользователей, которые часто отсоединены от своей сети, но нуждаются в SQL Server для локального хранения данных и запуска отдельных приложений на клиентском компьютере. В отличие от стандартной и корпоративной редакций, для которых требуется серверная версия операционной системы Windows NT или Windows 2000, персональная редакция может работать с Windows 2000 Professional, Windows NT 4.0 Workstation и Windows ME или Windows 98. Эта редакция ограничивает производительность сервера, если одновременно запущено более пяти пакетных заданий.
SQL Server 2000 Developer Edition — это редакция для разработчиков, включающая все элементы корпоративного издания, но с лицензией только для разработки и тестирования приложений SQL Server; она не может использоваться как рабочий сервер баз данных.
SQL Server 2000 Desktop Engine(MSDE)- это настольное ядро, содержащее большинство элементов стандартной редакции. Оно может свободно распространяться как часть небольшого приложения или демонстрационных версий. Размер базы данных, поддерживаемой этим ядром, не превышает 2 Гбайт и, подобно персональной редакции, ее производительность заметно падает при выполнении одновременно более пяти пакетных заданий. Однако она не содержит графических инструментов разработки и управления.
НА ЗАМЕТКУ
С каждой редакцией Visual Basic .NET или Visual Studio .NET поставляется редакция MSDE сервера баз данных SQL Server 2000. Редакции Enterprise Developer Visual Studio .NET и Enterprise Architect Visual Studio .NETтакже включают редакцию для разра- ботчиков Developer SQL Server 2000. При этом следует учитывать такие особенности:
MSDE не включает графических инструментов SQL Server, которые описываются в этой главе; поэтому с ней нельзя выполнять примеры из данной книги (тем не менее для доступа к MSDE в Visual Studio .NET предусмотрено несколько ограниченных графических инструментов);
редакция для разработчиков SQL Server 2000 Developer Edition может применяться только для разработки и тестирования приложений SQL Server, а для использования ее в качестве рабочего сервера баз данных нужно приобрести лицензии для серверной и клиентской части SQL Server 2000.
SQL Server 2000 Windows CE Edition— это редакция, используемая как хранилище данных на портативных устройствах под управлением операционной системы Windows СЕ и способная реплицировать данные из других изданий SQL Server 2000.
Требования для инсталляции SQL Server 2000
Для инсталляции SQL Server 2000 требуется компьютер с процессором Pentium (или совместимым с ним) с частотой не менее 166 МГц, пространство на жестком диске от 95 до 270 Мбайт (270 Мбайт для типичного варианта инсталляции и 44 Мбайт для Desktop Engine), дисковод для компакт-дисков, броузер Internet Explorer версии 5.0 или выше, а также совместимая операционная система (см. выше). Для оперативной памяти установлены следующие требования:
для стандартной редакции Standard Edition — минимум 64 Мбайт;
для корпоративной редакции Enterprise Edition — минимум 64 Мбайт (рекомендуется 128 Мбайт);
для персональной редакции Personal Edition — минимум 64 Мбайт для операционной системы Windows 2000 и 32 Мбайт для других операционных систем;
для редакции Developer Edition — минимум 64 Мбайт;
для ядра Desktop Edition — минимум 64 Мбайт для операционной системы Windows 2000 и 32 Мбайт для других операционных систем.
Если вы уже пытались запускать SQL Server на компьютере с процессором Pentium (или совместимым с ним) с частотой 166 МГц и 64 Мбайт оперативной памяти, то убедились, что сервер баз данных работает очень медленно. Не удивляйтесь, ведь специалисты Microsoft предупредили вас, что это минимальные требования. SQL Server может работать на компьютере с такими скудными вычислительными ресурсами, но в реальной рабочей обстановке такая экономия может привести к печальным последствиям для вашего бизнеса. Если вы ограничены в средствах и можете модернизировать только какой-то один компонент аппаратного обеспечения, то в таком случае лучше инвестировать денежные средства в оперативную память, чем в более мощный процессор. Даже небольшое увеличение размера оперативной памяти может существенно повысить производительность системы.
НА ЗАМЕТКУ ■
Данная книга в основном посвящена вопросам создания решений для работы с базами данных на основе Visual Basic .NET. Поэтому здесь не рассматриваются другие многочисленные элементы SQL Server. Для получения более подробной информации на эту тему следует обратиться к официальной документации SQL Books Online, которая входит в состав комплекта программных продуктов SQL Server, или прочитать книгу М. Шпеника и О. Следжа Руководство администратора баз данных Microsoft® SQL Server™2000 (Издательский дом “Вильямс”, 2001).
Установка SOL Server 2000
После выбора компьютера с необходимой конфигурацией можно перейти к установке. В целом процесс установки SQL Server 2000 очень прост, за исключением следующих особенностей:
он длится довольно долго;
в процессе установки задается очень много на первый взгляд странных вопросов, которые не характерны для обычных приложений.
Время процесса установки сократить никак нельзя, а чтобы упростить поиск ответов на вопросы со стороны программы-инсталлятора SQL Server, далее приводятся небольшие пояснения.
Вообще говоря, для установки SQL Server в небольших организациях достаточно использовать предлагаемые по умолчанию значения параметров в диалоговых окнах и экранах программы-мастера инсталляции. Поэтому приведенные далее комментарии относятся к наиболее сложным диалоговым окнам.
В диалоговом окне Setup Type (Тип установки), которое показано на рис. 3.1, можно выбрать тип установки: Typical (Типичная), Minimum (Минимальная) и Custom (Специализированная), а также путь к каталогам, в которых будут установлены файлы SQL Server и данные. Перед установкой убедитесь в том, что на жестких дисках этих каталогов достаточно места для размещения данных и путь к ним указан среди параметров резервного копирования данных.
РИС. 3.1.Диалоговое окно Setup Type программы SQL
Server Installation Wizard
В диалоговом окне Services Accounts (Учетные записи служб), которое показано на рис. 3.2, по умолчанию создается учетная запись пользователя домена, т.е. выбран параметр Use a Domain User account (Использовать учетную запись пользователя ДОмена). Однако вместо нее можно использовать учетную запись локальной системы, т.е. выбрать параметр Use the Local System account (Использовать учетную запись локальной системы), если нет домена или используется отдельный выделенный сервер. В этом диалоговом окне с помощью параметра Auto Start Service (Служба автоза-
пуска) можно указать, что SQL Server запускается при запуске Windows как служба этой операционной системы. Учетные записи служб действуют как составные части операционной системы, поэтому они не отображаются во вкладке (Приложения) менеджера задач Windows Task Manager и их нельзя закрыть как обычные приложения. В следующем разделе дается дополнительная информация о способах управления сервисами операционной системы Windows, а в конце главы — информация о методах запуска SQL Server.
РИС. З.2. Диалоговое окно Services Accounts граммы SQL Server Installation Wizard
Для рабочего варианта SQL Server в диалоговом окне Authentication Mode (Режим аутентификации), которое показано на рис. 3.3, по умолчанию предлагается режим Windows Authentication Mode (Режим аутентификации Windows). При этом используются
РИС. 3.3. Диалоговое окно Authentication Mode программы SQL Server Installation Wizard
(рабочем) компьютере.(рабочем) компьютере.РИС. З.4. Исходный вид диа/юго- вого окна программы SQL Server Service Manager после запуска SQL ServerРИС. З.4. Исходный вид диа/юго- вого окна программы SQL Server Service Manager после запуска SQL Server
(рабочем) компьютере.
(рабочем) компьютере.
РИС. З.4. Исходный вид диа/юго- вого окна программы SQL Server Service Manager после запуска SQL Server
РИС. З.4. Исходный вид диа/юго- вого окна программы SQL Server Service Manager после запуска SQL Server
преимущества системы обеспечения безопасности Windows NT/2000. При попытке подключения к SQL Server для аутентификации пользователя используется его учетная запись, если он (или его группа пользователей) имеют право доступа к SQL Server. В некоторых ситуациях можно использовать режим Mixed Mode (Смешанныйрежим), в котором помимо аутентификации в операционной системе Windows пользователь должен пройти процедуру аутентификации SQL Server. Для этого в SQL Server нужно создать учетную запись, которая будет проверяться при попытке подключения к SQL Server. Основным преимуществом этого режима является то, что при этом не требуется устанавливать доверительное соединение между сервером и рабочей станцией, что особенно удобно при подключении клиентов из операционной системы UNIX или Web-клиентов. Однако для реализации этого режима требуются дополнительные действия по обслуживанию двойного набора учетных записей (операционной системы Windows и сервера баз данных SQL Server).
НА ЗАМЕТКУ ——;
Часто бывает очень удобно конфигурировать компьютер разработчика в смешанном режиме, чтобы можно было использовать предустановленную учетную запись системного администратора sa. Для рабочего компьютера нужно создать более надежный и безопасный подход, т.е. по крайней мере указать другой пароль для учетной записи системного администратора sa.
Запуск и остановка SQL Server
Для запуска и остановки SQL Server можно использовать программу SQL Server Service Manager. В некоторых случаях необходимо остановить SQL Server, например для выполнения каких-то задач или запуска сервера баз данных SQL Server на другом
В обычных условиях не нужно останавливать сервер баз данных SQL Server, который играет роль корпоративной СУБД. Предполагается, что SQL Server один раз запускается и непрерывно работает в течение длительного времени. Но в некоторых редких ситуациях сервер баз данных все же бывает нужно остановить, например для обновления аппаратного обеспечения или изменения конфигурации сервера. В таких случаях для запуска и остановки SQL Server используется программа SQL Server Service Manager.
Программа SQL Server Service Manager не является обязательным элементом SQL Server и предназначена только для более удобного управления процессом активизации и деактивизации SQL Server. После перехода к рабочему варианту SQL Server программа SQL Server Service Manager уже не понадобится.
После запуска SQL Server программы SQL Server Service Manager (после щелчка на ее пиктограмме в группе программ SQL Server) диалоговое окно SQL Server Service Manager будет выглядеть так, как показано на рис. 3.4.
Если SQL Server запущен, индикатор состояния сервера баз данных приобретает вид зеленой стрелки, а если остановлен — вид красного квадратика. Для запуска SQL Server нужно щелкнуть на кнопке Start (Запуск) или Continue (Продолжить), а для остановки — на кнопке Stop. .
Управление способом запуска SQL Server
После установки SQL Server операционная система автоматически запускает его при включении компьютера. С помощью элемента панели управления Services (Службы) можно управлять состоянием SQL Server. Для просмотра текущего состояния SQL Server и управления им выполните следующее.
Откройте окно Control Panel и выберите в нем пиктограмму Administrative Tools (Администрирование).
Выберите пиктограмму Services (Службы).
Найдите в панели управления Services службу MSSQLServer.
После установки SQL Server для службы MSSQLServer по умолчанию задается автоматический режим запуска Automatic. Для остановки службы MSSQLServer в панели управления Services выполните ряд действий.
Выберите службу MSSQLServer в панели управления Services и дважды щелкните на ней. В открывшемся диалоговом окне свойств этой службы щелкните на кнопке Stop.
Спустя несколько секунд SQL Server будет остановлен.
Для перезапуска SQL Server щелкните на кнопке Start в диалоговом окне свойств службы MSSQLServer.
НА ЗАМЕТКУ
Запуск и остановка SQL Server с помощью панели управления Services происходят точно так же, как и при использовании программы SQL Server Service Manager.
Основы работы с SOL Server 2000
После установки и запуска SQL Server необходимо выполнить следующие действия, прежде чем приступить к извлечению или сохранению данных:
создать одну или несколько баз данных;
создать таблицы в базе данных;
создать представления и хранимые процедуры, которые будут управлять данными, возвращаемыми из базы данных;
установить учетные записи и группы пользователей.
Большинство из этих действий не требуют написания кода, достаточно использовать инструменты и возможности программы SQL Server Enterprise Manager СУБД SQL Server 2000.
Запуск программы SQL Server
Enterprise Manager
Большинство действий по конфигурированию базы данных выполняются с помощью программы SQL Server Enterprise Manager. Эта программа благодаря своей простоте и мощности является одним из основных инструментов SQL Server 2000. Она предоставляет администратору простой и понятный интерфейс для выполнения необходимых действий, которые прежде требовали использования сложных команд SQL.
Для запуска программы SQL Server Enterprise Manager щелкните на кнопке Start (Пуск) и выберите команду Programs^Microsoft SQL Servers Enterprise Manager (Программы1^ Microsoft SQL Server1^ Enterprise Manager). После ее запуска можно получить доступ ко всем серверам баз данных SQL Server в доступной области сети. В следующих разделах главы описываются некоторые наиболее распространенные задачи, выполняемые с помощью Enterprise Manager в рабочем приложении.
НА ЗАМЕТКУ '——■
После установки SQL Server создается только одна учетная запись sa с пустым паролем. Очевидно, что этот пароль нужно изменить, так как учетная запись с пустым паролем напоминает банковский сейф без замка. Более подробные сведения об учетных записях и системе безопасности SQL Server можно найти далее в главе.
При первом запуске программы SQL Server Enterprise Manager нужно зарегистрировать установленный SQL Server. Это позволяет программе SQL Server Enterprise Manager определить тот сервер баз данных SQL Server, с которым предстоит работать. Она также позволяет администрировать несколько установленных серверов SQL Server. Для регистрации SQL Server используется диалоговое окно Registered SQL Server Properties (Свойства зарегистрированных серверов SQL Server) программы SQL Server Enterprise Manager (рис. 3.5).
Рис. З.5. Диалоговое окно Registered SQL Server Properties программы SQL Server Enterprise Manager
107
Для регистрации SQL Server, который установлен на данном компьютере, в поле Server (Сервер) используется строка (local). Для подключения SQL Server по локальной сети нужно щелкнуть на кнопке с многоточием возле этого поля для просмотра всех имеющихся серверов в локальной сети.
СОВЕТ
Диалоговое ркно Registered SQL Server Properties содержит важный параметр Show | system databases and system objects (Показать системные базы данных и системные объекты). При снятии этого флажка системные базы данных и системные объекты остаются скрытыми в разных окнах программы SQL Server Enterprise Manager. Это позволяет сократить объем ненужной информации при работе с таблицами и фай-
лами приложения. Однако для просмотра системных объектов нужно вернуться в это диалоговое окно и установить указанный флажок.
После регистрации нужного сервера щелкните на кнопке ОК в диалоговом окне Registered SQL Server Properties. (Это нужно сделать только один раз. Программа SQL Server Enterprise Manager запоминает способ соединения с указанным сервером SQL Server.) Имя зарегистрированного сервера появится в окне серверов Microsoft SQL Servers вместе с другими зарегистрированными серверами. На компьютере с подключением к локальному серверу SQL Server окно Microsoft SQL Servers будет иметь такой вид, как на рис. 3.6.
РИС. 3.6. Окно Microsoft SQL Servers с локальным сервером баз данных SQL Server в программе SQL Server Enterprise Manager
Создание базы данных с помощью программы SQL Server Enterprise Manager
После регистрации сервера можно приступить к созданию рабочей базы данных и ее объектов: таблиц, представлений и хранимых процедур.
Это можно выполнить с помощью команд SQL, но лучше воспользоваться программой SQL Server Enterprise Manager. Дело в том, что программа SQL Server Enterprise Manager позволяет создавать большинство объектов базы данных с помощью ских инструментов без явного использования сложных команд SQL. Для создания новой базы данных с помощью SQL Server Enterprise Manager выполните ряд действий.
Щелкните правой кнопкой мыши на папке Databases в левой области окна SQL Server Enterprise Manager.
Из контекстного меню выберите команду New Database (Создать базу и на экране появится диалоговое окно Database Properties (Свойства базы данных), показанное на рис. 3.7.
Рис. З.7. Диалоговое окно Database Properties
В поле Name (Имя) введите имя базы данных (в этой главе используется имя Novelty).
По умолчанию данные хранятся в файле с именемa
журнал регистрациив файлеЭти
предлагаемые по умолчанию имена (и пути к ним) можно изменить в полях File Name (Имя файла) и Location (Расположение) во вкладках Data Files (Файлы данных) и Transaction Logs (Журналы регистрации транзакций). Вкладка Data Files показана на рис. 3.8.
РИС. З.8. Вкладка Data Files диалогового окна Database Properties для указания расположения файлов и их размеров
НА ЗАМЕТКУ ——
В отличие от ранних версий SQL Server, теперь уже не нужно предварительно определять размер памяти для файлов данных И журналов регистрации транзакций.
В SQL Server 2000 предусмотрены средства автоматического увеличения размера файлов в случае необходимости согласно заданному приросту, выраженному в мегабайтах и процентах от текущего размера. Кроме того, нужно указать максимальный размер файла так, чтобы размеры файлов не росли неограниченно вплоть до полного заполнения жесткого диска.
Щелкните на кнопке ОК, и на жестком диске появятся два новых файла, Novelty_ Data .mdfи Novelty_Data.Idf, с исходным размером 1 Мбайт.
После создания новой базы данных ее окно свойств закроется; новая база данных появится в папке Databases в левой области окна SQL Server Enterprise Manager.
НА ЗАМЕТКУ — —
Во вкладке General (Общие) можно в поле Collation name (Имя порядка сортировки) также указать имя принимаемого по умолчанию порядка сортировки данных. Имя порядка сортировки определяет используемый набор символов и порядок их сортировки и сравнения. Эта спецификация приобретает особое значение при работе с данными на других языках, отличных от английского. Кроме того, она применяется для учета (или игнорирования) регистра символов при выполнении операций сортировки и сравнения.
Создание таблиц в базе данных SQL Server
В Microsoft SQL Server таблицы можно создавать двумя способами:
с помощью языка определения данных (Data Definition Language —DDL), который подробно описывается в главе 2, "Запросы и команды на языке SQL";
с помощью графических инструментов программы SQL Server Enterprise Manager.
Оба способа создания таблиц имеют как преимущества, так и недостатки. DDL- команды языка SQL довольно сложные, особенно если вы до этого никогда их не использовали. Применение команд SQL позволяет более гибко создавать и поддерживать код создания базы данных. С другой стороны, DDL-команды могут быть полностью автоматизированы, например с помощью одного щелчка можно запустить сценарий создания базы данных. Они также предлагают более богатый уровень функциональности, который не предусмотрен в графических инструментах программы SQL Server Enterprise Manager. Кроме того, с помощью DDL-команд хотя и очень грубо, но все же можно документировать схему базы данных.
Тем не менее использование программы SQL Server Enterprise Manager позволяет быстро и легко создавать структуру базы данных с помощью инструментов с графическим пользовательским интерфейсом. Однако в программе SQL Server Enterprise Manager не так просто автоматизировать выполнение многих операций.
Некоторые разработчики предпочитают использовать для создания баз данных только команды SQL, потому что у них в распоряжении всегда остается запись выполняемых действий (в виде DDL-кода). Выбор способа создания баз данных зависит от персональных предпочтений, стандартов работы в вашей организации, а также вида создаваемых приложений для работы с базами данных. Оба способа создания базы данных рассматриваются далее в главе.
СОВЕТ
SQL Server 2000 содержит специальный элемент для генерации DDL-команд SQL для создания объектов базы данных. Его можно увидеть, щелкнув правой кнопкой мыши i на базе данных в папке Databases окна Microsoft SQL Servers, а затем выбрав в кон- I текстном меню команду All Tasks ^Generate SQL Scripts (Все задачи ^Генерация сценариев SQL) для открытия диалогового окна Generate SQL Scripts (Генерация сцена-
I риев SQL).
Использованиепрограммы SQLServer
Enterprise Manager для создания таблиц базы данных SQL Server
После создания базы данных необходимо создать в ней таблицы. Для этого с помощью программы SQL Server Enterprise Manager выполните ряд действий.
В окне Microsoft SQL Servers программы SQL Server Enterprise Manager щелкните на знаке "плюс" возле имени созданной базы данных.
Щелкните правой кнопкой мыши на объекте Tables.
Из контекстного меню выберите команду New Table (Новая таблица), и на экране появится диалоговое окно Design Table (Создание таблицы), как показано на рис. 3.9.
РИС. 3.9. Диалоговое окно конструктора таблиц в программе SQL Server Enterprise Manager
НА ЗАМЕТКУ г
При первом открытии диалогового окна Design Table в его заголовке будут слова New Table, а не Design Table.
Начнем с создания таблицы, в которой будут храниться данные о клиентах. Щелкните в столбце ColumnName и введите имя FirstName для первого поля в таблице.
Нажмите клавишу <ТаЬ> для перехода к следующему столбцу, Data Type. В этом столбце выберите из списка типов данных поле переменной длины, или varchar. Этот тип данных в SQL Server используется для хранения относительно небольшой части строковых данных.
В следующем столбце введите число 20, т.е. максимальное количество символов в поле FirstName.
В столбце Allow Nulls определяется возможность использования неопределенных значений. Если в этом столбце установлен флажок, то неопределенные значения могут быть введены в это поле. Для поля FirstName этот флажок должен быть установлен.
Введите определения остальных полей и их параметры. После определения полей окно SQL Server Enterprise Manager примет такой вид, как на рис. 3.10.
По окончании определения полей сохраните таблицу, щелкнув на кнопке Save (Сохранить).
РИС. 3.10. Диалоговое окно конструктора таблиц с определениями полей в новой таблице
НА ЗАМЕТКУ
Теперь можно создать уникальные идентификаторы для каждой записи в таблице. Поле с такими идентификаторами называется идентификационным полем. Учтите, что идентификационное поле нужно определить во время создания таблицы потому
| что его нельзя создать впоследствии после включения данных в таблицу. Дело в том, | что такое поле не может содержать неопределенные значения, а поля без неопределенных значений можно создавать только до вставки данных. В этом отношении SQL ;
) Server не обладает такой гибкостью, которой обладает база данных Microsoft Access, i но это именно та цена, которую придется заплатить за повышенную производитель- I ность и масштабируемость SQL Server. В следующем разделе более подробно опи- I сывается способ создания идентификационных полей в таблице базы данных.
После этого на экране появится диалоговое окно Choose Name (Выбор имени), в котором следует ввести имя созданной таблицы. Здесь можно ввести любой имя, но для примеров данной главы следует использовать имя tblCustomer.
Вновь созданная таблица появится в окне Microsoft SQL Servers.
Создание идентификационного поля
для уникальной идентификации записей
Полезно, но не обязательно, для каждой записи иметь информацию, которая будет уникально идентифицировать ее. Часто этот уникальный идентификатор не имеет ничего общего с бизнес-данными. В SQL Server можно создавать идентификационное поле, аналогичное полю AutoNumber в базах данных Microsoft Jet. При создании
каждой записи ей автоматически присваивается уникальное числовое значение в идентификационном поле.
Идентификационное поле в SQL Server отличается от поля AutoNumber в Microsoft Jet и обладает следующими возможностями:
любой числовой тип данных (в Jet оно может иметь тип только длинного целого числа);
увеличениесвоегозначенияна любоеопределенное число(вJetэтоможет
быть либо единица, либо случайное значение);
нумерация с любого значения (в Jet нумерация может начинаться только с 1);
восстановление значений; это позволяет вставить специфическое число в уни
кальное поле для восстановления записи, которая, например, была случайно удалена (в Jet уникальное поле всегда доступно только для чтения).
Идентификационное поле в SQL Server обладает меньшей гибкостью, чем поле AutoNumber в Jet: если вы собираетесь создать идентификационное поле, то должны сделать это при создании таблицы. Это происходит из-за того, что в SQL Server допускается позднее создание полей, содержащих неопределенные значения, а поля, не содержащие неопределенных значений, могут быть созданы только во время создания таблицы.
Чтобы создать идентификационное поле с помощью программы SQL Server Enterprise Manager, выполните ряд действий.
В окне конструктора таблицы Design Table создайте новое поле ID. Выберите для него тип данных int. Помните, что этот тип данных имеет размер четыре байта, подобно типу данных Integer в Visual Basic .NET.
Снимите флажок в столбце Allow Nulls. Это значит, что в данном поле не допускаются неопределенные значения и оно наилучшим образом подходит для превращения в идентификационное.
Нижняя часть диалогового окна Design Table содержит страницу свойств для свойств текущего выбранного поля в таблице. Щелкните в странице свойств на текстовом поле Identity.
Выберите в нем параметр Yes. По желанию укажите значения в текстовых полях Identity Seed (Начальное значение) и Identity Increment (Приращение).
После создания идентификационного поля окно SQL Server Enterprise Manager будет выглядеть, как на рис. 3.11.
Помните, что значения идентификационного поля в SQL Server не всегда последовательны. Например, если пользователь Антон пытается создать запись с ID, равным 101, а пользователь Степан создает следующую запись (ее ID равен 102) и транзакция Антона не будет выполнена, то запись с номером 101 не будет создана никогда.
В этом ничего страшного нет, особенно в том случае, когда в приложениях значение первичного ключа не используется пользователем. Однако помните, что такие "потерянные" значения возможны, так что не удивляйтесь, если, например, при просмотре номеров счетов вы не обнаружите счета с номером 101.
РИС. 3.11. Создание идттификаи,ионного поля в диалоговом окне конструктора таблиц Design Table программы SQL Server Enterprise Manager
Использованиедругихметодовдлягенерации первичных ключей
Вовсе не обязательно, чтобы каждая таблица имела первичный ключ, но в практическом отношении желательно, чтобы было именно так. Важность первичного ключа в таблице трудно переоценить. Как отмечалось в предыдущей главе, с помощью первичных ключей очень просто выполнять операции объединения нескольких таблиц в запросе. Первичный ключ можно также использовать для указания на запись в пользовательском интерфейсе. Применяя первичный ключ в передаче записи от одной процедуры к другой, можно свести к минимуму количество передаваемых данных, принадлежащих записи. Существует несколько альтернативных способов генерации первичного ключа.
Первый вариант— генерация случайного значения в поле первичного ключа для каждой создаваемой записи. Этот способ используется таблицами, содержащими поля AutoNumber, которые преобразованы при переносе данных из Microsoft Access в SQL Server. Он также используется при репликации баз данных Access для устранения коллизий между записями, которые вводятся неподключенными пользователями.
Второй вариант — сохранение значения счетчика во временной таблице и использование этого значения для создания каждого нового первичного ключа создаваемой записи. При этом необходимо использовать транзакцию, которая считывает текущее значение из таблицы счетчика, определяет с его помощью первичный ключ новой записи и увеличивает значение в таблице счетчика. Причем все эти действия представляют собой одну атомарную операцию. При
использовании этой технологии есть одно преимущество: соблюдается последовательность значений первичного ключа. К недостаткам (по сравнению с простым созданием идентификационного поля) можно отнести необходимость написания хранимой процедуры и добавления нужных таблиц к базе данных. Сохранение значения счетчика в одной таблице также может привести к возникновению конфликтов при одновременном доступе к ней, что может вызвать проблемы с производительностью в интенсивно используемой системе.
Третий вариант — создание первичного ключа на основе данных. Например, первичный ключ для записи клиента Vito Polito может иметь вид VP001. Для другого клиента с аналогичными инициалами ключ будет иметь вид VP002 и т.д. К преимуществам можно отнести ассоциацию ключа с данными, а к недостаткам — необходимость создания дополнительного кода в виде хранимых процедур.
Создание поля с первичным ключом
Созданное идентификационное поле можно использовать в качестве первичного ключа. Для создания первичного ключа таблицы с помощью программы SQL Server Enterprise Manager выполните приведенные ниже действия.
Установите курсор мыши в том поле, которое собираетесь использовать в каче-
стве первичного ключа.
Щелкните на кнопке Set primary key (Установить первичный ключ) с изображением ключа. После этого в определении таблицы появится первичный ключ, а диалоговое окно конструктора таблицы Design Table будет иметь такой вид, как на рис. 3.12. Левый столбец в перечне полей в верхней части окна содержит
РИС. 3.12. Выбор поля для создания первичного ключа в программе SQL Server Enterprise Manager
обозначения (в виде изображения ключа) полей, которые образуют первичный ключ таблицы. Учтите, что любое поле может быть первичным ключом, а не только идентификационным полем.
НА ЗАМЕТКУ
Для создания первичного ключа таблицы можно использовать несколько полей, и такой ключ называется конкатенированным ключом (concatenated key). Для него мож-
[ но использовать, например, имя и фамилию клиента. Это позволяет предотвратить
j ввод записи-дубликата для клиента Amy Rosenthal. Чтобы выбрать несколько полей для первичногополя, щелкнитена них, удерживая нажатой клавишу<Ctrl>.
Использование программы SQL Query Analyzer для доступа к базе данных
Для выполнения команд SQL Server можно использовать программу SQL Query Analyzer (раньше она называлась ISQLW). С помощью этой программы можно не только осуществлять SQL-запросы, но также обновлять записи, удалять их и выполнять другие действия над ними. Эта программа также позволяет проводить очень сложные операции с базами данных и сервером базы данных, например создавать базы данных, представления и хранимые процедуры.
РИС. 3.13. Основное окно программы SQL Query Analyzer
Если вы знакомы с синтаксисом SQL, то изучение принципов работы программы SQL Query Analyzer не составит для вас никакого труда. (Однако выполнение некоторых специализированных и сложных операций может оказаться более трудным, поэтому далее в примерах вместо SQL Query Analyzer используется программа SQL Server Enterprise Manager.)
Для создания и выполнения команд с помощью программы SQL Query Analyzer запустите ее, щелкнув на кнопке Start (Пуск) и выбрав командуSQL
AnalyzerSQLQuery Analyzer) или выбрав команду менюAnalyzerAnalyzer) в окне про
граммы SQL Server Enterprise Manager. На экране появится диалоговое окно Connect to SQL Server (Подключение к серверу SQL Server), в котором нужно выбрать подключаемый сервер баз данных, учетное имя и пароль, а затем щелкнуть на кнопке Connect (Подключиться). После этого появится основное окно программы SQL Query Analyzer (рис. 3.13). При выполнении команд на экране будут появляться дополнительные вкладки для отображения результатов, сообщений, статистических данных и планов. Более подробно они описываются далее в главе.
Перед выполнением команд SQL необходимо протестировать соединение с сервером. Для этого выполните приведенные ниже действия с базой данныхкото
рая поставляется вместе с SQL Server.
Выберите используемую базу данных. Это можно сделать с помощью SQL- команды USE. Введите следующую команду в диалоговое окно ввода запросов Query (Запрос):
USE pubs
НА
Команды SQL можно вводить как строчными, так и прописными буквами. Но ключевые слова SQL принято вводить прописными буквами, поэтому следуйте такому соглашению.
Выполните введенную команду, нажав клавишуили щелкнув на кнопке
Execute Query (Выполнить запрос) с изображением зеленого треугольника. После выполнения этой команды SQL появится вкладка Messages (Сообщения) со следующим сообщением:
Thecompleted successfully.
Что в переводе означает:
Команды успешно выполнены.
Для удаления введенной команды SQL выберите команду менюWindowОчистить окно) или используйте комбинацию клавиш
<Ctrl+Shift+Del>.
Теперь попробуйте выполнить простой запрос к базе данных pubs. Для этого введите следующий код SQL в окно Query:
SELECT *
FROM authors
Выполните запрос, нажав клавишу <F5> или щелкнув на кнопке Execute Query (Выполнить запрос) с изображением зеленого треугольника. После выполнения этой команды SQL появится вкладка Grid (Решетка) с результатами выполнения запроса (рис. 3.14) и вкладка Messages (Сообщения) со следующим сообщением о количестве извлеченных строк:
(23 row(s) affected)
Что в переводе означает:
(Извлечено 23 строки)
РИС. 3.14. Результат выполнения простого запроса к таблице pubs в окне программы SQL Query Analyzer
СОВЕТ
Помимо выполнения сразу всех команд сценария в диалоговом окне Query одну или несколько строк с командами SQL можно выполнять, выделяя нужные строки и нажимая клавишу <F5> или щелкая на кнопке Execute Query. Это позволяет повторно выполнять части команд, возможно, даже после их модификации.
Просмотр всех объектов базы данных с помощью хранимой процедуры sp_help
SQL Server позволяет просмотреть все объекты, доступные в любой базе данных. Это можно сделать с помощью хранимой процедуры^ stored procedure — небольшого кода, хранящегося и выполняемого на сервере.
Просмотреть все объекты базы данных можно с помощью хранимой процедуры sp_help. Для этого необходимо ввести ее и выполнить как обычный SQL-запрос в окне программы Query Analyzer.
Для просмотра всех объектов базы данных с помощью хранимой процедуры sp_help выполните действия, приведенные ниже.
Очистите диалоговое окно запроса Query, нажав комбинацию клавиш <Ctrl+Shift+Del>.
Введите в нем следующую команду:
sp_help
Выполните введенную команду. SQL Server сгенерирует список доступных объектов базы данных, как показано на рис. 3.15.
РИС. 3.15. Результат выполнения хранимой процедурыsp_help для базы данных pubs
НА ЗАМЕТКУ
Вы можете создавать собственные хранимые процедуры. Этот процесс описан в разделе о создании и выполнении хранимых процедур далее в этой главе. Кроме пользовательских хранимых процедур используемой базы данных, существуют и системные хранимые процедуры, которые доступны для любой базы данных на SQL Server. Примеры использования системных хранимых процедур приведены далее в главе.
Использование существующей базы данных
Для работы с определенной базой данных в программе Query Analyzer можно использовать команду USE. После выбора с ее помощью соответствующей базы данных
все вводимые SQL-команды будут выполняться для этой выбранной базы данных. Обратите особое внимание на необходимость применения команды USE, поскольку существует ряд команд, которые имеют один и тот же синтаксис при использовании с разными базами данных. Выполнение такой команды "не с той базой данных" может привести к неприятным последствиям.
НА ЗАМЕТКУ ~
Во избежание случайного ошибочного выполнения команд по отношению к "не той базе данных" нужно явно указать используемую по умолчанию базу данных вместо
I используемой по умолчанию основной базы данных master (т.е. в ней сохраняются | только данные конфигурации сервера). Для каждой учетной записи можно указать j свою используемую по умолчанию базу данных во время создания учетной записи или впоследствии с помощью способа, описанного далее в главе.
Например, для активизации базы данных novelty вместо основной базы данных master выполните перечисленные ниже действия.
Введите в окне Query Analyzer команду USE novelty. Если база данных novelty существует, то SQL Server вернет следующее сообщение:
The command(s) completed successfully.
Что в переводе означает:
Команды успешно выполнены.
Если же базы данных novelty на этом сервере нет, то появится сообщение
Server: Msg 911, Level 16, State 1, Line 1
Could not locate entry in sysdatabases for database 'novelty'. No entry found with that name. Make sure that the name is entered correctly.
Что в переводе означает следующее:
Сервер: Сообщение 911, Уровень 16, Состояние 1, Строка 1
Не могу обнаружить системную базу данных 'novelty'. Нет никаких регистрационных записей с этим именем. Убедитесь в том, что введено правильное имя базы данных.
Если вы забыли имя базы данных, то запустите программу SQL Server Enterprise Manager и найдите интересующую вас базу данных. Для просмотра объектов базы данных можно также воспользоваться хранимой процедурой sp_helpdb.
Создание команд SQL в программе Query Analyzer
С помощью программы Query Analyzer можно создавать практически любые команды SQL, причем этот способ обладает многими преимуществами по сравнению со всеми другими способами создания команд SQL. Учтите, что синтаксис некоторых команд SQL может оказаться гораздо сложнее, чем простое использование графических элементов управления в программе SQL Server Enterprise Manager, особенно при использовании редко выполняемых команд, например для создания баз данных. В этом смысле программа Query Analyzer обладает несомненным преимуществом интерактивного создания команд SQL и мгновенно реагирует на создаваемые команды. Query Analyzer обладает некоторыми другими функциональными возможностями, которых нет в программе SQL Server Enterprise Manager, например способностью запускать запросы и хранимые процедуры.
В главе 2, "Запросы и команды на языке SQL", рассматриваются некоторые наиболее часто используемые команды SQL. Большая часть этого материала относится также к выполнению запросов и хранимых процедур для SQL Server.
Использование представлений для управления доступом к данным
Представление (view) — это, по сути, определение запроса, хранящегося в базе данных. Оно подобно определению запроса в базах данных Microsoft Jet, однако отличается местом хранения: располагается в базе данных и предоставляет приложению- клиенту доступ к данным.
Представление можно использовать в том случае, если необходимо предоставить пользователю доступ к данным, но нежелательно, чтобы он мог обращаться непосредственно к данным в таблице. Представления можно рассматривать как виртуальные таблицы, которые могут использоваться точно так же, как и другие реальные таблицы базы данных. Фактически для приложения-клиента представление выглядит как обычная таблица, а кроме того, дает ему ряд преимуществ.
Например, если пользователь получает доступ к данным с помощью представления, а не посредством прямого обращения к таблице, это открывает ему следующие возможности:
изменять структуру таблицы, не меняя ассоциированного с ней представления;
ограничивать количество строк и столбцов, возвращаемых представлением;
обеспечивать простой доступ к данным, возвращаемым из нескольких таблиц, с помощью объединений в представлении.
Для того чтобы полностью использовать все преимущества представлений, необходимо разработать стратегию безопасности базы данных. Это позволяет применить специализированные разрешения для представлений, а не таблиц, что упрощает выдачу и отмену разрешений для разных типов пользователей. Эти и другие вопросы обеспечения безопасности обсуждаются далее в главе.
Создание представлений с помощью программы
SQL Server Enterprise Manager
Представления, как и большинство других объектов базы данных, можно создавать с помощью программ SQL Server Enterprise Manager или SQL Query Analyzer. Программа SQL Server Enterprise Manager обладает мощными и выразительными графическими инструментами, а программа SQL Query Analyzer— интерактивными инструментами для создания и выполнения команд SQL, а также тестирования созданных представлений.
Для создания представления с помощью программы SQL Server Enterprise Manager выполните приведенные ниже действия.
1. В диалоговом окне Server Manager щелкните правой кнопкой мыши на папке Views в той базе данных, в которой необходимо создать представление. В этом примере используется база данных pubs.
122 Jr—
2. Из контекстного меню выберите команду New View (Создать представление), и на экране появится окно конструктора представлений программы, как показано на рис. 3.16.
РИС. 3.16. Созданиенового представления в окне программы SQL Server Enterprise Manager
НА ЗАМЕТКУ
i При первом открытии диалогового окна Design View в его заголовке будут слова New View, а не Design View.
СОВЕТ
Эти графические инструменты создания представлений можно использовать для создания запросов. Для этого щелкните правой кнопкой мыши на таблице базы данных и выберите из контекстного меню команду Open Table XQuery (Открыть таблицу^Зап- । рос). Созданный запрос нельзя сохранить, потому что отдельный запрос не является объектом базы данных SQL Server. Однако конструктор базы данных полезен для создания и тестирования хранимых процедур и извлечения отдельного набора данных.
Для открытия диалогового окна Add Table (Добавить таблицу) щелкните на кнопке Add Table с изображением плюса и таблицы или щелкните правой кнопкой мыши на пустом поле со схемой представления и выберите в контекстном меню командуAdd Table.
123
Выберите таблицуи щелкните на кнопке Add (или дважды щелкните на
таблице j obs) для включения таблицы j obs в представление.
Выберите таблицу employee и щелкните на кнопке Add (или дважды щелкните на таблице employee) для включения таблицы employee в представление.
Щелкните на кнопке Close, чтобы закрыть диалоговое окно Add Table.
Выберите поля в каждой таблице, т.е. полев таблице jobs и поля
ив таблице employee.
Проверьте созданное представление, щелкнув на кнопке Run с изображением восклицательного знака или щелкнув правой кнопкой мыши на пустом поле со схемой представления, а затем выбрав изменю команду Run. Ре
зультат проверки созданного представления показан на рис. 3.17.
РИС. 3.17.создания инового представления в
окне конструктора представлений программы SQL Enterprise Manager
Сохраните новое представление, щелкнув на кнопке Save или щелкнув правой кнопкой мыши на пустом поле со схемой представления, а затем выбрав из контекстного меню команду Save.
НА ЗАМЕТКУ ——— '
Для указания имени представления можно использовать соглашение об именах с суффиксом _view, напримерЭто позволяет пользователям сразу же определить, что они имеют дело са не с таблицей.
Конечно, для этого можно использовать любое соглашение об именах, либо вообще не использовать его.
В диалоговом окне Save As (Сохранить как) введите имя представления и щелкните на кнопке ОК. Здесь можно указать любое имя, но в данном примере используется EmployeeJobs_view.
После создания представления EmployeeJobs_view в базе данных появится еще один объект, которым можно манипулировать так же, как любой простой таблицей, несмотря на то что это результат объединения двух таблиц. Таким образом, можно получить более короткие и простые команды SQL на основе этого одного объекта, но по отношению к прежней корректно структурированной (нормализованной) базе данных.
Аналогично можно создать представление на основе вычислений или манипуляций с данными таблицы. Предположим, что нужно извлечь имена сотрудников в виде одного поля с именем и фамилией в формате имя, фамилия. Для этого можно создать следующее представление:
CREATE VIEW EmployeeNames_view AS
SELECT Iname +' + fname AS Name
FROM employee
Использование представлений в приложениях
Представление — это конструкция, которая позволяет управлять процессом извлечения информации из базы данных SQL Server. Это управление может осуществляться несколькими способами. В представлении можно ограничить количество строк или столбцов, что позволяет управлять данными, возвращаемыми пользователю. Для этого необходимо создать критерий отбора, который известен только разработчику базы данных, или предоставить пользователям доступ к данным на основе разрешений системы безопасности. Каждый объект базы данных — таблица, представление, хранимая процедура — может быть ассоциирован с пользователем или группой системы безопасности. В базах данных, которые используют преимущества представлений и хранимых процедур, прямой доступ к таблицам обычно ограничивается администратором базы данных. Клиентские приложения при этом ограничены только доступом к связанным представлениям или хранимым процедурам, которые, в свою очередь, отвечают за извлечение данных из основных таблиц.
Создание скрытого поля — еще один способ ограничения доступа. При этом данные такого поля не возвращаются пользователю, а отфильтровываются с помощью представления. Клиентскому приложению даже не будет известно, что существуют еще какие-либо данные, поскольку оно будет ограничено лишь данными представления.
Практически все профессиональные серверы баз данных SQL ограничивают непосредственный доступ к базе данных. Помимо ограничения доступа к строкам и полям, сокрытие таблиц баз данных с помощью представлений позволяет изменять их незаметно для клиентских приложений.
Кроме этого, с помощью компонентов среднего уровня можно отделить изменения структуры базы данных от изменений бизнес-правил. Эти компоненты аналогичны представлениям и хранимым процедурам тем, что скрывают изменения структуры базы данных от клиентского приложения. Помимо этого, существует еще одно преимущество использования таких компонентов по сравнению с представлениями и хранимыми процедурам SQL Server: они упрощают процесс программирования, возвращают данные в виде объектов, а не строк и столбцов, а также не привязаны к
конкретной СУБД или языку программирования. Более подробно компоненты среднего уровня рассматриваются в главе 12, "Web-службы и технологии промежуточного уровня".
Создание представления с помощью программы
Query Analyzer
Для создания представлений можно использовать программу SQL Query Analyzer. Процесс создания представления в ней аналогичен подобному процессу в программе SQL Server Enterprise Manager, но обладает большей гибкостью, несмотря на использование более скромных графических инструментов. Для создания представления без конфиденциального поля Salary в программе SQL Query Analyzer выполните приведенные ниже действия.
Введите в окно программы SQL Query Analyzer следующий код (он организован таким образом, чтобы представление создавалось независимо от того, существует оно уже или
USE novelty
GO
DROP VIEW Employee_view
GO
CREATE VIEW Employee_view AS
SELECT ID, FirstName, LastName, DepartmentID
FROM tblEmployee
GO
SELECT * FROM Employee_view
Для выполнения этой последовательности команд нажмите клавишу <F5> или щелкните на кнопке Execute Query. Представление будет создано и запущено, а результаты будут показаны во вкладке Grids (или Results).
Проверьте работоспособность представления с помощью программы SQL Server Enterprise Manager, в которой нужно выбрать вкладку Views для базы данных Novelty.
НА ЗАМЕТКУ
Ранее в этой главе рассматривалось, как создать представление графическими | средствами в программе SQL Server Enterprise Manager, щелкая на нем правой кой мыши и выбирая команду Design View (Создать представление) из
Текст команды SQL для создания представления можно редактировать,|
щелкнув на представлении и изменяя этот текст в диалоговом окне.
В рассматриваемом примере, кроме создания представления с помощью программы SQL Server Enterprise Manager, демонстрируется удобство использования пакетов команд SQL. Приведенный выше пакет команд SQL не только создает новое представление, но и связывает с нужной базой данных и извлекает данные для него. Полученный результат подтверждает, что представление работает именно так, как и предполагалось.
Пакеты можно создавать для упрощения процесса создания объектов базы данных с помощью программы SQL Server Enterprise Manager, потому что в большинстве случаев при создании объектов базы данных приходится выполнять сразу несколько операций. Типичным примером пакета команд SQL является удаление старой таблицы, создание новой таблицы и наполнение ее данными. Кроме этой и других задач, пакет команд SQL может использоваться для проверки наличия учетной записи и создания учетной записи с используемым по умолчанию паролем.
Создание и запуск хранимых процедур
С помощью представления можно контролировать данные, возвращаемые SQL Server, однако существует еще более мощное средство — хранимые процедуры (stored рто- cedures). Хранимые процедуры подобны представлениям, но могут выполнять более сложные операции над данными. Например, с помощью хранимых процедур можно выполнять следующие операции:
вычисления;
установка или возврат параметров;
реализация логики приложения, для чего требуется выполнить несколько этапов или запросов с помощью языка программирования, предназначенного для использования с базой данных;
возврат данных в форме, которая наилучшим образом подходит для приложения-клиента.
Заметьте: не все эти операции можно выполнить с помощью представлений.
Хранимую процедуру можно представить себе как особый вид процедуры, так как она хранится в самой базе данных, а не является частью приложения, которое выполняется на клиентском компьютере или прикладном сервере (именно поэтому она так и называется). В предыдущем списке операций указано, что хранимая процедура может содержать не только простой однострочный запрос, но и сложный многострочный запрос, который способен выполнить несколько действий, прежде чем возвратить результат.
Хранимые процедуры создаются на специальном языке программирования, который применяется в используемой базе данных. Этот язык содержит практически все распространенные программные конструкции, хотя его синтаксис не всегда очевиден. В SQL Server для создания хранимых процедур используется язык Transact SQL.
НА ЗАМЕТКУ :
Компания Microsoft заявила, что будущие версии SQL Server позволят создавать хранимые процедуры на любом языке, который совместим с платформой .NET
I (например, Visual Basic ■NET), а не только на языке Transact SQL. Это позволит разра- jлегко переходить от одного аспекта программирования к другому, не изу
чая синтаксиса нового языка программирования.
В этом разделе не ставится задача подробного описания всех команд, которые доступны программисту при создании хранимых процедур, а только дается общее описание принципов работы хранимых процедур, достоинств и способов применения в приложениях на основе SQL Server.
127
Создание хранимых процедур с помощью программы SQL Server Enterprise Manager
Для создания хранимой процедуры с помощью SQL Server Enterprise Manager вы- полнитеряддействий.
В окне Microsoft SQL Servers программы SQL Server Enterprise Manager щелкните правой кнопкой мыши на папке Stored Procedures рабочей базы данных. В нашем примере это база данных pubs.
Из контекстного меню выберите команду New Stored Procedure (Создать хранимую процедуру). Появится диалоговое окно Stored Procedure Properties (Свойствахранимойпроцедуры).
НА ЗАМЕТКУ ;
Хотя диалоговое окно Stored Procedure Properties выглядит как окно с фиксированными размерами, его размер можно изменить, перетаскивая один из краев или уголков окна, как это делается с другими диалоговыми окнами. Таким образом можно изменить размер окна для более удобного отображения всего введенного текста.
3. Введите текст хранимой процедуры, который показан на рис. 3.18.
РИС. 3.18. Диалоговое окно создания новой хранимой процедуры Stored Procedure Properties в программе SQL Server Enterprise Manager
После выполнения этих действий щелкните на кнопке ОК в нижней части диалогового окна Stored Procedure Properties.
Запуск хранимых процедур в окне программы SQL Query Analyzer
Для запуска хранимых процедур (а также представлений и других команд SQL) можно воспользоваться программой SQL Query Analyzer. Таким образом можно протестировать созданную хранимую процедуру или представление. Для запуска хранимой процедуры в окне программы SQL Query Analyzer выполните приведенные ниже действия.
В окне программы SQL Server Enterprise Manager выберите команду Tools^SQL Query Analyzer (CepBHC^SQL Query Analyzer); запустится программа SQL Query Analyzer.
В диалоговом окне Query введите имя хранимой процедуры, которую нужно запустить. Например, для запуска хранимой процедуры, описанной в предыдущем разделе, введите procEmployeesSorted.
Выполните запрос, нажав клавишу <F5> или щелкнув на кнопке Execute Query. Хранимая процедура будет выполнена и возвратит (при наличии данных в таблице) результирующий набор во вкладке Grids (или во вкладке Results).
В окне Microsoft SQL Servers программы SQL Server Enterprise Manager выберите папку Stored Procedures для проверки наличия только что созданной хранимой процедуры. Здесь для обновления содержимого консольного окна, возможно, понадобится щелкнуть на кнопке Refresh (Обновить).
Конечно, хранимую процедуру можно запустить, непосредственно запуская программу SQL Query Analyzer, и для этого совсем не обязательно запускать ее изнутри программы SQL Server Enterprise Manager.
Создание хранимой процедуры с помощью программы SQL Query Analyzer
Процесс создания хранимой процедуры с помощью SQL Query Analyzer практически не отличается от аналогичного процесса в окне программы SQL Server Enterprise Manager.
НА ЗАМЕТКУ :
Убедитесь в том, что хранимая процедура создается в базе данных Novelty. Довольно | часто разработчики забывают переключиться на нужную базу данных (с помощью ко- | манды USE или списка баз данных в программе SQL Query Analyzer) до выполнения КО- | j манд по отношению к ней. Создание хранимых процедур с помощью программы SQL | Server Enterprise Manager только усугубляет последствия этойошибки.
Для создания хранимой процедуры с помощью программы SQL Query Analyzer ио пользуйте команду CREATE PROCEDURE.
В окне программы SQL Query Analyzer введите следующий код:
' CREATE PROCEDURE GetCustomerFromID @custID int
AS
SELECT * FROM tblCustomer
WHERE ID = ScustID
В этом коде создается хранимая процедура GetCustomerFromID, которая принимает аргумент @custID и возвращает запись, в поле ID которой находится значение, совпадающее со значением аргумента @custID (поскольку поле ID в таблице tblCustomer является первичным ключом, эта процедура всегда будет возвращать либо нуль, либо одну запись).
Выполните введенную команду для создания хранимой процедуры.
Теперь необходимо протестировать созданную хранимую процедуру в окне Query. Для этого попробуйте извлечь запись из таблицы, введя следующую команду: GetCustomerFromID 22
Сервер вернет запись клиента с идентификационным номером 22 (рис. 3.19). Использование другого идентификационного номера в качестве параметра этой хранимой процедуры позволит вернуть другую запись с данными о другом клиенте.
Созданная процедура вернет данные только в том случае, если они есть в таблице.
РИС. 3.19. Запись клиента возвращается с помоги.ъю хранимой процедуры GetCus- tomerFromID
НА ЗАМЕТКУ
Теперь можно приступить к зафузке данных в таблицу. В прилагаемых к книге файлах (их вы можете найти на Web-узле Издательского дома "Вильяме" по адресу: http: //ww. williamspublishing.com) находится текстовый файл CustomerData.sql, который предназначен для загрузки данных о клиенте в базу данных Novelty. Кроме него, no этому адресу располагаются и другие сценарии загрузки данных в таблицы базы данных Novelty.
130
Отображение текста существующих представлений или хранимых процедур
Для отображения кода представлений или хранимых процедур можно использовать хранимую процедуру sp_helptext. Для того чтобы отобразить эти данные, необходимо ввести команду sp_helptext, а затем имя интересующего вас объекта базы данных. После этого SQL Server возвратит полный текст представления или хранимой процедуры. Рассмотрим пример отображения текста представления Employee_ view, создание которого описано в предыдущих разделах.
В окне Query программы SQL Query Analyzer введите следующую команду: sp_helptext Employee_view.
Выполните введенную команду, нажав клавишу <F5> или щелкнув на кнопке Execute Query панели инструментов программы SQL Query Analyzer. В результате выполнения этой команды SQL Server вернет текст хранимой процедуры во вкладке Grids (рис. 3.20).
РИС. 3.20. Отображение текста представления с помои^ъю хранимой процедуры sp_helptext
131
Создание триггеров
Триггер (trigger) — это особый тип хранимой процедуры, который выполняется при доступе к данным в таблице. Понятие триггера в SQL Server аналогично понятию процедуры события в Visual Basic: триггер выполняется при обновлении, удалении или вставке данных в таблицу.
Триггер обычно используется при сложном доступе к данным, например: сохранение в файле журнала информации об обновлениях базы данных или же создание в новой записи сложного значения поля (по умолчанию) на основе запроса к одной или нескольким таблицам.
Не нужно использовать триггеры для поддержки ссылочной целостности; для этого лучше обратиться к встроенным средствам SQL Server. Старайтесь при работе с базами данных использовать средства, предоставляемые SQL Server.
Например, можно использовать триггеры, чтобы обеспечить уникальное значение в столбце для сохранения первичного ключа. Этот способ характерен для программы Microsoft Access Upsizing Tools; в ней генерируется случайное значение первичного ключа каждой записи с помощью триггера. (Для этого можно использовать уникальное поле, как уже упоминалось ранее в главе.) Пример такого кода генерации первичного ключа приведен ниже.
CREATE TRIGGER tblCustomer_ITrig ON dbo.tblCustomer
FOR INSERT
AS
DECLARE @randc int, @newc int
SELECT @randc = (SELECT convert(int, rand() * power(2, 30) ) )
SELECT @newc = (SELECT ID FROM inserted)
UPDATE tblCustomer SET ID = @randc WHERE ID = @newc
НА ЗАМЕТКУ :
Для корректной работы каждого из этих триггеров и обновления идентификационного поля нужно переустановить значения этого поля таким образом, чтобы оно не считалось идентификационным. Для этого перейдите в диалоговое окно Design Table и задайте для свойства Identity (Идентификационное поле) значение No.
Создание первичного ключа записи на основе случайного значения — самый простой способ уникальной идентификации записи. Однако такой способ имеет два недостатка.
Во-первых, первичный ключ генерируется в произвольном порядке. В некоторых случаях это не очень существенная проблема, однако если использовать первичный ключ для нумерации выписываемых счетов, то может случиться так, что счет с номером 20 010 будет выписан раньше, чем счет с номером 20 009.
Во-вторых, существует потенциальная проблема, состоящая в том, что сгенерированный уникальный ключ на самом деле не будет уникальным, т.е. при создании ключа не выполняется проверка существования записи с таким же значением первичного ключа. Конечно, вероятность того, что будет сгенерировано два одинаковых значения, очень мала, но она все же существует (тип данных integer в SQL Server имеет длину 4 бита, т.е. диапазон возможных значений: -2,1x10 ... 2,1x10 ).
Бизнес-ситуация 3.1: создание триггера для поиска созвучных слов
Брэд Джонс, президент компании Jones Novelties Incorporated, одобрил предварительную работу своего разработчика базы данных. Теперь он готов приступить к следующей проблеме, связанной с базой данных, и создать запросы для извлечения информации о клиентах по введенному созвучному имени (например, имена произносятся одинаково, но пишутся по-разному) с учетом случайных опечаток в нем. Как организовать поиск клиента, если вы не помните точное написание его имени, а только его произношение: Smith или Smyth, McManus или MacManus? Каждому человеку с необычной фамилией наверняка приходилось сталкиваться с ее неверной записью на слух.
Разработчик базы данных для решения этой проблемы решил воспользоваться функцией soundex (), специально предусмотренной для этого в SQL Server. Она преобразует слово в алфавитно-цифровое значение, которое представляет базовые звуки слова. Если такое значение создается в момент ввода имени, то поиск имени можно вести по его алфавитно-цифровому значению. Конечно, такой запрос вернет гораздо больше записей, чем нужно, но все они будут отвечать одному произношению.
Для реализации этого компонента в базе данных Jones Novelties нужно выполнить следующие действия:
изменить таблицу tblCustomer для вставки нового поляLastNameSoundex;
запустить запрос обновления данных для создания алфавитно-цифровых значений звучания для имен клиентов в таблице tblCustomer;
создать триггер, который сгенерирует в поле LastNameSoundex алфавитноцифровое значение звучания для введенного или измененного имени;
создать хранимую процедуру, которая возвращает всех клиентов с одинаково звучащими именами.
Разработчик базы данных начинает с создания нового поля LastNameSoundex в таблице tblCustomer для хранения в нем алфавитно-цифровых значений звучания имен всех клиентов. Это можно сделать с помощью следующей команды:
ALTER TABLE tblCustomer ADD
LastNameSoundex varchar(4) NULL
Затем разработчику нужно создать и только один раз выполнить команду UPDATE для вычисления алфавитно-цифровых значений звучания уже имеющихся имен в базе данных.
UPDATE tblCustomer
SET LastNameSoundex = soundex(lastName)
GO
SELECT LastName, LastNameSoundex
FROM tblCustomer GO
Включать команду SELECT в пакет команд SQL после команды UPDATE совсем необязательно, это нужно лишь для того, чтобы разработчик базы данных смог убедиться в правильности выполненных действий.
Далее разработчику нужно создать триггер, чтобы вставить алфавитно-цифровое значение звучания для каждого введенного в базу данных имени нового клиента.
CREATE TRIGGER trCustomerl
ON tblCustomer
FOR insert, update
AS
UPDATE tblCustomer
SET tblCustomer.LastNameSoundex =
FROM inserted
WHERE tblCustomer.ID =
НА ЗАМЕТКУ ;
Хотя в SQL Server 2000 для одной таблицы допускается определение несколькихтригте- I . ров одного типа (для вставки, обновления и удаления), но порядок их выполнения контро- | i лируется только частично, т.е. можно указать только первый и последний выполняемый ! триггер. Для гарантированного выполнения данного триггера после всех триггеров встав
ки для таблицы tblCustomer (например, триггера присвоения значения столбцу ID) нуж-
- но выполнить в программе SQL Query Analyzer следующую команду:
sp_settriggerorder @triggername= 'trCustomerl1, _
@order='last' , @stmtype='INSERT'
Причина такой сложной организации триггеров заключается в том, что они выполняются только один раз, даже если операция вставки, обновления или удаления, которая вызвала запуск триггера является частью пакета команд для тысяч записей. Поэтому созданные разработчиком базы данных триггеры должны уметь обрабатывать потенциально неограниченное количество записей.
Для обработки нужного набора записей триггеру должно быть известно, какие записи участвовали в выполнении процедуры, которая привела к ее запуску. Откуда же у триггера такие сведения? Триггеры обладают доступом к этой информации благодаря виртуальным таблицам вставки и удаления. Виртуальная таблица вставки содержит записи, вставленные (или обновленные) процедурой, которая привела к запуску триггера вставки. Аналогично, виртуальная таблица удаления содержит записи, удаленные процедурой, которая привела к запуску триггера удаления.
Поскольку разработчику базы данных в данном примере нужно создать триггер вставки и триггер удаления, то упоминание записей во вставленной виртуальной таблице охватывает все вставленные и удаленные записи, независимо от способа вставки или удаления. Каждая запись в таком случае будет иметь алфавитно-цифровое значение звучания его имени, генерированное триггером.
После создания надежного триггера, который генерирует алфавитно-цифровое значение звучания для имени для каждой записи в таблице tblCustomer, нужно проверить его работоспособность, вставив запись, которую невозможно извлечь с помощью традиционного запроса. Допустим, что в базе данных есть несколько клиентов с именем Smith, и попробуем вставить в нее запись о клиенте Smytheс помощью следующей команды INSERT: INSERT INTO tblCustomer (FirstName, LastName)
VALUES ('Abigail', 'Smythe')
Теперь работу созданного триггера можно проверить с помощью следующего запроса:
SELECT LastNameSoundex, LastName
FROM tblCustomer
WHERE LastName = 'Smythe'
После подтверждения работоспособности триггера можно создать хранимую процедуру, которая будет использовать преимущества поля LastNameSoundex. Эта процедура принимает один параметр, т.е. фамилию искомого клиента, и возвращает информацию обо всех клиентах в таблице tblCustomer, чьи имена звучат так же, как имя искомого клиента. Вот как выглядит код такой хранимой процедуры:
CREATE PROC LastNameLookup
@name varchar(40)
AS
SELECT * FROM tblCustomer
WHERE soundex(@name) = LastNameSoundex
Наконец, можно приступать к извлечению записей из базы данных на основе значений звучания. Для этого нужно выполнить хранимую процедуру LastNameLookup. LastNameLookup 'smith1
После выполнения этой процедуры программа Query Analyzer возвращает набор записей, состоящий из клиентов с фамилией, созвучной smith, включая клиента с фамилией Smythes (рис. 3.21).
РИС. 3.21. Набор записей, возвращенный хранимой процедурой LastNameLookup
Управление пользователями и средства безопасности с помощью программы SQL Server Enterprise Manager
Одной из наиболее важных причин использования сервера SQL Server является управление несколькими пользователями, которые пытаются осуществить доступ к одним и тем же данным одновременно. Хотя это приводит к возникновению большого количества проблем (например, при попытке пользователей получить доступ
к привилегированной информации или одновременно обновить одну запись), но их можно решить с помощью параметров работы сервера.
Параметры безопасности SQL Server позволяют добиться большой гибкости в определении доступа к данным. Каждая база данных обладает своим набором пользователей, которые обладают своими наборами разрешений. Набор разрешений дает пользователю возможность доступа к данным и их изменения, а также (в случае необходимости) потенциального создания и удаления самой базы данных.
Параметры безопасности SQL Server также позволяют присвоить роли отдельным пользователям для упрощения присвоения разрешений. Например, можно создать роль разработчика, который обладает доступом ко всем объектам базы данных, либо роль менеджера, который обладает доступом к конфиденциальной информации, например зарплате или продажах компании, либо роль пользователя без экстраординарных разрешений доступа к данным. Способ присвоения ролей целиком находится во власти разработчика, но для упрощения доступа к информации роли нужно использовать даже в очень простой базе данных.
Создание и сопровождение учетных записей пользователей
Для надежной и гибкой системы безопасности требуется создать систему идентификации каждого пользователя. В SQL Server система идентификации пользователей основана на учетных записях пользователей. Учетной записью (login) называется объект (как правило, человек), который обладает доступом к SQL Server. Учетные записи используются для регистрации в системе пользователей. Пользователь может обладать специальными разрешениями учетной записи для доступа к отдельной базе данных. Широкий набор разрешений пользователей определяется с помощью ролей.
Для регистрации пользователя нужно сначала создать его учетную запись, выполнив приведенные ниже действия.
В программе SQL Server Enterprise Manager откройте папку Security для используемого сервера SQL Server.
Щелкните правой кнопкой мыши на объекте Logins и выберите команду Action^New Login (Действие^ Новая учетная запись).
Появится диалоговое окно SQL Server Login Properties — New Login (рис. 3.22).
Для создания новой учетной записи установите переключатель SQL Server Authentication (Аутентификация на сервере SQL Server) и введите имя пользователя в поле Name. По желанию введите пароль в поле Password.
НА ЗАМЕТКУ™|—
При использовании аутентификации Windows нужно предусмотреть процедуру в клиентском приложении, с помощью которой пользователь мог бы изменить свой пароль. Эту процедуру можно реализовать с помощью sp_password. Учтите, что при использовании интегрированной системы безопасности это необязательно, потому что в таком случае используются механизмы аутентификации и изменения паролей системы Windows NT/2000.
136
РИС. 3.22. Диалоговое окно SQL Server Login Properties, предназначенное для создания новой учетной записи
При отображении учетной записи Windows NT/2000 на учетную запись SQL Server установите переключатель Windows Authentication (Аутентификация Windows), а затем щелкните на кнопке с многоточием для выбора существующей учетной записи Windows NT/2000.
Укажите используемую по умолчанию базу данных, выбрав Novelty в списке
баз данных Database.
Укажите базы данных, к которым осуществляется доступ с помощью этой учетной записи, воспользовавшись вкладкой Database Access (Доступ к базе данных) в диалоговом окне SQL Server Login Properties (рис.
Щелкните на кнопкепо окончании присвоения учетной записи. После
создания такой учетной записи связанный с ней пользователь может осуществлять доступ к указанной базе данных. Новая учетная запись сразу же отображается в консольном окне Microsoft SQL Servers программы SQL Server Enterprise Manager (рис. 3.24).
РИС. 3.23. Создание новой учетной записи для доступа к базе данных
Управление ролями с помощью программы SQL Server Enterprise Manager
В SQL Server 2000 роли используются для группирования пользователей с одинаковыми разрешениями. Любой пользователь отдельной роли наследует все разрешения данной роли, а изменения разрешений роли изменяют разрешения всех пользователей данной роли. Поэтому, чтобы одновременно изменить разрешения для большого количества пользователей, следует изменить разрешения их роли.
В SQL Server 2000 используются два типа ролей: серверные и роли базы данных. Серверные роли управляют доступом к операциям, которые влияют на сервер SQL Server, например запуск и остановка сервера, конфигурирование таких усовершенствованных компонентов, как репликация, управление системой безопасности и создание баз данных. Роли баз данных управляют операциями и доступом к данным указанной базы данных.
Для добавления пользователя с серверной ролью в программе SQL Server Enterprise Manager выполните перечисленные ниже действия.
В окне Microsoft SQL Servers программы SQL Server Enterprise Manager откройте папку Security используемого сервера и выберите папку Server Roles для отображения фиксированного набора серверных ролей.
РИС. 3.24. Новая учетная запись отображается в консольном окне Microsoft SQL Servers программы SQL Server Enterprise Manager
Щелкните правой кнопкой мыши на изменяемой роли и выберите команду Properties из контекстного меню или щелкните дважды на изменяемой роли. При этом на экране появится диалоговое окно Server Role Properties (Свойства серверной роли), показанное на рис. 3.25.
Чтобы указать учетную запись для данной роли, щелкните на кнопке Add и выберите учетную запись из списка доступных учетных записей.
Для удаления учетной записи из роли выберите нужную запись (или несколько записей) из списка учетных записей, которые в данный момент являются членами роли, а затем щелкните на кнопке Remove (Удалить).
Щелкните на кнопке ОК, чтобы закрыть диалоговое окно Server Role Properties.
СОВЕТ
Для включения или удаления учетной записи из серверной роли можно использовать вкладку Server Roles (Серверные роли) диалогового окна SQL Server Login Properties, которое рассматривалось выше в главе.
РИС. 3.25. Диалоговое ОКНО Server Role Properties с примером использования учетной записи дляроли Process Administrators (Администраторы процессов)
Для создания учетной записи с ролью базы данных в программе SQL Server Enterprise Manager выполните ряд действий.
В окне Microsoft SQL Servers программы SQL Server Enterprise Manager откройте папку Roles (Роли) для той базы данных, в которой необходимо создать новую роль. После этого на экране будут показаны все имеющиеся роли базы данных.
Щелкните правой кнопкой мыши на нужной роли и выберите команду Properties в контекстном меню или щелкните дважды на изменяемой роли. При этом на экране появится диалоговое окно Database Role Properties (Свойства роли базы данных), показанное на рис. 3.26.
Для добавления новой роли к базе данных щелкните на кнопке Add и выберите одного или нескольких пользователей из списка всех пользователей этой базы данных.
Для удаления пользователя из роли выберите одного или нескольких пользователей из списка всех пользователей роли и щелкните на кнопке Remove.
Щелкните на кнопке ОК, чтобы закрыть диалоговое окно Database Role Properties.
РИС. 3.26. ДиалоговоеRole Properties с примером добавления роли
db
СОВЕТ———
В SQL Server 2000, помимо предварительно заданных ролей базы данных, предусмотрено использование определенных пользователем ролей базы данных. Эти ли позволяют настроить способ доступа к данным и выполняемым операциям с базой данных (на этом этапе станет активной кнопка Permissions). Более информацию о ролях базы данных можно найти в справочных руководствах SQL Server Books Online.
Тестирование системы безопасности с помощью программыAnalyzer
Здесь у читателя может возникнуть вопрос: а что произойдет, если "обычный" пользователь попытается получить доступ к объектам базы данных, не имея разрешения. Поскольку программа SQL Query Analyzer разрешает регистрироваться под любым именем, то ответ на этот вопрос можно получить, выполнив приведенные ниже действия с помощью диалогового окна Query.
Убедитесь в том, что для проверяемой учетной записи снят флажок роли db_owner для базы данных Novelty, который показан на рис. 3.23.
Знакомство с SQL Server 2000 141
Отключитесь от используемого сервера, выбрав команду File^Disconnect (Файл=> Отключиться) или команду File^ DisconnectAll (Файл^Отключиться от всех) в меню программы SQL Query Analyzer.
Теперь выберите команду File^Connect (Файл^Подключиться). В появившемся диалоговом окне Connect to SQL Server введите имя пользователя и пароль, созданные в предыдущем разделе для подключения к базе данных Novelty.
Теперь попробуйте выполнить запрос к таблице, доступ к которой этому пользователю запрещен. Например, выполните запрос
SELECT * FROM tblCustomer
SQL Server вернет следующую строку:
Server: Msg 229, Level 14, State 5, Line 1
SELECT permission denied on object 'tblCustomer', database 'Novelty', owner 'dbo'.
НА ЗАМЕТКУ
В этом примере (особенно в п. 5) предполагается, что используется база данных | Novelty,a не какая-то другая.
Попытаемся теперь выполнить следующую хранимую процедуру:
LastNameLookup'smith'
В ответ SQL Server извлечет из таблицы tblCustomer все имена, созвучные имени Smith.
Применение ограничений безопасности в программе SQL Query Analyzer
Параметры системы безопасности можно контролировать с помощью программы SQL Query Analyzer. Обычно для этого используется тот же пакет команд SQL, что и для создания базы данных. Такой порядок позволяет разработчику учесть и применить все необходимые ограничения безопасности для вновь созданных объектов базы данных.
Для создания новой учетной записи со средствами аутентификации SQL Server с помощью программы SQL Query Analyzer следует использовать хранимую процедуру sp addlogin. Например, чтобы создать учетную запись Frances, выполните такую команду: sp_addlogin 'Frances'
А чтобы создать пароль stairmaster для учетной записи Frances, выполните команду sp_addlogin 'Frances', 'stairmaster'
Если вместо создания учетной записи SQL Server требуется добавить уже существующую учетную запись Windows, то для этого нужно аналогичным образом использовать хранимую процедуру sp_grantlogin. Учтите, однако, что в таком случае
нельзя указать пароль, так как в SQL Server пароли не являются отдельной частью определения учетной записи Windows (потому что они поддерживаются системой безопасности Windows).
Чтобы пользователь с учетной записью Frances мог работать с базой данных Novelty, используйте хранимую процедуру sp_adduser.
USE Novelty
GO
sp_adduser 'Frances', 'Frannie' GO
Для отображения списка всех учетных записей и баз данных, связанных с ними по умолчанию, в используемой вами версии SQL Server с помощью программы SQL Query Analyzer выполните приведенную ниже команду.
USE Novelty
GO
SELECT name, dbname
FROM syslogins
Для включения нового пользователя в состав группы пользователей с ролью базы данных db_datawriter выполните хранимую процедуру sp_addrolemember.
USE Novelty
GO
sp_addrolemember 'db_datawriter', 'Frannie'
Для отображения списка всех ролей базы данных выполните хранимую процедуру sp_helprole.
Чтобы выдать и отменить разрешения для любого объекта базы данных, используйте команды SQL GRANT и REVOKE соответственно. Команда GRANT разрешает пользователю выполнять указанную роль, а команда REVOKE отменяет разрешение. Например, для выдачи членам роли public разрешения на полный доступ к таблице tblCustomer воспользуйтесь следующей командой SQL:
GRANT ALL
ON tblCustomer
TO public
А для выдачи членам роли public разрешения только на выборку данных из таблицы tblCustomer воспользуйтесь командой SQL
GRANT SELECT ON tblCustomer TO public
Для отмены разрешения на полный доступ к таблице tblCustomer со стороны членов роли public используйте команду SQL REVOKE.
REVOKE ALL
ON tblCustomer
TO public
Аналогично можно указывать разрешения на обновление, выборку, удаление и вставку данных в таблицы и представления. Более того, разрешения можно выдавать и отменять специально для выполнения хранимых процедур.
143
Определение подключенных пользователей
Для того чтобы определить пользователей, подключенных к базе данных, можно использовать хранимую процедуру sp_who. При выполнении эта процедура возвращает список пользователей, которые в настоящий момент подключены к базе данных (рис. 3.27).
РИС. 3.27. Результат выполнения хранимой процедуры sp_who
С помощью списка подключенных пользователей можно выполнять разные действия, например прекращать пользовательские сеансы работы с сервером, как описывается в следующем разделе.
Завершение процесса с помощью команды KILL
В SQL Server администратор может удалить процесс, например пользовательское подключение или блокировку базы данных, с помощью команды KILL. Обычно эта команда применяется для чрезвычайного прекращения пользовательского сеанса, устранения зависшего сеанса или удаления установленной пользователем блокировки данных и продолжения работы с ними. (Эти ситуации хотя и крайне редко, но все же встречаются, особенно в процессе создания приложения.)
Перед использованием этой команды необходимо узнать идентификатор процесса, выполнив хранимую процедуру sp_who (если предполагается удалить пользовательский сеанс) или хранимую процедуру sp_lock (если предполагается удалить бло-
^ /| ^|мнм^мвмавянмнм
кировку базы данных). В обоих случаях в поле spid возвращаются идентификаторы всех процессов. Зная идентификатор нужного процесса, его можно удалить.
Допустим, что после выполнения хранимой процедуры sp_who вы узнали номер процесса, например 10, который необходимо удалить. Для его удаления введите в окне Query программы SQL Query Analyzer команду
KILL 10
После выполнения этой команды указанный процесс будет немедленно удален.
НА ЗАМЕТКУ |—*
Рекомендуется периодически выполнять процедуру sp_who, особенно в процессе создания приложения, только для того, чтобы знать, что происходит с вашей базой данных.
Удаление объектов базы данных
В SQL Server для удаления объектов используется команда DROP. Удаленный с ее помощью объект исчезает безвозвратно, причем удаляется не только структура объекта, но и его содержимое.
Для удаления объекта с помощью программы Enterprise Manager, например таблицы, выделите ее, щелкните правой кнопкой мыши и из контекстного меню выберите команду Delete.
А для удаления объекта из базы данных с помощью программы SQL Query Analyzer используйте команду DROP. Например, для удаления таблицы tblCustomer введите следующую команду:
DROP TABLE tblCustomer
Бизнес-ситуация 3.2: SQL-сценарий для создания базы данных
Для запуска примеров кода требуется запустить сценарий NoveltyDB . sql, который предназначен для создания и запуска базы данных Novelty. (Наверное, вы уже заглядывали в него и интересовались тем, что он делает.)
Поскольку структура базы данных может изменяться в процессе создания приложения, разработчику нужно периодически создавать сценарий для документирования текущей структуры базы данных, а также для автоматического создания (или воссоздания) базы данных. Хотя разработчик может обладать полным контролем и вручную создавать такие сценарии, но гораздо лучше применять для этого программу SQL Server Enterprise Manager. По крайней мере она прекрасно подходит для создания основы сценария и его последующего редактирования. Этот подход позволяет гораздо быстрее добиться решения поставленной задачи с меньшей вероятностью возникновения ошибок.
Разработчик базы данных для компании Jones Novelties Incorporated решил применить этот подход для создания корпоративной базы данных. Созданный им сценарий позволяет просто инсталлировать объекты базы данных на компьютере, на кото-
145
ром она даже не определена. Иначе говоря, сценарий выполняет все необходимые действия для создания и инсталляции базы данных и ее объектов на совершенно "пустом" компьютере, что обычно требуется при инсталляции системы на новом рабочем месте. На рис. 3.28 показана схема базы данных Novelty в диалоговом окне программы SQL Server Enterprise Manager.
7#l SU* Server t ill it|imi-M.i...,M-T |Z I -hl Ui.Hirmn ЧНА1 .KAMI* m N.vliy m lUU Al )'|HR Q7#l SU* Server t ill it|imi-M.i...,M-T |Z I -hl Ui.Hirmn ЧНА1 .KAMI* m N.vliy m lUU Al )'|HR QtbiCiJS tamerIDRrstNaiTW La-stNameCompanyAddressCityStatePost aK odePhoneFaxEmailLast Name SoundextbiCiJS tamerIDRrstNaiTW La-stNameCompanyAddressCityStatePost aK odePhoneFaxEmailLast Name SoundextbiReototrtbiReototrtbiOderltemtbiOderltemID OrderlD ItemID QuanttyID OrderlD ItemID Quantty10 State [RegionName10 State [RegionNametijilnwntary ':kWi Product Ne me; IwhclesalePiiceiRetaFnce|jy Descriptiontijilnwntary ':kWi Product Ne me; IwhclesalePiiceiRetaFnce|jy DescriptionРИС. 3.28. Структура базы данных Novelty компании Jones Novelties Incorporated в диалоговом окне программы SQL Server Enterprise ManagerРИС. 3.28. Структура базы данных Novelty компании Jones Novelties Incorporated в диалоговом окне программы SQL Server Enterprise Manager
7#l SU* Server t ill it|imi-M.i...,M-T |Z I -hl Ui.Hirmn ЧНА1 .KAMI* m N.vliy m lUU Al )'|HR Q
7#l SU* Server t ill it|imi-M.i...,M-T |Z I -hl Ui.Hirmn ЧНА1 .KAMI* m N.vliy m lUU Al )'|HR Q
tbiCiJS tamer
ID
RrstNaiTW La-stName
Company
Address
City
State
Post aK ode
Phone
Fax
Last Name Soundex
tbiCiJS tamer
ID
RrstNaiTW La-stName
Company
Address
City
State
Post aK ode
Phone
Fax
Last Name Soundex
tbiReototr
tbiReototr
tbiOderltem
tbiOderltem
ID OrderlD ItemID Quantty
ID OrderlD ItemID Quantty
10 State [RegionName
10 State [RegionName
tijilnwntary ':
kWi Product Ne me
; IwhclesalePiice
iRetaFnce
|jy Description
tijilnwntary ':
kWi Product Ne me
; IwhclesalePiice
iRetaFnce
|jy Description
РИС. 3.28. Структура базы данных Novelty компании Jones Novelties Incorporated в диалоговом окне программы SQL Server Enterprise Manager
РИС. 3.28. Структура базы данных Novelty компании Jones Novelties Incorporated в диалоговом окне программы SQL Server Enterprise Manager
Сценарий создания базы данных выполняется в программе SQL Query Analyzer. Чтобы сократить до минимума ошибки, этот сценарий удаляет все прежние объекты базы данных. Это значит, что при любом изменении структуры базы данных в режиме конструктора целостность базы данных может быть восстановлена с самого начала просто за счет повторного выполнения сценария. Этот подход гарантирует, что все изменения структуры базы данных учитываются каждый раз при ее повторном создании.
Недостатком этого подхода является то, что если разработчик будет недостаточно аккуратен, то сценарий удалит все таблицы в базе данных вместе с содержащимися в них данными. Поэтому все такие сценарии нужно удалить после переноса создаваемой системы на рабочее место. При этом, как обычно, большое значение имеет резервное копирование базы данных еще до внесения в нее каких-либо существенных изменений для восстановления в случае непредвиденных сбоев.
Для генерации сценариев создания базы данных для компании Jones Novelties Incorporated выполните перечисленные ниже действия.
Откройте программу SQL Server Enterprise Manager и щелкните правой кнопкой мыши на базе данных Novelty.
Выберите команду All Tasks^ Generate SQL Script (Все задачи^Генерировать сценарий SQL) из контекстного меню для открытия диалогового окна Generate SQL Scripts.
Щелкните на кнопке Show All для отображения всех доступных объектов выбранной базы данных.
Теперь у вас есть возможность выбора одного или нескольких объектов сценария. Установите флажок Script all objects (Включить в сценарий все объекты), чтобы выбрать все объекты базы данных, как показано на рис. 3.29.
РИС. 3.29. Выбор всех объектов базы данных Novelty для включения в сценарий в диалоговом окне Generate SQL Scripts
Выберите вкладку Formatting (Форматирование). Помимо установленных по умолчанию флажков параметров установите флажок Include descriptive headers in the Script files (Включить описательные заголовки в файлы сценариев).
СОВЕТ —:
Не забудьте установить во вкладке Formatting флажок Include descriptive headers in the script files, потому что он приводит к автоматическому созданию строки заголовка с указанием времени и даты генерации сценария. Это очень полезное усовершенствование позволит вам впоследствии легко определить самую последнюю (или наиболее правильную) версию сценария.
Если вам нужен сценарий только для создания объектов базы данных, то на этом можно остановиться. Однако для генерации сценария создания физической базы данных нужно продолжить работу, выбрав вкладку Options (Параметры).
В группе параметров Security Scripting Options (Параметры сценария безопасности) установите флажок Script (Включить в сценарий базу данных). Кроме него, можно также установить флажки другихдля пользователей
базыролей и учетных записей, но здесь мы этого делать не будем, счи
тая это отдельной задачей администрирования базы данных.
В группе параметров Table Scripting Options (Параметры сценария таблицы) установите флажки Script indexes (Включить в сценарийScript triggers
(Включить в сценарий триггеры) и Script PRIMARY keys, FOREIGN keys, defaults, and check constraints (Включить в сценарий первичные ключи, внешние ключи, принимаемые по умолчанию значения и ограничения целостности), как показано на рис. 3.30.
РИС. 3.30. Вкладка Options диалогового окна Generate SQL
Щелкните на кнопке OK для запуска процесса генерации сценария. При этом на экране появится стандартное диалоговое окно Save As (Сохранить как), в котором следует ввести имя файла (с расширением .и указать путь к нему.
Сделайте это и щелкните на кнопке Save. После успешного создания сценария на экране появится диалоговое окно с сообщением об этом. Щелкните на кнопке ОК для его удаления.
НА ЗАМЕТКУ
Созданный вамиможет отличаться от приведенного ниже из-за использованиясервера или параметров базы данных. Однако сейчас об
этом не следует
148
сценарий содержит множество строк с командой GO, которая предназначена для принудительного выполнения предыдущих команд еще до перехода к следующим командам сценария. Например, довольно часто можно встретить такие блоки
SET QUOTED_IDENTIFIER OFF
GO
iETON
GO
ИЛИ
ON
GO
SETON
GO
Они гарантируют корректную (временную) конфигурацию базы данных для выполнения нужной операции и переустановку параметров для выполнения следующих операций.
Далее созданный сценарий предлагается в виде отдельных фрагментов, но на практике его рекомендуется создавать и применять в виде целого файла. В таком случае после создания рабочего сценария можно вставить его текст в окно Query программы SQL Query Analyzer (либо загружая его из тестового файла, либо копируя и вставляя с помощью буфера обмена) и щелкнуть на кнопке Execute для запуска. Можно также использовать отдельные фрагменты сценария или создать на основе единого сценария несколько отдельных сценариев и выполнять каждый из них. При создании сценариев SQL с помощью программы SQL Query Analyzer отдельные команды SQL можно редактировать в диалоговом окне Query, тестировать и проверять результаты их выполнения, а затем сохранять их в файле после завершения этих операций.
Сначала нужно создать физическую базу данных, с помощью сценария, который представлен в листинге
ЛистингСценарий создания физической базы данных
Object: Database Novelty Script Date: 10-Jul-02 12:41:09
IF EXISTS (SELECT name FROMWHERE name = N'Novelty1)
DROP DATABASE [Novelty]
GO
CREATE DATABASE [Novelty] ON (NAME =
FILENAME =server\
,
SIZE 3,= 10%) LOG ON (NAME =
FILENAME =sql server\
,= 3, FILEGROWTH = 10%)
COLLATE Latinl_General_CI_AI
GO
Перед попыткой создания любого нового объекта сценарий всегда проверяет его существование, а затем удаляет его. Поэтому в данном сценарии после проверки и удаления существующей базы данныхсоздается новая база данных.
Код в листинге 3.2 задает параметры базы данных, описание которых приводится в официальной справочной документации SQL Server Books Online.
Листинг 3.2. Сценарий указания параметров базы данных Novelty
exec
GO
sp_dboption
N1 Novelty',
N'autoclose', N'false'
exec
GO
sp_dboption
N'Novelty',
N'bulkcopy', N'false'
exec
GO
sp_dboption
N'Novelty',
N'trunc. log', N'false'
exec
GO
sp_dboption
N1 Novelty1,
N'torn page detection', N'true'
exec
GO
sp_dboption
N'Novelty',
N'read only', N'false'
exec
GO
sp_dboption
N'Novelty',
N'dbo use ', N'false'
exec
GO
sp_dboption
N'Novelty',
N'single', N'false'
exec
GO
sp_dboption
N'Novelty',
N'autoshrink', N'false'
exec
GO
sp_dboption
N'Novelty',
N'ANSI null default', N'false'
exec
GO
sp_dboption
N'Novelty',
N'recursive triggers', N'false'
exec
GO
sp_dboption
N'Novelty',
N'ANSI nulls', N'false'
exec
GO
sp_dboption
N'Novelty',
N'concat null yields null', N'false'
exec
GO
sp_dboption
N'Novelty',
N'cursor close on commit', N'false'
exec
GO
sp_dboption
N'Novelty',
N'default to local cursor', N'false'
exec GO
sp_dboption
N'Novelty',
N'quoted identifier', N'false'
exec
GO
sp_dboption
N'Novelty',
N'ANSI warnings', N'false'
exec
GO
sp_dboption
N'Novelty',
N'auto create statistics', N'true'
exec
GO
sp_dboption
N'Novelty',
N'auto update statistics', N'true'
Теперь после создания базы данных можно приступить к работе с ней, т.е. начать выполнять команды по отношению к ней. Для выполнения команд SQL с другой базой данных NoveltyTest (например, для ее тестирования на том же сервере) нужно просто указать другую базуданных с помощью командыUSE.
USE [NoveltyTest]
GO
Далее в сценариях создания объектов базы данных также проверяется их наличие и удаление (если они имеются). Это относится к ограничениям, триггерам, хранимым процедурам, представлениям и таблицам. Этот порядок имеет большое значение, тому что таблицу нельзя удалить, если существует какой-то из связанных с ней объектов. Код выполнения этих операций приведен в листинге 3.3.
Листинг 3.3. Сценарий удаления существующих объектов в базе данных
if exists (select * fromwhere id =
object_id(N'[dbo].[FK_tblOrder_tblCustomer]') and= 1)
ALTER TABLEDROP CONSTRAINT
GO if exists (select * fromwhere id =
object_id(N'[dbo].[FK_tblEmployee_tblDepartment]') and= 1)
ALTER TABLEDROP CONSTRAINT
FK_tblEmployee_tblDepartment GO if exists (select * fromwhere id =
object_id(N' [dbo].[FK_tblOrderItem_tblInventory] ') and= 1)
ALTER TABLEDROP CONSTRAINT
FK Go" if exists (select * fromwhere id =
object_id(N'[dbo].[FK_tblOrderItem_tblOrder]') and= 1)
ALTER TABLEDROP CONSTRAINT
GO if exists (select * fromwhere id =
object_id(N'[dbo].[FK_tblCustomer_tblRegion] ') and= 1)
ALTER TABLEDROP CONSTRAINT FK_tblCustomer_tblRegion
GO object: Trigger Script Date: 10-Jul-02 12:41:09 if exists (select * fromwhere id =
object_id(N'[dbo].[trCustomerl]') and= 1)
drop trigger GO
/****** Object: Stored Procedure
Script Date: 10-Jul-02 12:41:09 PM
if exists (select * fromwhere id =
object_id(N1[dbo].[DeleteEmployee]1)
and= 1)
drop procedure GO
/****** object:Stored Procedure
Script Date: 10-Jul-02 12:41:09 PM
if exists (select * fromwhere id =
object_id(N'[dbo].[GetCustomerFromID]')
and= 1)
drop procedure GO
Object: Stored Procedure
Script Date: 10-Jul-02 12:41:09 PM
if exists (select * fromwhere id =
object_id(N'[dbo].[InsertEmployee]')
and= 1)
drop procedure GO
Object: Stored Procedure
Script Date: 10-Jul-02 12:41:09 PM
if exists (select * fromwhere id =
object_id(N'[dbo].[InsertEmployeeOrg]')
and= 1)
drop procedure
GO
/****** Object: Stored Procedure
Script Date: 10-Jul-02 12:41:09 PM
if exists (select * fromwhere id =
object_id(N'[dbo].[LastNameLookup]')
and= 1)
drop procedure GO
object:Stored Procedure
Script Date: 10-Jul-02 12:41:09 PM ******/
if exists (select * fromwhere id =
object_id(N'[dbo].[SelectEmployees]')
and= 1)
drop procedure GO
object:Stored Procedure
Script Date: 10-Jul-02 12:41:09 PM
if exists (select * fromwhere id =
object_id(N'[dbo].[UpdateEmployee]')
and= 1)
drop procedure GO
object:Stored Procedure
Script Date: 10-Jul-02 12:41:09 PM ******/
Глава 3 —
if exists (select * fromwhere id =
object_id(N'[dbo].[procEmployeesSorted]') and=
drop procedure GO
/****** object: View
Script Date: 10-Jul-02 12:41:09******/
if exists (select * fromwhere id =
object_id(N'[dbo].[EmployeeDepartment_view]')
and= 1)
drop view
GO
Object: View
Script Date: 10-Jul-02 12:41:09 PM ******/
if exists (select * fromwhere id =
object_id(N'[dbo].[qryEmployee_view]')
and= 1)
drop view GO
/****** Object: Table
Script Date:12:41:09 PM
if exists (select * fromwhere id =
object_id(N'[dbo].[tblCustomer]')
and= 1)
drop table GO
/****** Object: Table
Script Date: 10-Jul-02 12:41:09 PM ******/
if exists (select * fromwhere id =
object_id(N'[dbo].[tblDepartment]')
and= 1)
drop table GO
/****** Object: Table
Script Date: 10-Jul-02 12:41:09 PM
if exists (select * fromwhere id =
object_id(N'[dbo].[tblEmployee] ')
and= 1)
drop table GO
/****** Object: Table
Script Date: 10-Jul-02 12:41:09 PM
if exists (select * fromwhere id =
object_id(N'[dbo].[tbllnventory] ’)
and= 1)
drop table GO
/****** Object: Table
Script Date: 10-Jul-02 12:41:09 PM ******/
if exists (select * fromwhere id =
object
153
and= 1)
drop table
GO
/****** object: Table
Script Date: 10-Jul-02 12:41:09******/
if exists (select * fromwhere id =
object_id(N'[dbo].[tblOrderltem]') and= 1)
drop table GO
object: Table [dbo] . [tblRegion] Script Date: 10-Jul-02 12:41:09 PM
if exists (select * fromwhere id =
object_id(N'[dbo].[tblRegion]')
and= 1)
drop table
GO
Теперь можно приступать к созданию новых объектов базы данных. Начнем с создания таблиц; сценарий этих операций приведен в листинге 3.4.
Листинг 3.4. Сценарий создания таблиц базы данных Novelty
Object: Table
Script Date: 10-Jul-02 12:41:10 PM ******/
CREATE TABLE(
[ID] [int] IDENTITY (1, 1) NOT NULL ,
[FirstName] [varchar] (20) COLLATENULL ,
[LastName] [varchar] (30) COLLATE Latinl_General_CI_AI NULL ,
[Company][varchar](50)COLLATELatinl_General_CI_AI NULL,
[Address][varchar](50)COLLATELatinl_General_CI_AI NULL,
[City] [varchar] (30) COLLATENULL ,
[State] [char] (2) COLLATENULL ,
[PostalCode] [varchar] (9) COLLATE Latinl_General_CI_AI NULL ,
[Phone] [varchar] (15) COLLATE Latinl_General_CI_AI NULL ,
[Fax] [varchar] (15) COLLATE Latinl_General_CI_AI NULL ,
[Email] [varchar] (100) COLLATE Latinl_General_CI_AI NULL ,
[LastNameSoundex] [varchar] (4) COLLATENULL
) ON [PRIMARY]
GO
object: Table
Script Date: 10-Jul-02 12:41:11 PM
CREATE TABLE(
[ID] [int] IDENTITY (1, 1) NOT NULL ,
[DepartmentName] [varchar] (75) COLLATE Latinl_General_CI_AI NOT NULL
) ON [PRIMARY]
GO
Object: Table
Script Date: 10-Jul-02 12:41:11 PM ******/
CREATE TABLE(
[ID] [int] IDENTITY (1, 1) NOT NULL ,
[FirstName] [varchar] (50) COLLATE Latinl_General_CI_AI NOT NULL ,
[LastName] [varchar] (70) COLLATE Latinl_General_CI_AI NOT NULL , [int] NULL ,
[Salary] [money] NULL ) ON
GO
/****** object: Table
Script Date: 10-Jul-02 12:41:11
CREATE TABLE(
[ID] [int] IDENTITY (1, 1) NOT NULL , [varchar] (75) COLLATE Latinl_General_CI_AI NOT NULL [money] NULL , [RetailPrice] [money] NULL , [Description] [ntext] COLLATE Latinl_General_CI_AI NULL
) ON [PRIMARY][PRIMARY]
GO
/****** object: Table
Script Date: 10-Jul-02 12:41:12 PM ******/
CREATE TABLE(
[ID] [int] IDENTITY 1) NOT NULL , [int] NULL , [OrderDate]NULL ,
[Amount] [money] NULL ) ON [PRIMARY] GO
object: Table
Script Date: 10-Jul-02 12:41:12 PM
CREATE TABLE(
[ID] [int] IDENTITY (1, 1) NOT NULL , [int] NOT NULL , [int] NOT NULL , [Quantity] [int] NULL , [Cost] [money] NULL ) ON [PRIMARY] GO
object: Table
Script Date:12:41:12 PM
CREATE TABLE(
[ID] [int] IDENTITY (1, 1) NOT NULL ,
[State] [char] (2) COLLATE Latinl_General_CI_AI NOT NULL , [varchar] (25) COLLATE Latinl_General_CI_AI NULL ) ON [PRIMARY]
_
Далее следует создать ограничения, как показано в листинге 3.5.
Листинг 3.5. Сценарий создания ограничений для базы данных Novelty
ALTER TABLEWITH NOCHECK ADD
CONSTRAINTPRIMARY KEY CLUSTERED
( [ID] ) ON [PRIMARY]
GO
ALTER TABLEWITH NOCHECK ADD
CONSTRAINTPRIMARY KEY CLUSTERED
155
[ID]
) ON [PRIMARY] GO
ALTER TABLE [dbo].[tblEmployee] WITH NOCHECK ADD CONSTRAINT [PK_tblEmployee] PRIMARY KEY CLUSTERED (
[ID]
) ON [PRIMARY] GO
ALTER TABLE [dbo].[tbllnventory] WITH NOCHECK ADD CONSTRAINT [PK_tblInventory] PRIMARY KEY CLUSTERED
I
[ID]
) ON [PRIMARY] GO
ALTER TABLE [dbo].[tblOrder] WITH NOCHECK ADD CONSTRAINT [PK_tblOrder] PRIMARY KEY CLUSTERED (
[ID]
) ON [PRIMARY] GO
ALTER TABLE [dbo].[tblOrderltem] WITH NOCHECK ADD CONSTRAINT [PK_tblOrderItem] PRIMARY KEY CLUSTERED (
[ID]
) ON [PRIMARY] GO
ALTER TABLE [dbo].[tblRegion] WITH NOCHECK ADD CONSTRAINT [PK_tblRegion] PRIMARY KEY CLUSTERED (
[ID]
) ON [PRIMARY]
GO
CREATE UNIQUE INDEX [IX_tblRegion] ON [dbo].[tblRegion]([State]) ON [PRIMARY]
GO
ALTER TABLE [dbo].[tblCustomer] ADD CONSTRAINT [FK_tblCustomer_tblRegion] FOREIGN KEY
(
[State]
) REFERENCES [dbo].[tblRegion] ( [State]
) ON DELETE CASCADE ON UPDATE CASCADE GO
ALTER TABLE [dbo].[tblEmployee] ADD
CONSTRAINT [FK_tblEmployee_tblDepartment] FOREIGN KEY (
[DepartmentID]
) REFERENCES [dbo].[tblDepartment] (
[ID]
) ON DELETE CASCADE ON UPDATE CASCADE GO
ALTER TABLEADD
CONSTRAINTFOREIGN KEY
(
[CustomerlD]
) REFERENCES(
[ID]
) ON DELETE CASCADE ON UPDATE CASCADE GO
ALTER TABLEADD
CONSTRAINT [FK_tblOrderItem_tblInventory] FOREIGN KEY
(
[ItemlD]
) REFERENCES(
[ID]
) ON DELETE CASCADE ON UPDATE CASCADE , CONSTRAINTFOREIGN KEY
(
[OrderlD]
) REFERENCES(
[ID]
) ON DELETE CASCADE ON UPDATE CASCADE GO
SETOFF
GO
SET ANSI_NULLS ON
GO
Наконец, для создания представлений, хранимых процедур и триггеров следует выполнить сценарий из листинга 3.6.
Листинг 3.6. Сценарий создания представлений, хранимых процедур и триггеров
View
Script Date: 10-Jul-02 12:41:13
CREATE view EmployeeDepartment_view as
selectFirstName, LastName,
frome,t
where=
GO
SETOFF
GO
SETON
GO
SET QUOTED_IDENTIFIER ON
GO
SETON
GO
/****** Object: View
Script Date: 10-Jul-02 12:41:13 PM ******/
create viewas
SELECT ID,DepartmentID
FROM
GO
SETOFF
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON GO
/****** Object: Stored Procedure
Script Date: 10-Jul-02 12:41:13 PM ******/ CREATE PROCEDURE
I @Original_ID int )
AS
SET NOCOUNT OFF;
DELETE FROM tblEmployee WHERE (ID = @Original_ID) GO
SETOFF
GO
SET ANSI_NULLS ON GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON GO
Object:Stored Procedure
Script Date: 10-Jul-02 12:41:13 PM ******/ create procedure GetcustomerFromID
@custID int as select * from tblCustomer where ID =
GO
SETOFF
GO
SETON
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI NULLS OFF
GO
/****** Object:Stored Procedure
Script Date: 10-Jul-02 12:41:13 CREATE PROCEDURE
@FirstName varchar(50),
@LastName varchar(70), int, money
)
AS
SET NOCOUNT OFF;
if (@Salary = 0 or @Salary is null) begin
- Do complicated salary calculations
set @Salary = @DepartmentID * 10000 end
INSERT INTOLastName,Salary) VALUES
(@FirstName, @LastName, @DepartmentID, @Salary)
GO
SET QUOTED_IDENTIFIER OFF
GO
SETON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO
Object:Stored Procedure
Script Date: 10-Jul-02 12:41:13 PM
CREATE PROCEDURE
(
@FirstName varchar(50),
@LastName varchar(70),
int,
SSalary money ) AS
SET NOCOUNT OFF;
INSERT INTOLastName, DepartmentID, Salary) VALUES
@LastName, GO
SETOFF
GO
SET ANSI_NULLS ON GO
SETON
GO
SET ANSI_NULLS ON
GO
2 15g
Object: Stored Procedure
Script Date: 10-Jul-02 12:41:13******/
create proc
@name varchar(40)
as select * from where= LastNameSoundex
GO
SET QUOTED_IDENTIFIER OFF
GO
SETON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO
Object:Stored Procedure
Script Date: 10-Jul-02 12:41:13 PM
CREATE PROCEDURE
AS
SET NOCOUNT
SELECT FirstName, LastName,Salary, ID FROM tblEmployee
GO
SETOFF
GO
SETON
GO
SET QUOTED_IDENTIFIER ON
GO
SETON
GO
Object: Stored Procedure
Script Date: 10-Jul-02 12:41:13 PM
CREATE PROCEDURE
@FirstName varchar(50),
@LastName varchar(70),
int,
money, @Original_ID int )■
AS SET NOCOUNT OFF;
UPDATE tblEmployee SET FirstName =LastName = @LastName, De-
partmentID =Salary = @Salary WHERE (ID = @Original_ID)
GO
SET QUOTED_IDENTIFIER OFF
GO
SETON
GO
SETOFF
GO
SET ANSI_NULLS OFF
GO
Object:Stored Procedure
Script Date: 10-Jul-02 12:41:13 PM ******/
CREATE PROCEDURE procEmployeesSorted AS
select * from tblEmployee
order byFirstName
return GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SETON
GO
/****** Object: Trigger
Script Date: 10-Jul-02 12:41:14 PM ******/ create trigger on for insert, update as
update tblCustomer
set=
from inserted where=
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
Хотя сценарий, созданный автоматически с помощью программы SQL Server Enterprise Manager, достаточно хорош для запуска его даже в том состоянии, в котором он находится, его можно отредактировать нужным вам образом. Учтите, что при повторной генерации сценария внесенные вручную изменения будут утрачены.
Полезной модификацией сценария может стать включение командыв стра
тегически важных местах сценария, например для отображения некоторых фрагментов текста в диалоговом окне Messages программы SQL Query Analyzer. Это позволит отслеживать прогресс выполнения сценария, а также использовать выводимые сообщения для отладки сценария: поиска и устранения замеченных ошибок. Использование команды PRINT совсем не обязательно и не имеет прямой связи с процессом создания базы данных.
Помните, что при использовании подобных пакетов команд SQL их можно запускать повторно нужное количество раз. Дело в том, что он создан так, чтобы полностью удалить и воссоздать повторно базу данных. При загрузке тестовых данных для
161
проверки работоспособности базы данных не следует беспокоиться о повреждении или утрате данных при выполнении такого сценария. Кроме того, создавая базу данных с помощью пакета команд SQL, можно легко переносить структуру базы данных на другие серверы. Это позволяет создавать две совершенно разные физические базы данных: одну для создания приложения, а другую для работы.
Резюме
В этой главе представлены основные сведения об использовании СУБД SQL Server компании Microsoft для создания распределенных вычислительных систем. И хотя здесь рассматривается SQL Server 2000 компании Microsoft, все описанные основные действия по управлению базами данных, но уже с особенностями реализации, применимы и к СУБД других компаний — Oracle, Sybase, Informix и пр. Не забывайте, что при наличии драйвера ODBC или провайдера OLE DB, которые позволяют при разработке приложений абстрагироваться от конкретной реализации СУБД, с этими базами данных можно работать с помощью Visual Basic .NET.
Вопросы и ответы
Меня всегда пугала сложность работы с SQL Server, потому что эта СУБД казалась мне "черным ящиком". Мне знакомы случаи, когда люди чуть ли не сходили с ума от работы по созданию серверной части приложения. Неужели материала этой главы будет достаточно?
Можно сказать, и да и нет. В этой главе рассматриваются только основы использования SQL Server и совсем не охвачены вопросы ежедневного администрирования базы данных, настройки производительности и т.д. Эту главу следует рассматривать как введение в SQL Server, а не подробное руководство по его использованию. В первой части главы приведены необходимые сведения, которые могли бы облегчить работу с SQL Server и развеять опасения, что для этого нужны какие-то сверхусилия. Конечно, работа с SQL Server включает более сложные операции, например миграцию приложений от однопользовательских систем к распределенным, но для этого вовсе не нужно обладать искусством "черной" магии. (А чтобы не потерять рассудок, нужно не перетруждаться и почаще консультироваться с врачами.)
Если большинство моих запросов очень просты и не содержат сложных логических выражений, нужно ли мне использовать хранимые процедуры?
Да. По сути, есть два основных преимущества использования хранимых процедур вместо кодирования запросов SQL в коде приложения.
Производительность. Для многих программистов производительность является единственной причиной, по которой они стремятся использовать хранимые процедуры. Более высокая производительность хранимой процедуры по сравнению с кодом приложения связана с тем, что хранимая процедура откомпилирована и спланирована в SQL Server еще до вызова на выполнение. При передаче серверу на выполнение обычной команды SQL от клиентского приложения она должна быть обработана синтаксическим анализатором, откомпилирована, а также должен быть создан план ее выполнения. Поэтому выполнение
всех этих действий непосредственно во время работы системы связано с большими накладными расходами и снижением производительности.
Управляемостъ. Реализация запросов в виде хранимых процедур означает, что все запросы приложения хранятся в одном централизованном месте, а не разбросаны среди сотен тысяч строк кода. Более того, такая организация запросов позволяет использовать их повторно и одновременно для одной базы данных во многих проектах и приложениях. Это приводит к меньшему объему работы (кодирование/отладка/тестирование) и меньшей вероятности возникновения ошибок. Это также позволяет использовать систему безопасности SQL Server. Наконец, применение хранимых процедур позволяет использовать стратегию "разделяй и властвуй", т.е. специализацию при создании кода приложения. Разработчики программного обеспечения, которые специализируются на создании бизнес-логики и управлении потоком выполнения приложения, могут сфокусировать внимание на создании кода приложения, а организацией доступа к базе данных и созданием запросов могут заняться разработчики баз данных и серверной части системы.
ГЛАВАГЛАВА
ГЛАВА
ГЛАВА
Модель
ADO.NET: данных
В ЭТОЙ ГЛАВЕ...
обзор технологии AD0.NET
Провайдеры данных ADO.NET
объект Connection
объект command
Объект DataReader
Использование объектов Connection
и Command во время создания приложения
Другие провайдеры данных
Резюме
Вопросы и ответы
Порой кажется, что не успели еще разработчики приложений баз данных привыкнуть к новой технологии, как компания Microsoft предложила совершенно новую модель доступа к базам данных. В этой главе основное внимание уделяется модели ADO.NET, которая является наиболее новым воплощением модели ADO. Сначала обосновывается необходимость введения новой модели доступа к базам данных (с точки зрения авторов), а затем предлагается описание самой модели и ее архитектуры.
В этой главе преследуется цель изложить основные принципы функционирования технологии ADO.NET. Здесь обсуждаются базовые операции и описываются базовые объекты провайдера данных ADO.NET, в частности Connection, Command, Parameter и DataReader. Далее (в главах 5, "ADO.NET: объект DataSet”, 6, "ADO.NET: объект DataAdapter”, и 7, "ADO.NET: дополнительные компоненты") рассматриваются более сложные объекты, которые тесно связаны с основным объектомADO.NET- DataSet.
Обзор технологии ADO.NET
Разработчики приложений для работы с базами данных на основе Visual Basic уже привыкли к тому, что Microsoft каждые несколько лет предлагает новую усовершенствованную модель доступа к данным. Кроме новой трехбуквенной аббревиатуры, технология ADO.NET также предлагает новую модель API-интерфейсов и объектов. В течение последних нескольких лет разработчики уже успели познакомиться с предшественниками ADO.NET — технологиями ODBC, DAO, RDO и ADO. При знакомстве с каждой новой технологией им требовалось тщательно изучить ее назначение и принципы работы. При этом они часто задавали себе один и тот же вопрос: имеет ли смысл переходить на новую технологию? В большинстве случаев ответ был положительным, если только новшества не оказывали никакого влияния на текущие и будущие требования проекта. Действительно, чаще всего переход на новые технологии был вполне обоснован, за исключением технологии RDO (Remote Data Objects) для проектов с процессором баз данныхJet (потому что технология DAO по-прежнему является более удачной технологией работы сJet).
Мотивация и философия
Итак, зачем же все-таки нужна новая объектная модель доступа к данным? Наиболее простой ответ на этот вопрос можно сформулировать по аналогии с рекламным лозунгом компании Toyota конца 1970-х годов: "Спрашивали? Так получите". Технология ADO.NET предоставляет множество функциональных возможностей, о которых разработчики мечтали с момента появления технологии ADO. Некоторые компоненты, например отсоединенные от источника данных наборы записей и поддержка XML, были добавлены в модель ADO уже после ее выпуска. Но, поскольку они являлись добавлениями к основной модели, которые отсутствовали в исходной архитектуре ADO, способ их применения был крайне неудобным.
Классическая модель ADO основана на модели СОМ и одном объекте recordset, применение которого может быть весьма разнообразным. В зависимости от параметров конфигурации, например типа курсора, его расположения и типа блокировки, объект recordset будет действовать по-разному и выполнять различные операции.
В технологии ADO.NET разные функции разделены и выполняются разными объектами, которые могут использоваться либо отдельно, либо вместе с другими объектами.
165
Это дает разработчику возможность специализированного применения объектов без дополнительного обременительного "багажа". В то же время совершенно разные объекты могут взаимодействовать друг с другом для выполнения более сложных функций.
Поддержка распределенных приложений и отсоединенной модели программирования
В технологии ADO.NET предусмотрена эффективная и гибкая поддержка приложений, распределенных между несколькими компьютерами (серверами баз данных, серверами приложений и клиентскими рабочими станциями). В частности, особая поддержка предусмотрена для отсоединенных (трехуровневых или n-уровневых) приложений с минимизацией нагрузки при работе с параллельными операциями доступа к данным и блокировкой ресурсов сервера базы данных. В результате повышаются возможности масштабирования приложений, т.е. поддержки бальшего количества параллельно работающих пользователей за счет постепенного увеличения количества клиентских компьютеров. Это преимущество имеет особенно большое значение при создании Web-приложений.
Расширенная поддержка XML
Хотя классическая модель ADO способна сохранять и считывать данные в формате XML, фактически используемый для этого формат имеет несколько необычную форму и не так прост в применении. Кроме того, поддержка XML в модели ADO была добавлена в ходе ее эволюции, а потому обладает некоторыми ограничениями, в то время как поддержка XML в технологии ADO.NET является ее ключевым элементом. Философия ADO.NET формулируется очень кратко и просто: "Данные — это данные". Независимо от источника поступления, они могут считываться и обрабатываться как реляционные или иерархические данные, исходя из поставленной задачи и используемых инструментов.
XML используется как формат передачи данных между уровнями и компьютерами. Это не только исключает проблему прохождения СОМ-объектов через брандмауэры, но и позволяет совместно использовать данные сразу несколькими приложениями, которые работают на платформах, отличных от Windows (так как практически любая платформа способна обрабатывать текстовые данные XML).
Интеграция с .NET Framework
ADO.NET — это не просто следующая версия ADO, она специально предназначена для использования на платформе .NET Framework. Код всех объектов ADO.NET полностью управляемый, т.е. работает под управлением платформы .NET Framework. Технология ADO.NET является составной частью платформы .NET Framework, что позволяет избежать проблем с контролем версий объектов, которые хорошо известны разработчикам, имеющим опыт работы с ADO.
Внешний вид объектов ADO.NET
Хотя ADO.NET является составной частью платформы .NET Framework, многие ее компоненты будут знакомы опытным разработчикам, поскольку внешне они во многом напоминают объекты ADO. Даже совершенно новые элементы ADO.NET реали-
166
зованы так, чтобы максимально упростить процесс их изучения и применения на основе имеющихся знаний.
ADO.NET И ADO 2.X
При работе с моделью ADO.NET нужно учитывать перечисленные ниже отличия от классической модели ADO.
В классической модели ADO предусмотрен присоединенный к источнику данных способ доступа и использование физической модели данных.
В модели ADO.NET проводиться четкое разделение между присоединенным способом доступа к данным и отсоединенной моделью программирования.
В модели ADO.NET нет свойств CursorType, CursorLocation или LockType,
потому что в ADO.NET предусмотрены только статические курсоры, клиентские курсоры и оптимистическая блокировка.
Вместо использования простого многоцелевого объекта recordset в ADO.NET разные функции распределены среди объектов меньшего размера — DataReader,DataSetи DataTable.
BADO.NET разрешается полноценное манипулирование данными в формате XML, а не только использование его в качестве формата ввода-вывода данных.
BADO.NET предусмотрена поддержка строго типизированных наборов данных DataSet, а не использование для всех полей типа Variant. Это позволяет эффективнее обнаруживать ошибки времени выполнения и повышает производительность работы приложений.
Место ADO.NET в архитектуре .NET Framework
На рис. 4.1 показано место классов ADO.NET в архитектуре .NET Framework. В основе этой платформы лежит общеязыковая исполняющая среда (Common Language Runtime — CLR), которая образует единую среду выполнения для всех .NET- совместимых приложений, независимо от используемого языка программирования. Среда CLR включает общую систему типов, управление памятью и жизненным циклом объектов.
На следующем логическом уровне над средой CLR располагаются базовые системные классы. Именно эти классы отвечают за выполнение базовых функций, которые могут использоваться в .NET-приложениях. На рис. 4.1 показаны только некоторые классы библиотеки классов .NET Framework, которая, по сути, является новым набором API-интерфейсов Windows. В прошлом доступ к функциям операционной системы Windows осуществлялся только через API-интерфейсы, которые состояли из большого набора разрозненных и плохо продуманных функций. На платформе .NET Framework такой доступ организован на основе свойств и методов, которые предлагаются базовыми системными классами. Это объектно-ориентированный, последовательный и комфортабельный способ создания приложений Windows, независимо от типа клиентского приложения: традиционного настольного приложения, броузера или Web-службы.
Базовые системные классы
Data
Oledb
SqICIient
XML
IO
Threading
Security
ServiceProcess
System .Windows. FormsSystem .Windows. FormsSystem .WebSystem .Web167167ЭлементыуправленияЭлементыуправленияГрафическиефункцииГрафическиефункцииWeb-службыWeb-службыWeb-формыWeb-формы
System .Windows. Forms
System .Windows. Forms
System .Web
System .Web
167
167
Элементыуправления
Элементыуправления
Графическиефункции
Графическиефункции
Web-службы
Web-службы
Web-формы
Web-формы
Общеязыковая исполняющая среда
СистематиповСистематиповУправлениепамятьюУправлениепамятьюУправление жизненнымциклом объектов
Систематипов
Систематипов
Управлениепамятью
Управлениепамятью
РИС. 4.1. Классы ADO.NET в структуре платформы .NET Framework
Этот уровень включает несколько пространств имен (групп классов и других определений), предназначенных для организации доступа к данным: System. Data, System. OleDb и System. Data. SqICIient. В оставшейся части данной главы, а также в главах 5, 6 и 7 эти классы и пространства имен рассматриваются более подробно.
Прикладные интерфейсы
На этом наиболее высоком уровне происходит дифференциация, или разделение, выполняемых функций, которые разработчики могут использовать в разных типах приложений. Он содержит классы и элементы управления для создания (классических) приложений Windows на основе форм (Windows Forms), другие классы и элементы управления для создания Web-ориентированных приложений (Web Forms), а также классы для создания приложений на основе Web-служб. Однако все они используют для прикладной логики базовую библиотеку классов — системные базовые классы.
Теперь после первого знакомства с расположением классов ADO.NET в общей структуре платформы .NET Framework рассмотрим подробнее основные объекты ADO.NET.
Провайдеры данных ADO.NET
Несмотря на подчеркнутое значение отсоединенной модели программирования, для извлечения, обновления, вставки и удаления данных все же придется подключиться к физической базе данных. Программное обеспечение ADO.NET для подсоединения и взаимодействия с физической базой данных называется провайдером данных ADO.NET. Провайдер данных (data provider) — это управляемый код .NET, который эквивалентен провайдеру OLEDB или драйверу ODBC. Провайдер данных состоит из нескольких объектов, которые реализуют необходимую функциональность в соответствии с определениями своих классов и интерфейсов.
В настоящее время существует три разных провайдера данных ADO.NET, каждый из которых определен в своем собственном пространстве имен. Для всех объектов в этих пространствах имен используются следующие префиксы: OleDb, Sql и Odbc. Однако при упоминании этих объектов в рамках своего пространства имен имя объекта можно указывать без употребления префикса данного пространства имен.
ПровайдерданныхSqICIient
Оптимизирован для работы с SQL Server версии 7.0 (или выше) и позволяет добиться более высокой производительности по следующим причинам:
взаимодействует с базой данных непосредственно через собственный протокол табличной передачи данных (Tabular Data Stream — TDS), а не через OLEDB с отображением интерфейса OLEDB на протокол TDS;
исключает накладные расходы, связанные с использованием СОМ-служб взаимодействия;
отсутствуютненужныефункции,которыенеподдерживаютсявSQLServer
(объекты этого провайдера данных находятся в пространстве имен System. Data. SqICIient) .
Провайдер данных Oledb
Основан на существующем СОМ-поставщике OLEDB и СОМ-службах взаимодействия платформы .NET Framework, предназначенных для доступа к базе данных. Этот провайдер данных используется для работы с SQL Server более ранних версий, чем 7.0. Он позволяет осуществлять доступ к любой базе данных, для которой имеется поставщик OLEDB. Объекты этого провайдера данных находятся в пространстве имен System.Data. Oledb.
Провайдер данных Odbc
Используется для доступа к базам данных, которые не имеют собственного провайдера данных .NET или СОМ-поставщика OLEDB. Иногда драйвер ODBC демонстрирует более высокую производительность, чем драйвер OLEDB, поэтому для сравнения их фактической производительности при работе с конкретной базой данных рекомендуется провести ряд тестов. Объекты этого провайдера данных находятся в пространстве имен System. Data . Odbc.
НА ЗАМЕТКУ —•
Создание провайдера данных для ODBC несколько задержалось и отстало от создания платформы .NET Framework и Visual Studio .NET. Поэтому он не был включен в исходный выпуск Visual Studio .NET и его можно скопировать с Web-узла компании Microsoft. Кроме того, в скором будущем следует учитывать возможное появление дополнительных .N ЕТ-совместимых провайдеров данных.
В настоящее время на Web-узле компании Microsoft также можно скопировать провайдер данных Oracle .NET. Эти провайдеры данных ODBC и Oracle будут включены в следующую версию 1.1 платформы .NETFramework и Visual Studio .NET2003. В результате
169
I этого пространство имен провайдера данных ODBCMicrosoft.Data.Odbc станет на
: зываться System.Data.Odbc.
i В примерах этой главы используется провайдер данных ODBC версии 1.0, поэтому
j при использовании провайдера данных ODBC версии 1.1 нужно изменить имя его i j пространства имен.
Основные объекты
Каждый провайдер данных имеет четыре основных объекта, которые указаны в табл. 4.1.
Таблица4.1. Основные объекты провайдера данных
ОбъектОбъектКраткое описаниеКраткое описание
Объект
Объект
Краткое описание
Краткое описание
Connection Устанавливает соединение с указанным источником данных
CommandВыполняет команду по отношению к источнику данных. Содержит коллекцию
объектов Parameters и методы для выполнения команд разного типа
DataReader Считывает данные и возвращает из источника поток данных, предназначенный только для чтения и только в одном направлении
DataAdapter Соединяет набор данных DataS et и источник данных для извлечения и сохранения данных
Каждый объект основан на базовом родовом классе и реализует родовой интерфейс, но имеет собственную реализацию. Например, объекты SqlDataAdapter, OleDBDataAdapter и OdbcDataAdapter являются производными от класса DbDa- taAdapter и реализуют те же интерфейсы. Однако каждый из них реализует их своим собственным способом для соответствующего источника данных.
Пространство имен System. Data.OleDb содержит объекты:
OleDbConnection;
OleDbCommand;
OleDbDataReader;
OleDbDataAdapter.
Пространство имен System. Data. SqlClient содержит объекты:
SqlConnection;
SqlCommand;
SqlDataReader;
SqlDataAdapter.
Пространство имен Microsoft.Data. Odbc содержит объекты:
OdbcConnection;
OdbcCommand;
OdbcDataReader;
OdbcDataAdapter.
Аналогично, все будущие провайдеры данных будут находиться в отдельных ствах имен и содержать такие же объекты, но с другими реализациями и префиксами.
Объект Connection
Этот объект моделиочень похож на объект Connection в классической мо
дели ADO. Его предназначение очевидно: он служит для установления соединения с данным источником данных и с указанным в строке подключения учетным именем и ролем. Соединение можно настроить, редактируя нужным образом значения параметров строки подключения. Объект Command (или DataAdapter) может затем использовать это подключение для выполнения нужных операций с источником данных.
НА
В отличие от объекта Connection в модели ADOв объекте Connection в модели
ADO.NET нет методов Execute иДля выполнения команд SQL следует
j пользовать объекты Command или DataAdapter. Функции метода OpenSchema
с помощью методовобъекта
Хотя объекты OleDbConnection, SqlConnection и OdbcConnecticn одинаковые интерфейсы, они все же имеют разные реализации. Например, они имеют разный формат строки подключения. В объекте OleDbConnection используется стандартный формат строки подключения OLEDB с незначительными исключениями. В объекте OdbcConnection также используется стандартный формат строки подключения ODBC, но с незначительными отклонениями. Наконец, в объекте SqlConnection используется совершенно другой формат строки подключения, который имеет отношение только к SQL Server версии 7.0 или выше.
Более того, некоторые объекты обладают дополнительными свойствами. Например, объект OleDbConnection имеет свойство Provider для указания используемого вайдера данных OLEDB, а объект OdbcConnection имеет свойство Driver для ния используемого драйвера ODBC. Объект SqlConnection вообще не имеет этих свойств, так как используется с предопределенным источником данных, т.е. с SQL Server. Однако он имеет свойства PacketSize и WorkstationID, которые используются только для работы с SQL Server и не нужны для других типов подключения.
Итак, приступим к созданию кода. Познакомимся последовательно со всеми типами источников данных, начав с самого простого примера, который по мере продвижения к концу главы будет постепенно усложняться.
Запустите интегрированную среду разработки приложений Visual Studio
Создайте новый проект Visual Basic Windows Application. Для этого в диалоговом окне New Project (Новый проект) выберите тип проекта Visual Basic Project в области Project Types (Типы проектов), а затем шаблон Windows Application (Приложение Windows) в области Templates
Назовите проектects.
Укажите путь к файлам проекта.
Увеличьте размер формы
171
В окне Properties укажите значение Data Provider Objects для свойства Text формы Forml.
В верхнем левом углу формы создайте кнопку, перетаскивая ее из панели элементов управления.
В окне Properties укажите значение cmdConnection для свойства (Name) и значение Connection для свойства Text этой кнопки.
В верхнем правом углу формы создайте текстовое поле, перетаскивая его из панели элементов управления.
В окне Properties укажите значение txtResults для свойства (Name), значение True для свойства Multiline и значение Both для свойства ScrollBars этого текстового поля.
Увеличьте размер текстового поля, чтобы оно занимало до 80% всей площади формы.
По окончании этих операций форма будет выглядеть так, как на рис. 4.2.
РИС. 4.2. Форма Forml проекта DataProviderObjects
Перейдите в окно редактирования кода формы и включите в верхнюю часть файла приведенные ниже строки, которые импортируют пространства имен, используемые здесь и в следующих примерах главы.
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.OleDb
Imports Microsoft.Data.Odbc
Обратите внимание, что эти пространства имен содержат классы и определения объектов ADO.NET для каждого провайдера данных.
НА
Среда Visual Studioможет не обнаружить пространство имен
потому что оно является расширением базовой версии продукта.
В таком случае выполните ряд
Скопируйте инсталлятор провайдера данных ODBC с Web-узла компании Microsoft и выполните все инструкции по
В окнещелкните правой кнопкой мыши на папке References про
екта
Выберите в контекстном меню команду Add Reference.
Во вкладкедиалогового окна Add Reference прокрутите список компонентов
и найдите файл Micorosoft. Data.
Щелкните дважды на файледля включения его в
список избранных компонентов Selected Components в нижней части диалогового окна Add Reference.
на кнопке ОК для закрытия диалогового окна Add Reference.
| Если по какой-либо причине не распознано какое-то другое импортированное пространство имен, потребуется привести ссылку файл System.Для этого выполни-
| те действия, перечисленные вгде вместо файлаi
[ при выполнении п. 4 нужно использовать файл System.dll.
Теперь для кнопки btnConnection нужно создать код, приведенный в листинге для создания подключения к базе данных pubs сервера SQL Server. Этот код создает подключение и отображает состояние подключения до и после попытки подключения к базе данных.
ЛистингКод открытия подключения и отображения его состояния
Private Subsender As
e AsHandles
' Создание экземпляра объекта Connection
Dim cnn As SqlConnection = New
' Создание строки подключения =
txtResults.Clear()
' Отображение состояния подключения
If=Then
=& "Connection is Open"
Else
=& "Connection is Closed"
End If
=&
' Открытие подключения
=& "Opening DB
&&
cnn.OpenO
173
' Отображение состояния подключения
If=Then
=& "Connection is Open"
Else
=& "Connection is Closed"
End If
=&
End Sub
СОВЕТ :
Новым полезным компонентом Visual Basicявляется возможность получения
представления для значений перечислениявместо создания специальной
подпрофаммы на основе операторовдля всех возможных значений пере-
Все типы перечисления, которые являются объектами, наследуют метод возвращающийстрокустекстовымпредставлениемтекущегозначения.
В листингеиспользуется приведенный ниже фрагмент кода для отображения со
стояния подключения на основе операторов
' Отображение состояния- вариант 1
IfThen
txtResults.Text =£ "Connection is Open"
Else txtResults.Text = txtResults.Text & "Connection is Closed If
Его можно заменить другим вариантом кода, содержащим только одну строку.
' Отображение состояния подключения вариант 2
txtResults. Text. = txtResults.Text & "Connection is " & &
После запуска полученного приложения DataProviderOb j ects и щелчка на кнопке Connection в текстовом поле появятся строки о закрытии подключения, о состоянии подключения и повторном открытии подключения, как показано на рис. 4.3.
НА ЗАМЕТКУ
При создании рабочего кода нужно выбрать и реализовать стратегию обработки исключительных ситуаций для большинства подпрограмм и операций.
Обычно основана на блоках try-catch обработки исключительных ситуаций. Этот код не включен в приведенные здесь примеры,что наша цель — сфокусировать внимание на концепциях программирования база не на описании об
щих принципов программирования для Visual
Объект Command
Аналогично объекту Connection, объект Command модели ADO.NET очень похож на своего предшественника из прежней модели ADO 2.X. Объект Command позволяет выполнять команды по отношению к источнику данных и получать возвращенные данные или результаты выполнения команд.
Глава 4 —
РИС. 4.3. Состояние приложениядо и после открытия подключения скода из листинга
Этот объект имеет следующие свойства:идля опре
деления текста и типа фактической команды; Connection для указания подключения, используемого для выполнения команды;для указания време
ни ожидания, по истечении которого команда отменяется и выдается сообщение об ошибке;для коллекции параметров команды; Transaction для указания
транзакции, в которой используется данная команда.
Все три версии объекта Command (в пространствах именOdbc) имеют
идентичные свойства и методы, за исключением того, что объектимеет
дополнительный метод, которого нет у двух других вариантов этогоа
именно ExecuteXmlReader. Он использует преимущества SQL Server для автоматического возвращения данных в формате(если вSQL добавлено предло
жение FOR
НА ЗАМЕТКУ
Еще одно отличие между версиями объекта Command для разных провайдеров данных заключается в использовании значенийВсе они
значения Text иа объектыиподдер
живают еще одно, третье возможное значение — TableDirect. Это позволяет эффективно загружать все содержимое таблицы за счет установки значения TableDirect для свойства CommandType и имени таблицы для свойства CommandText.
Продолжим работу с формой, показанной на рис. 4.3.
Добавим еще одну кнопку сразу под кнопкой btnConnection, перетаскивая пиктограмму кнопки из панели элементов управления.
175
В окне свойств Properties установите значение btnCommand для свойства Name и значение Command для свойства Text.
Добавьте для кнопки код, показанный в листинге 4.2.
Листинг 4.2. Код открытия подключения к базе данных и подготовки
Объекта Command
Private Sub btnCommand_Click(ByVai sender As System.Object, _
ByVai e As System.EventArgs) Handles btnCommand.Click
' Создание экземпляра объекта Connection
Dim cnn As SqlConnection = New SqlConnection( _
"server=localhost;uid=sa;database=pubs")
Создание экземпляра объекта Command
Dim and As SqlCommand = New SqlCommand()
txtResults.Clear()
' Открытие подключения
cnn.Open()
' Указание подключения и текста команды cmd.Connection = cnn
cmd.CommandType =
=
"Select au_lname, state from authors"
1 Вывод текста команды
txtResults.Text = "Command String:" & ControlChars.CrLf
txtResults.Text =& ControlChars.Tab &
cmd.CommandText() & ControlChars.CrLf
End Sub
После запуска на выполнение приложения DataProviderObjects щелкните на кнопке Command, и в текстовом поле будет показана команда SQL, которая находится в свойстве CommandText объекта SqlCommand, а именно: SELECT au_lname, state FROM authors.
НА ЗАМЕТКУ
Многие классы платформы .NET Framework, а также созданные другими разработчиками классы перегружают конструкторы объектов. Иначе говоря, существует несколько способов создания нового экземпляра класса, где каждый конструктор принимает свойственный только ему набор аргументов. Таким образом можно выбрать версию, оптимальную для выполнения текущей задачи.
Конструктор в листинге 4.2 для объекта SqlConnection отличается от принимаемого по умолчанию конструктора в листинге 4.1, в котором не используется никаких аргументов. Позже строке подключения присваивается объект SqlConnection с помощью свойства ConnectionString.
Создание экземпляра объекта Connection
Dim cnn As SqlConnection New SqlConnection()
Создание строки подключения
cnn.Connectionstring = "server=localhost;uid=sa;database=pubs"
176
Влистинге 4.2 используется конструктор объектаSqlConnection, который прини- маетстрокуподключениявкачествепараметра. Это позволяетсоздатьобъектисра- зужеуказатьдля него строку подключенияводной строке кода.
: 1 Создание экземпляра объекта Connection
Dim cnn As SqiConnection = New SqlConnection ( _.
"server=localhost;uid=sa;database=pubs")
Применение объекта Command
с параметрами и хранимыми процедурами
При создании запросов или команд для источника данных часто требуется передавать значения параметров действия (обновление, вставка или удаление данных) или хранимой процедуры. Для решения этой проблемы в объекте Command предусмотрено свойство Parameters, которое является объектом-коллекцией ParameterCollection и содержит коллекцию объектов-параметров Parameter. Это аналогично способу работы, применимому в модели ADO 2.X.
Объекты Parameter и ParameterCollection тесно связаны с соответствующим провайдером данных, поэтому они должны быть реализованы как составная часть провайдера данных ADO.NET. Способы программирования объекта SqlParameterCol- lection и использование объектов OdbcParameterCollection и OledbParameter- Collection имеют существенные отличия. Объекты OdbcParameterCollection и OledbParameterCollection основаны на позиционных параметрах, а объект SqlParameterCollection— на именованных параметрах. Эти различия в значительной степени влияют на способ определения запросов и параметров.
Начнем с создания простого запроса с параметрами для извлечения из базы данных pubs имен и фамилий всех авторов из заданного штата.
С одной стороны, при использовании провайдеров данных OLEDB или ODBC запрос будет иметь следующий вид:
SELECT state, au_fname, au_lname from authors WHERE state = ?
Здесь вопросительный знак заменяет один параметр, а для нескольких параметров можно использовать несколько вопросительных знаков. Порядок расположения параметров в коллекции ParameterCollection должен точно соответствовать порядку, в котором располагаются их заменители в запросе или хранимой процедуре.
С другой стороны, при использовании провайдера данных SqlClient запрос будет выглядеть следующим образом:
SELECT state, au_fname, au_lname from authors WHERE state = @MyParam
Здесь заменителем параметра является его имя, а дополнительные параметры также обозначаются их именами, поэтому порядок расположения параметров в коллекции ParameterCollection не имеет значения.
Объект Parameter можно создать явно, используя конструктор Parameter (т.е. с использованием оператора New) или передавая нужные аргументы методу Add объекта-коллекции ParameterCollection (свойство Parameters объекта Command). Помните, что оба метода (конструктор Parameter и метод Add) имеют перегруженные версии.
177
Ниже приведен один из способов включения параметра команды за счет явного указанияобъекта-параметра.
Dim rayParameter As New OdbcParameter("@MyParam", OdbcType.Char, 2) myParameter.Direction = myParameter.Value =
cmd.Parameters.Add (myParameter)
А способ включения параметра команды с помощью метода Add выглядит иначе.
cmd.Parameters.Add("@MyParam", OdbcType.Char, 2) cmd.Parameters("@MyParam").Direction = cmd.Parameters("@MyParam").Value = "CA"
Второй метод короче и обычно предпочтительнее, если только нет особой причины для повторного использования объекта Parameter.
Для метода Add объекта Parameter обычно требуется указать имя, тип и длину параметра. Затем нужно указать направление передачи данных: Input, Output, InputOutput или ReturnValue. По умолчанию используется направление Input. Наконец, для присвоения значения параметру нужно использовать свойство Value объекта Parameter. Кроме того, для параметра можно указать другие свойства, например масштаб (свойство Scale), точность (свойство Precision) и допустимость использования неопределенных значений (свойство IsNullable).
При использовании провайдера данных SqlClient можно применять практически идентичный код. Единственным отличием являются префиксы Odbc вместо префиксов Sql, а также тип перечисления SqlDbType вместо OdbcType.
Dim myParameter As New SqlParameter("@MyParam", SqlDbType.Char, 2)
" =
myParameter.Value = "CA"
cmd.Parameters.Add (myParameter)
АналогичновыглядитспособвключенияпараметракомандыспомощьюметодаAdd.
cmd.Parameters.Add("gMyParam", SqlDbType.Char, 2)
cmd.Parameters("@MyParam").Direction =
cmd.Parameters("@MyParam").Value = "CA"
СОВЕТ " I—
Для передачи параметру неопределенного значения можно использовать свойство
Value объекта DBNull.‘
cmd.Parameters("@MyParam").Value =
Измените код кнопки cmdButton, как показано в листинге 4.3. После запуска программы и щелчка на кнопке cmdButton в текстовом поле появится текст запроса, а также имя и значение параметра.
Листинг 4.3. Код подготовки и отображения команды и ее параметров
Private Sub btnCommand_Click(ByVai sender As System.Object, By Vai e As System.EventArgs) Handles btnCommand.Click
Создание экземпляра объекта Connection
Dim cnn As SqlConnection = New SqlConnection( _
"server=localhost;uid=sa;database=pubs")
' Создание экземпляра объекта Command и объектов Parameter
Dim and As SqlCommand = New SqlCommand()
Dim prm As SqlParameter = New SqlParameter()
txtResults.Clear()
' Открытие подключения
cnn.Open()
Указание подключения и текста команды cmd.Connection = cnn cmd.CommandType =
=
"Select au_lname,state from authors where state = @MyParam"
' Создание параметра и указание его значения
cmd.Parameters.Add(New SqlParameter("@MyParam", SqlDbType.Char, 2))
cmd.Parameters("QMyParam").Value =
' Вывод текста команды
txtResults.Text = "Command String:" & ControlChars.CrLf
txtResults.Text =& ControlChars.Tab &
cmd.CommandText() & ControlChars.CrLf
' Вывод параметров команды
txtResults.Text = txtResults.Text & "Command parameters:" & ControlChars.CrLf
For Each prm In cmd.Parameters txtResults.Text =& ControlChars.Tab &
prm.ParameterName & " =& prm.Value & ControlChars.CrLf
Next
End Sub
Аналогично вызываются хранимые процедуры, за исключением того, что вместо свойства CommandType. Text используется свойство CommandType. StoredProcedure, а имя хранимой процедуры присваивается свойству CommandText. Таким образом, код вызова хранимой процедуры GetAuthorsFromState с двухсимвольным параметром для извлечения информации обо всех авторах заданного штата будет выглядеть, как показано ниже.
cmd.CommandType =
cmd.CommandText = "GetAuthorsFromState"
cmd.Parameters.Add("@MyParam", SqlDbType.Char, 2)
cmd.Parameters("@MyParam").Direction =
cmd.Parameters("@MyParam").Value = "CA"
СОВЕТ
Для вызова хранимой процедуры с помощью OdbcCommand нужно использовать стандартные последовательности символов ODBC (с участием фигурных скобок), а не только имя процедуры для свойства CommandText. В качестве заменителей параметров в ODBC используются вопросительные знаки. Вот как выглядит приведенный выше КОДдля провайдера данных ODBC:
cmd.CommandType =
cmd.CommandText = "{GetAuthorsFromState ?)"
cmd.Parameters.Add("@MyParam", OdbcType.Char, 2)
cmd.Parameters("@MyParam").Direction =
cmd.Parameters("@MyParam").Value =
| Хранимая процедура также возвращает значение, которое указывается с помощью I специальной последовательности символов (вопросительного знака и знака
ства) у открывающейкак показано ниже.
==
Чтобы хранимая процедура возвращала значение, нужно указать направление а затем считать значение свойствапосле вызова хранимой проце
дуры. В данном примере также определяется возвращаемое значение хранимой процедуры. Поскольку здесь указан типсервера SQL Server, то для этого параметра
не нужно указывать длину, так как по определению она составляет 4 байт.
cmd.Parameters.Add(New SqlParameter("result", SqlDbType.Int)
= ParameterDirection.ReturnValue
cmd.Parameters.Add(New SqlParameter("@MyParam", SqlDbType.Int) cmd.Parameters("@MyParam").Direction = ParameterDirection.Output ' Вызов хранимой процедуры Msg
НА ЗАМЕТКУ ;
При определении параметра, который будет использоваться для возвращаемого значения ReturnValue хранимой процедуры его нужно указать первым в списке парамет- j ров коллекции Parameters. Это обязательное условие для провайдеров данных
и ODBC, потому что, как отмечалось выше, параметры этих провайдеров определяются по порядку иха возвращаемое значение может располагаться только с
| начала. Однако при определении возвращаемого значения для провайдера данных SQL его можнов произвольном месте коллекции параметров, потому
параметры провайдера данныхопределяются по именам.
Далее представлены другие фрагменты кода с указанием параметров команд и хранимых процедур, а также описываются способы их выполнения.
Выполнение команд
До сих пор мы только указывали свойства и параметры объекта Command, но не выполняли эти команды. Существует три стандартных способа выполнения команд для объекта Command и один способ для объекта
Метод ExecuteNonQuery. Выполняет команду SQL и не возвращает записей.
Метод ExecuteScalar. Выполняет команду SQL и возвращает первое поле первой записи.
Метод ExecuteReader. Выполняет команду SQL и возвращает набор записей с
помощью объекта DataReader.
Метод(только для объекта-команды SqlCommand). Вы
полняет команду SQL и возвращает набор записей в формате XML с помощью объекта
Первые три метода рассматриваются далее в этойа последний — в главе 10,
"ADO.NET ипри обсуждении способов использования XML в модели ADO.NET.
Метод ExecuteNonQuery
Этот метод позволяет выполнять команды без возвращения значений (наборов записей или скалярных значений), кроме значения, сообщающего об успешном или неудачном исходе выполнения команды. Это наиболее эффективный способ выполнения команд по отношению к источнику данных. Таким образом можно выполнять команды SQL или хранимые процедуры, которые являются DDL-командами определения данных (например, для создания или изменения структуры базы данных: таблиц, представлений или хранимых процедур) либо DML-командами управления данными (например, их обновления, вставки или удаления).
НА ЗАМЕТКУ —
Метод ExecuteNonQuery возвращает только целочисленное значение, сообщающее об успешном или неудачном исходе выполнения команды.
При удачном выполнении DDL-команд определения данных для изменения структуры баз данных возвращается значение -1, а при удачном выполнении DML-команд управления данными для их обновления, вставки или удаления возвращается количество строк, задействованных в команде. При неудачном выполнении команд обоих типов возвращается значение 0.
Продолжая работу с проектом DataProviderObjects, попробуем использовать объекты пространства имен OleDb и базу данных pubs. Наша задача — создать новую таблицу tblStateZipCodes для этой базы данных с помощью DDL-команды. Новая таблица tblStateZipCodes предназначена для организации связи между почтовыми индексами и штатами. Определения ее полей совпадают с определениями полей в других таблицах базы данных pubs, но отличаются от определений полей в других таблицах базы данных Novelty. Эта таблица имеет два поля: ZipCode для почтового индекса и State для названия соответствующего штата. Ниже приведена команда SQL для создания этой таблицы.
CREATE TABLE tblStateZipCodes (
ZipCode char (5) NOT NULL,
State char (2) NOT NULL )
Теперь нужно изменить исходную форму Forml, выполнив ряд действий.
Откройте форму Forml в интегрированной среде разработки Visual Studio .NET.
В верхнем левом углу формы создайте еще одну кнопку, перетаскивая ее пиктограмму из панели элементов управления.
В окне свойств Properties укажите значение btnNonQuery для свойства (Name) и значение ExecuteNonQuery для свойства Text.
Затем создайте код подпрограммы btnNonQuery_Click, который приведен в листинге 4.4.
Листинг 4.4. код создания таблицы базы данных с помощью объектов
пространства имен OleDb
Private Sub btnNonQuery_Click(ByVai sender As System.Object, _
ByVai e As System.EventArgs) Handles btnNonQuery.Click
' Создание экземпляра объекта Connection.
Dim cnn As OleDbConnection = New
"provider=SQLOLEDB;server=localhost;uid=sa;database=pubs")
Dim As String
Dim result As Integer
' Создание экземпляра объекта Command.
Dim cmd As= New
' Указание подключения и текста = cnn =
' Указание команды SQL для создания новой sql = "CREATE TABLE tblStateZipCodes (&
char (5) NOT NULL,&
"State char (2)NOT NULL
MsgBox(sql) = sql
' Открытие подключения перед вызовом метода ExecuteNonQuery. cnn.Open ()
' Для обработки исключительных ситуаций нужно поместить код в блоке Try-Catch, потому что неудачное выполнение
' команды ТАКЖЕ генерирует ошибку времени выполнения.
Try result =
Catch ex As Exception
' Отображение сообщения об
MessageBox.Show(ex.Message)
End Try
' Вывод результатов выполнения команды.
If result = -1 Then completed successfully") выполнена успешно")
Else execution failed") 'не выполнена")
End If
cnn.Close()
End Sub
После запуска полученного приложения и щелчка на кнопке ExecuteNonQuery сначала появится диалоговое окно с сообщением об успешном выполнении команды. Правильность выполнения команды можно просматривая список таблиц базы данных pubs в диалоговом окне Server Explorer интегрированной среды разработки Visual Studio (которое описывается в главе 1, "Основы построения баз данных") или в программе SQL Server Enterprise Manager (которая рассматривается в главе 3, "Знакомство с SQL Server 2000").
При повторном щелчке на кнопке ExecuteNonQuery появятся два диалоговых окна с сообщениями: одно с сообщением о возникшей исключительной ситуации (оно создается блоком обработки исключительных ситуаций try-catch), а другое — о неудачном выполнении команды.
182
Аналогичным способом можно создать представление или хранимую процедуру. Для создания представления EmployeeJobs_view, которое возвращает отсортированный по должностям перечень сотрудников с указанием их имен, фамилий и должностей, замените команду SQL в листинге 4.4 приведенной ниже командой.
sql = "CREATE VIEWAS" &
"SELECT TOP 100 PERCENT&
&
"FROM jobs INNER JOIN" &
"employee ON=&
"ORDER BY
НА ЗАМЕТКУ
Для включения предложения ORDER BY в определение представления с сортировкой результатов нужно включить в команду SELECT предложение ТОР.
Для создания хранимой процедуры, которая принимает один параметр и возвращает значение, нужно изменить эту команду SQL, как показано в листинге 4.5.
Листинг4.5. Код, содержащийкоманду SQLдля создания хранимой
процедуры
sql = "CREATE PROCEDURE AuthorsInStatel&
AS declare&
select= count (*) from authors &
where state =&
return
НА ЗАМЕТКУ —
Хотя методне возвращает записи, входные и выходные
а также возвращаемые значения можно передавать или возвращать с помощью коллекции Parameters. Это более эффективный подход, чем выполнение команды, которая возвращает набор записей или скалярное значение.
Обратимся теперь ко второму типу команд SQL для управления данными, т.е. их обновления, вставки или удаления. Для этих команд обычно требуется указать параметры, особенно при работе с хранимыми процедурами.
Продолжая работу с проектомпредположим, что издательство, которое создало базу данныхрешило повысить авторам выраженный в
центах гонорар. Включение новой кнопки и нового текстового поля в форму позволяет главному бухгалтеру издательства повысить гонорары авторов с помощью параметра команды UPDATE. Для этого нужно выполнить перечисленные ниже действия.
Создайте новую кнопку под кнопкой
В окне свойств Properties для этой кнопки укажите значениедля
свойства (Name) и значение Update для свойства Text.
Создайте новое текстовое поле под новой кнопкой Update.
В окнесвойствPropertiesдляэтоготекстовогополяукажитезначение
txtParaml для свойстваи значение 0 для свойства Text. Установка та-
Модель ADO.NET: провайдеры данных 1 183
кого значения гарантирует, что при запуске программы и случайном нажатии кнопки Update не будет причинен ущерб данным.
Создайте код подпрограммы btnUpdate_Click, приведенный в листинге 4.6.
Листинг 4.6. Код обновления таблицы базы данных с помощью команды UPDATE с параметром
Private Sub btnUpdate_Click(ByVai sender As System.Object, _
ByVai e As System.EventArgs) Handles btnUpdate.Click
Dim result As Integer
' Создание экземпляра объекта Connection.
Dim cnn As SqlConnection = New SqlConnection( _ ”server=localhost;uid=sa;database=pubs")
' Создание экземпляра объекта Command.
Dim cmd As SqlCommand = New SqlCommand()
txtResults.Clear()
1 Указание подключения и текста команды.
cmd.Connection = cnn
cmd.CommandType =
cmd.CommandText =UPDATE roysched SET royalty = royalty + @paraml"
* Создание параметра и указание его значения.
cmd.Parameters.Add(New SqlParameter("@paraml", SqlDbType.Int))
cmd.Parameters("@paraml").Direction =
cmd.Parameters("@paraml").Value =
' Открытие подключения перед вызовом метода ExecuteReader().
cnn.Open()
result = cmd.ExecuteNonQuery()
MessageBox.Show(result & " records updated", "DataProviderObjects")
cnn.Close ()
End Sub
Теперь таблицу с гонорарами авторов в базе данных pubs можно обновить, запустив приложение DataProviderObjects, задав новое значение гонорара в текстовом поле под кнопкой Update и щелкнув на этой кнопке. После этого на экране появится диалоговое окно с указанием количества охваченных записей. Этот результат можно проверить с помощью программы SQL Server Enterprise Manager, просматривая данные о гонорарах в таблице roysched до и после обновления.
Точно такое же обновление можно выполнить с помощью хранимой процедуры, что позволяет добиться более высокой производительности и централизованно хранить код. Потенциальным недостатком использования хранимых процедур является необходимость назначения администратора базы данных или специалиста с опытом создания хранимых процедур. В крупных организациях порой уходят целые дни на то, чтобы администратор базы данных изменил хранимые процедуры, которые можно самостоятельно изменить за несколько минут. Хранимые процедуры создаются с помощью программ SQL Server Enterprise Manager или SQL Query Analyzer, которые описаны в главе 3, "Знакомство с SQL Server 2000". Это можно также сделать с помощью проекта DataProviderOb j ects, изменив команду SQL, как это делалось ранее.
Итак, в данном примере хранимая процедура имеет следующий вид:
CREATE PROCEDURE UpdateRoyalties
@paraml int
AS
UPDATE roysched SET royalty = royalty + @paraml
В листинге 4.6 для организации вызова хранимой процедуры потребуется заменить свойства CommandText и CommandType объекта Command.
and.CommandType = cmd.CommandText =
Запуск этой программы должен привести к тем же результатам, что и прежде, однако теперь обновление происходит благодаря хранимой процедуре, а не команде SQL из кода приложения.
Метод ExecuteScalar
Иногда нужно выполнить команду, которая возвращает скалярное значение, т.е. только одно значение. Типичными примерами являются команды SQL для вычисления суммы всех значений SUM и общего количества значений COUNT. Другими примерами являются справочные таблицы для подстановки одного значения или команды, возвращающие логическое значение. Метод ExecuteScalar выполняет заданную команду и возвращает значение первой записи из первого поля возвращенного набора записей, а все другие поля и записи игнорируются.
Включим приведенную ниже хранимую процедуру в базу данных pubs.
CREATE PROCEDURE AuthorsInState2
Bparaml char(2)
ASESLECTFROM authors WHERE state =
Хранимая процедура AuthorsInState2 принимает параметр, который имеет двухсимвольный код штата, и возвращает из таблицы authors количество авторов из этого штата. Эта процедура с функциональной точки зрения эквивалентна процедуре AuthorsInStatel из листинга 4.5, но возвращает вычисленное значение для набора записей, а не только индикатор успешности выполнения команды.
| НА ЗАМЕТКУ ;
Использование метода ExecuteScalar вместо метода ExecuteNonQuery и передача скалярного значения с помощью параметра ReturnValue связаны с дополнительными накладными расходами. Зачем же его используют? Он проще в употреблении, потому что не нужно заботиться об указании параметров в определениях и вызовах команд.
Для вызова данной хранимой процедуры с помощью провайдера данных ODBC выполните следующее.
Создайте дополнительную кнопку под текстовым полем txtParaml.
В окне свойств Properties укажите значение cmdScalar для свойства (Name) и значение ExecuteScalar для свойства Text.
Создайте код подпрограммы btnExecuteScalar_Click, приведенный в листинге 4.7.
Листинг 4.7. Код извлечения скалярного значения из хранимой процедуры с помощью провайдера данных ODBC
Private Sub btnExecuteScalar_Click(ByVai sender As System.Object, ByVai e As System.EventArgs) Handles btnExecuteScalar.Click
Dim result As Integer
' Создание экземпляра объекта Connection.
Dim cnn As OdbcConnection = New OdbcConnection( _
"DRIVER=(SQL Server};server=localhost;uid=sa;database=pubs")
' Создание экземпляра объекта Command.
Dim cmd As OdbcCommand = New OdbcCommand()
txtResults.Clear()
' Указание подключения и текста команды, cmd.Connection = cnn
cmd.CommandType = CommandType.StoredProcedure cmd.CommandText =
' Создание параметра и указание его значения.
cmd.Parameters.Add("@paraml”, OdbcType.Char, 2) cmd.Parameters("@paraml").Value =
' Открытие подключения перед вызовом метода ExecuteReader. cnn.OpenO
result = cmd.ExecuteScalar()
MessageBox.Show("Count is " & result, "DataProviderObj ect s")
cnn.Close ()
End Sub
Запустите приложение и введите в текстовом поле над кнопкой ExecuteScalar двухсимвольный код штата. После щелчка на кноЯке ExecuteScalar появится диалоговое окно с сообщением о количестве авторов в данном штате. Полученный результат можно проверить с помощью программы SQL Server Enterprise Manager, просматривая данные в таблице authors в базе данных pubs.
НА ЗАМЕТКУ :'
Учтите, что по умолчанию база данных pubs содержит двух авторов из штата Юта
(код ЦТ) и 15авторов из штата Калифорния (код СА).
Метод ExecuteReader
Этот метод применяется для возвращения набора записей. В большинстве приложений для работы с базами данных именно он используется чаще всего. Работа этого метода основана на объекте DataReader, с помощью которого записи обрабатываются последовательно одна за другой. Более подробно метод ExecuteReader и объект DataReader рассматриваются в следующем разделе.
Объект DataReader
Данный объект предназначен для чтения в прямом направлении небуферизуемого потока записей, полученных от метода ExecuteReader объекта Command. Объект DataReader в основном эквивалентен объекту Recordset модели ADO 2.X, который также предназначен для чтения в прямом направлении. Объект DataReader предлагает наиболее быстрый способ доступа к источнику данных, но в нем не предусмотрены возможности прокрутки и обновления данных. Поскольку данные не буферизуются и не сохраняются в кэше, этот метод прекрасно подходит для извлечения большого объема данных. Для перехода к следующей записи объекта DataReader нужно вызвать его метод Read.
Кроме коллекции Fields, доступ к полям каждой записи можно осуществить с помощью строго типизированных методов. Доступ к полям с помощью коллекции Fields аналогичен способу доступа к полям в модели ADO 2.X.
X = MyReader("Myfield")
НА ЗАМЕТКУ —:—7——
Объект DataReader не имеет явного конструктора, т.е. его нельзя создать с помощью оператора New (). Для инициализации нового объекта нужно вызвать метод ExecuteReader объекта Command.
Зная тип данных в поле, доступ к нему можно получить с помощью типизированных методов доступа. Эти методы извлекают значение поля по номеру поля, нумерация которого начинается с нуля, например:
X = MyReader.Getlntl6(1) или
X = MyReader.GetString(2)
В самом первом примере используется имя поля Myfield, что значительно упрощает его чтение, использование и совместимость с унаследованными программами. Во втором примере используется номер поля, что значительно повышает производительность, поскольку исключается преобразование имени поля в его номер.
Для демонстрации способов использования этих методов выполните следующее.
Создайте под кнопкой ExecuteScalarдополнительную кнопку.
В окне свойств Properties укажите значение cmdExecuteReader для свойства (Name) и значение ExecuteReader для свойства Text.
Создайте код подпрограммы btnExecuteReader_Click, приведенный в листинге 4.8.
НА ЗАМЕТКУ ——
Кроме способов использования объекта DataReader, в этом примере демонстрируются и другие функциональные возможности. Например, здесь помимо членов Text и StoredProcedure свойства-перечисления CommandType для указания типа команды используется член TableDirect. Он содержит имя таблицы, все поля которой j
187
г возвращаются данной командой. Учтите, что этот член перечисленияподдерживает- ся только для провайдера данных ODBC.
Представления обычно обрабатываются точно так же, как и таблицы. Следовательно, при использовании членаableDirect свойства-перечисления:ommandType можно
• указывать не только имя таблицы, но и имя представления.
Листинг 4.8. Код создания объекта DataReader и извлечения значений полей
с помощью представления и члена TabieDirect
Private Sub btnExecuteReader_Click(ByVai sender As System.Object, _ ByVai e As System.EventArgs) Handles btnExecuteReader.Click
Создание экземпляра объекта Connection.
Dim cnn As OleDbConnection = New OleDbConnection( _
"provider=SQLOLEDB;server=localhost;uid=sa;database=pubs")
' Создание экземпляра объекта Command.
Dim and As OleDbCommand = New OleDbCommand()
txtResults.Clear()
' Указание подключения и текста команды.
cmd.Connection = cnn
cmd.CommandType = cmd.CommandText =
' Открытие подключения перед вызовом метода ExecuteReader.
cnn.Open()
Dim reader As OleDbDataReader
reader = cmd.ExecuteReader()
While reader.Read()
txtResults.Text =& reader("fname") &
ControlChars.Tab & reader("Iname") &
ControlChars.Tab & ControlChars.Tab & reader("job_desc") & ControlChars.CrLf
End While
reader.Close()
cnn.Close ()
End Sub
(Здесь предполагается, что представление EmployeeJobs_view уже создано с ПОМОщью подпрограммы btnNonQuery_Click из листинга 4.4, как описано выше. — Прим. ред.)
HA ЗАМЕТКУ —'— — 1 1
Перед попыткой доступа к данным объекта DataReader не забывайте вызывать метод Read (). в отличие от объекта Recordset в модели ADO 2.X, в которой после загрузки данных текущее расположение автоматически находится на первой записи, в модели ADO.NET в объекте DataReader нужно явно указать текущее расположение возле первой записи с помощью исходного вызова метода Read().
Для организации доступа к данным можно также применить цикл While с более эффективными строго типизированными методами доступа к полям.
While reader.Read()
txtResults.Text =& reader.GetString(1) &
ControlChars.Tab & reader. GetString(2) &
ControlChars.Tab s ControlChars.Tab &
reader. GetString(0) & ControlChars.Ctrlf
End While
Еще одно изменение, которое диктуется личным вкусом и стилем программирования автора, заключается в объединении определения объекта DataReader и выполнения метода ExecuteReaderв одной строке, т.е. вместо фрагмента кода DimreaderAs OleDbDataReader reader = cmd.ExecuteReader()
можно использовать следующую строку:
Dim reader As OleDbDataReader = cmd.ExecuteReader()
После запуска приложения DataProviderObjects щелкните на кнопке ExecuteReader, и в текстовом поле справа будут отображены данные из представления EmployeeJobs_view, как показано на рис. 4.4.
РИС. 4.4. Результаты выполнения команды ExecuteReader из листинга 4.8
НА ЗАМЕТКУ
По окончании использования объекта DataReader следует вызвать метод Close. Дело в TOM, что выходные данные или возвращаемые значения ДЛЯ объекта Command недоступны до тех пор, пока объект DataReader открыт. Он остается открытым до тех пор, пока открыто само подключение или объект DataReader.
189
Объект DataReader также предлагает простой и эффективный способ создания Web-страниц для работы с данными на основе элемента управления DataGrid, который подробно рассматривается в главе 11, "Web-формы: приложения на основе ASP.NET для работы с базами данных".
Использование объектов Connection и Command во время создания приложения
Вкладка Data панели элементов управления среды Visual Studio .NET содержит компоненты, которые соответствуют некоторым методам доступа к данным. С помощью окна Properties они позволяют указывать значения свойств во время создания приложения, а не только во время его выполнения. Они также предлагают визуальные инструменты указания значений для более сложных свойств.
Например, для реализации кода из листинга 4.8 с помощью таких элементов управления выполните перечисленные ниже действия.
Создайте еще одну форму Form2 в проекте DataProviderObjects.
В окне свойств Properties укажите значение Connection and Command Components для свойства Text формы Form2.
Увеличьте размер формы.
Включите в форму текстовое поле Textboxl.
В окне свойств Properties укажите значение True для свойства Multiline и значение Both для свойства Scrollbars этого текстового поля.
Увеличьте размер текстового поля так, чтобы оно покрывало большую часть формы.
Из вкладки Data панели элементов управления перетащите элемент управления OledbConnection в форму Form2. Этот компонент невидим во время выполнения приложения, поэтому он появится в разделе невизуальных компонентов в нижней части окна редактирования формы.
В окне свойств Properties укажите приведенное ниже значение для свойства ConnectionString элемента управления OledbConnectionl.
provider=SQLOLEDB;server=localhost;uid=sa;database=pubs
Из вкладки Data панели элементов управления перетащите еще один элемент управления OledbCommand в форму Form2. Этот компонент также невидим во время выполнения приложения, поэтому он появится в нижней части окна редактирования формы.
В окне свойств Properties укажите значение OledbConnectionl для свойства Connection и приведенное ниже значение для свойства CommandText элемента управления OledbCommandl.
SELECT * FROM EmployeeJobs_view
Создайте код подпрограммыприведенный в листинге 4.9.
СОВЕТ —:::—
| В 6,объектпредставлены графические инструменты,
которые позволяют автоматически создать строку подключения и текст команды SQL
i вместо создания вручную их кода.
Листинг 4.9. Код создания объектаи извлечения значений полей
С ПОМОЩЬЮИ
Private Subsender As
e AsHandles
' Открытие подключения перед вызовом метода
OleDbConnectionl.Open()
Dim reader =
TextBoxl.Clear()
While =&&
&&
ControlChars.Tab & ControlChars.Tab &
&
End While
Отмена выбора всех строк в текстовом = 0
reader.Close()
OleDbConnectionl.Close()
End Sub
Щелкните правой кнопкой мыши на проектев окне
Solution Explorer и выберите команду Properties в контекстном меню.
В папке Common Properties выберите элемент General, а затем выберите форму Form2 в текстовом поле Startup object (Объект запуска) данного приложения.
После запуска приложенияв текстовом поле формы
Form2 будут отображены данные из представленияJobs_view, как показа
но на рис. 4.5.
Другие провайдеры данных
Выше были представлены классы нескольких основных провайдеров данных (например, Parameter и Parameters), а также четыре основных объекта в табл. 4.1. В главе 5, "ADO.NET: объектболее подробно рассматривается объект Data
Set и связанные с ним объекты, а в главе 6, "ADO.NET: объект DataAdapter", — объект DataAdapter.
В оставшейся части главы рассматривается еще один провайдер данных — транзакция Transaction. Транзакции используются для гарантированного успешного
191
завершения сразу нескольких связанных операций по принципу "все или ничего". Это значит, что либо все операции транзакции успешно выполняются, либо они вообще не выполняются. Классическим примером транзакции является банковская операция перечисления денежных средств. Эта операция состоит из двух этапов: удержание денежной суммы с одного счета и зачисление ее на другой счет. При этом желательно избегать ситуаций, когда успешно выполняется только первый этап транзакции!
РИС. 4.5. Результаты отображения данных в форме Form 2 с помог^ъю элемен- товуправления OledbConnection и OledbCommand
Провайдеры данных ADO.NET содержат объект Transaction, который имеет фундаментальные методы обработки транзакций. Метод Commit фиксирует текущую транзакцию, а метод Rollback — откатывает (отменяет) текущую транзакцию. Выполнение транзакции и создание объекта Transaction осуществляется с помощью вызова метода BeginTransaction по отношению к открытому объекту Connection. Способ использования объекта Transaction демонстрируется на примере бизнес-ситуации 4.1.
Бизнес-ситуация 4.1: создание процедуры для архивирования старых заказов по годам
После относительно длительного использования системы управления базами данных некоторые данные рекомендуется архивировать. В каждой рабочей системе операцию архивирования следует включить в состав обязательных и регулярно выполняемых операций резервного копирования. Архивируемые данные — это данные, которые нужны не для постоянного использования (т.е. в оперативном режиме), а только изредка. Удаление этих данных из основных оперативных таблиц базы данных может повысить производительность операций доступа к этим таблицам, так как при этом приходится обрабатывать и фильтровать меньше записей. Однако архивная таблица часто хранится в идентичном формате таблицы и доступ к ней в случае необ-
ходимости можно организовать аналогичным образом. В этой бизнес-ситуации создается простая форма для выполнения простого архивирования данных из таблицы tblOrder базы данных Novelty. Она позволит выбирать и архивировать заказы по годам, т.е. после выбора нужного года выполняются перечисленные ниже действия.
Сначала в базе данных создается новая таблица tblOrderXXXX, где ХХХХ обозначает тот год, записи о заказах которого будут архивироваться.
Затем все записи о заказах за указанный год копируются из таблицы tblOrder втаблицуtblOrderXXXX.
Все скопированные записи о заказах за указанный год удаляются из таблицы tblOrder.
Хитрость здесь заключается в том, чтобы отменить всю транзакцию при неудачном выполнении какой-либо ее операции. Нам не нужна новая таблица, если в нее нельзя скопировать данные. Не нужно архивировать данные, если они не удаляются из основной таблицы. Аналогично, не нужно удалять никакие записи из основной таблицы, если они не скопированы в архивную таблицу. Для решения этой задачи можно использовать объект Transaction и вернуть (откатить) базу данных в исходное состояние в случае сбоя каких-то операций. Для создания формы с этими функциями выполните перечисленные ниже действия.
Запустите интегрированную среду разработки Visual Studio .NET.
Создайте новый проект Visual Basic Windows Application.
Назовите проект BusinessCase4.
Укажите путь к файлам проекта.
Увеличьте размер формы Forml.
В окне Properties укажите значение frmArchive для свойства (Name) и значение Archive Orders для свойства Text формы Forml.
Создайте в форме поле со списком IstYears, надпись Label 1, кнопку bntOK и кнопкуbntCancel, перетаскивая их из панели элементов управления.
В окне Properties укажите значение Archive all orders for the year для свойства Text надписи, значение OK для кнопки bntOK и значение Cancel для кнопки bntCancel.
Расположите все элементы управления, как показано на рис. 4.6.
РИС. 4.6. Расположение элементов управления в формеfrmArch i ve
193
В верхней части файла с исходным кодом вставьте приведенную ниже строку кода для импорта пространства имен SqlClient.
Imports System.Data.SqlClient
В теле определения класса для формы frmArchive включите код из листинга4.10.
Листинг 4.10. Код архивирования данных в новой таблице
Private Sub frmArchive_Load(ByVai sender As System.Object, _
В у Vai e As System.EventArgs) Handles MyBase.Load
IstYears.Items.Add("1995")
1stYears.Items.Add("1996")
IstYears.Items.Add("1997")
IstYears.Items.Add("1998")
IstYears.Items.Add("1999")
IstYears.Items.Add("2000")
IstYears.Items.Add("2001")
IstYears.Items.Add("2002")
' Указание значения по умолчанию.
IstYears.Selectedlndex = О
End Sub
Private Sub btnCancel_Click(ByVai sender As System.Object, _
ByVal e As System.EventArgs) Handles btnCancel.Click
Me. Close()
End Sub
Private Sub btnOK_Click(ByVai sender As System.Object, _
ByVal e As System.EventArgs) Handles btnOK.Click
Dim sql As String
Dim result As Integer
Dim records As Integer
Dim SelectedYear As String
' Создание экземпляров объектов Connection и Command.
Dim cnn As SqlConnection = New SqlConnection( _ "server=localhost;uid=sa;database=novelty") ■
Dim cmd As New SqlCommand()
Dim trans As SqlTransaction
' Получение значения года.
SelectedYear = IstYears.Selectedltem.ToString
Размещение кода внутри блока Try-Catch для
' обработки исключительных ситуаций.
Try
Открытие объекта Connection и запуск транзакции.
cnn.Open()
trans = cnn.BeginTransaction
' Включение команды в транзакцию.
cmd.Connection = cnn
cmd.Transaction = trans
' Указание команды SQL для вставки соответствующих
' записей в архивную таблицу.
sql = "SELECT * INTO tblOrder" & SelectedYear &
FROM tblOrder WHERE year (OrderDate) = " & SelectedYear
Передача текста команды SQL в транзакцию.
cmd.CommandText = sql
result = cmd.ExecuteNonQuery()
' Отображение результатов вставки записей в архивную таблицу. If result > 0 Then records = result
MessageBox.Show(records &
" records inserted successfully into tblOrder"
& SelectedYear)
Else
■ MessageBox.Show( _
"No records inserted into tblOrder" & SelectedYear)
' При отсутствии записей созданная таблица
' не нужна и транзакцию нужно откатить.
trans.Rollbackt)■
End If
If records > 0 Then
' Команда SQL для удаления соответствующих
' записей из текущей таблицы.
sql = "delete FROM tblOrder WHERE year(OrderDate) =_
& SelectedYear
' Эта команда находится в той же транзакции. cmd. CommandText = sql
result = cmd.ExecuteNonQuery()
' Показать результаты удаления записей.
If result = records Then
MessageBox.Show(records &
” records deleted successfully")
' Все действия успешно выполнены,
можно фиксировать транзакцию.
trans.Commit()
Else
MessageBox.Show("No records deleted!")
End If
Else
' Никаких действий.
End If
Catch ex As Exception
' Какие-то действия не выполнены, поэтому нужно
' отменить (откатить) всю транзакцию.
Try
1 Отображение сообщения об ошибке.
Модель ADO.NET: провайдеры данных 122195
MessageBox.Show(ex.Message &
ControlChars.CrLf & ControlChars.CrLf & "Transaction Failed ! ")
trans.Rollback()
Catch ex2 As Exception
End Try
Finally
cnn.Close ()
End Try
End Sub
Подпрограмма frmArchive_Load инициализирует список IstYears значениями, из которых можно выбрать год архивирования, и выбирает используемый по умолчанию год. Конечно, эту подпрограмму можно было бы усовершенствовать так, чтобы в списке отображались только те годы, для которых существуют записи о заказах. Однако для демонстрации принципов работы транзакций достаточно и такой подпрограммы.
Подпрограмма btnCancel_Click обработки щелчков мышью на кнопке Cancel просто закрывает форму, что в данном случае приводит к закрытию программы. Все необходимые действия выполняются обработчиком щелчков мышью на кнопке ОК. После объявлений переменных следует получить выбранный год из списка IstYears и сохранить его для дальнейшего использования. Для гарантированной отмены транзакции в случае возникновения любой исключительной ситуации следует окружить активный код блоком Try-Catch-Finally.
Поскольку транзакции определяются на уровне подключения, то сначала нужно открыть подключение, а затем создать объект Transaction с помощью вызова метода BeginTransaction для открытого подключения. Объекты Connection и Transaction присваиваются объекту Command, который будет использоваться для выполнения команд по отношению к базе данных.
Первые два этапа создания архивной таблицы и копирования выбранных строк выполняются с помощью одной команды SELECT, которая содержит предложение INTO имя_ та блицы.Указанная таким образом таблица создается автоматически, а если такая таблица уже существует, то генерируется исключительная ситуация. Выбранное значение года добавляется к имени таблицы tblOrder для создания имени новой архивной таблицы.
НА ЗАМЕТКУ
Команда SELECT INTO не создает индекс, если он существует в исходной таблице. i Для повышения производительности выполнения запросов по отношению к данной
таблице, вероятно, придется создать индексы по одному или нескольким полям.
Для выполнения команды SQL вызывается метод ExecuteNonQuery, который возвращает количество охваченных запросом записей. Если это возвращаемое значение больше нуля, следовательно, нужные записи найдены и вставлены в новую таблицу. В противном случае это значит, что таблица не может быть создана либо для копирования не найдено никаких записей. В любом из этих двух случаев транзакция
196
откатывается, даже если таблица создана, чтобы база данных не наполнялась пустыми и бесполезными таблицами.
Если хотя бы одна запись скопирована в таблицу, то ее прототип в исходной таблице tblOrder удаляется с помощью команды DELETE, содержащей предложение WHERE с заданным годом. В случае успешного выполнения этой команды, т.е. если количество скопированных и удаленных строк совпадает, транзакция считается успешно завершенной и фиксируется. В противном случае, т.е. если какая-то отдельная операция завершится неудачно (нарушится процесс удаления записей, будет отменено разрешение на удаление архивируемых данных или произойдет сбой сервера), вся транзакция будет отвергнута. Откат транзакции гарантирует, что при неудачной попытке удаления корректных записей из таблицы tblOrder архивная таблица tblOr- de гXXXX будет удалена.
До сих пор рассматривались только те исключительные ситуации, которые возникают при нарушении последовательного выполнения операций подпрограммы. Однако следует также позаботиться о многих других исключительных ситуациях, которые могут возникнуть во время выполнения. Например, исключительная ситуация может возникнуть при попытке создания уже существующей таблицы. Для ее перехвата и обработки следует использовать блок Try-Catch, который в данном случае выводит текстовое сообщение об исключительной ситуации, а вся транзакция отвергается.
НА ЗАМЕТКУ
Еще один блок Try-Catch потребуется для обработки исключительной ситуации, ко- | гда архивная таблица не может быть создана (например, из-за наличия именно такой j таблицы). Основная причина, по которой она предлагается, заключается в том, что j | транзакция началась, но никаких изменений данных не произошло, а потому в файл j регистрации транзакций не записано никаких действий, которые нужно откатить.
В блоке Finally используемое подключение закрывается независимо от того, произошла или нет исключительная ситуация.
Выполните все описанные выше действия и поэкспериментируйте с этим приложением, создавая архивы для записей разных лет. Для проверки полученных результатов сопоставьте содержимое новой архивной и исходной таблиц до и после архивирования. Не забывайте, что содержимое исходной таблицы tblOrder всегда можно вернуть в первоначальное состояние, запуская сценарий ее создания и наполнения данными.
Резюме
В этой главе приводятся общие сведения об ADO.NET и некоторых объектах провайдеров данных .NET. Провайдеры данных образуют интерфейс ADO.NET для взаимодействия с физическими хранилищами данных и предлагают модель программирования в режиме подключения. Здесь кратко рассматриваются свойства и методы объектов Connection, Command, Parameter, DataReader и Transaction, включая стандартные провайдеры данных SqlClient, Oledb и Odbc. В главе 5, "ADO.NET: объект DataSet”, описываются способы работы с данными в отключенном режиме на основе объектов DataSet и DataAdapter.
197
Вопросы и ответы
Судя по содержанию этой главы, модель ADO.NET предназначена для работы в отключенном режиме и нет никакой поддержки для серверных курсоров или пессимистической блокировки. А что же делать, если уже существующее приложение использует их или спецификации нового проекта требуют их применения? Следует ли мне использовать для этого только Visual Basic 6.0?
Во-первых, необходимо тщательно проанализировать приложение и убедиться в том, что действительно всегда нужно использовать серверные курсоры или пессимистические блокировки. Если они (или какие-то другие компоненты, которые не поддерживаются в ADO.NET) действительно необходимы, не стоит отчаиваться. Visual Basic .NET все равно можно использовать для таких приложений, так как на платформе .NET Framework предусмотрена расширенная поддержка взаимодействия с СОМ, что позволяет использовать в . NET-совместимом приложении COM-объекты, которые, в свою очередь, могут использовать управляемый (.NET-совместимый) код. Иначе говоря, можно использовать не только ADO 2.X, но и любые другие COM-объекты, для которых нет аналогов в .NET. Конечно, за организацию взаимодействия между СОМ и .NET придется заплатить снижением производительности. В какой мере? Ответ на этот вопрос можно получить только после тщательного тестирования приложения.
Похоже, что программирование объектов, методов и свойств ничем не отличается от способов их программирования в модели ADO 2.X. Зачем же переходить к модели ADO.NET?
Конечно, выполнение основных операций подключения к базе данных не очень отличается от таких же операций в модели ADO 2.X. Однако, кроме уже упомянутых здесь небольших усовершенствований, следует иметь в виду ряд важных отличий.
Visual Basic .NET и платформа .NET представляют собой совершенно новый мир, а модель ADO.NET является способом доступа к данным в этом мире.
Хотя при создании .NET-совместимых приложений можно продолжать использование уже существующих СОМ-компонентов, например из модели ADO 2.X, такой способ связан с сокращением производительности при доступе к СОМ- компонентам и необходимости их корректной инсталляции и регистрации.
Рассмотренные в этой главе объекты разных провайдеров данных отвечают только за часть всех операций с базой данных, а именно за чтение и запись данных в подключенном режиме. Другая часть операций выполняется в отключенном режиме и связана с объектом DataSet. Именно в этом режиме прекрасно проявляются преимущества модели ADO.NET, и потому в следующей главе описываются базовые блоки объекта DataSet.
ADO.NET: объектADO.NET: объект
ADO.NET: объект
ADO.NET: объект
DataSet
В ЭТОЙ ГЛАВЕ...
Компоненты объекта DataSet
Ввод данных в объект DataSet
Применение объекта DataSet
Резюме
Вопросы и ответы
Объект DataSet является центральным и наиболее революционным элементом модели доступа к данным ADO.NET. По сути, это кэш-область в оперативной памяти для данных из одного или нескольких источников. Его можно представить как полноценную базу данных, которая полностью находится в оперативной памяти. Вероятно, наиболее важной характеристикой объекта DataSet является его использование в отключенном режиме. Описанные в главе 4, "Модель ADO.NET: провайдеры данных", управляемые объекты провайдеров данных предоставляют функции, необходимые при физическом подключении к базе данных или другому источнику данных. А объект DataSet и связанные с ним объекты (DataTable, DataRow, DataColumn и DataRelation) предлагают богатую функциональность в отключенном от источника данных режиме работы.
Еще одной ключевой характеристикой объекта DataSet является независимость загруженных данных от их источника. Он содержит данные и позволяет манипулировать ими в реляционной манере. Поэтому DataSet и подчиненные ему объекты имеют универсальный способ применения и не связаны с каким-то отдельным провайдером данных. Таким образом, существует только один объект DataSet и не существует никаких других специализированных объектов DataSet, например SqlDataSet, OledbDataSetили OdbcDataSet.
Если объекту DataSet не известен источник данных, то как же данные загружаются в него и как изменения данных передаются источнику? Ответ на эти вопросы можно получить, познакомившись с объектом DataAdapter, который служит мостом между объектом DataSet и физическим источником данных. Объект DataAdapter содержит специальные команды чтения данных из источника данных, а также команды обновления, удаления и вставки данных в источнике. Более подробно он рассматривается в главе 6, "ADO.NET: объект DataAdapter".
Компоненты объекта DataSet
Объект DataSet является ключевым объектом ADO.NET и служит универсальным контейнером данных, независимо от используемого источника данных. Объект DataSet и связанные с ним подчиненные объекты предлагают реляционное представление данных, хотя он также способен загружать и сохранять свои данные в формате XML. Объект DataSet предлагает явную модель хранения данных в оперативной памяти, которая существует в полностью отключенном от источника данных состоянии и может легко передаваться между разными адресными пространствами и компьютерами.
Ниже перечислены возможности, которыми обладает объект DataSet.
Хранение данных приложения. Объект DataSet может легко и гибко использоваться для хранения локальных данных приложения. Доступ к данным так же прост, как и доступ к данным в массиве, но объект DataSet предлагает дополнительные функции, например для сортировки и фильтрации,
Использование удаленных данных. Объект DataSet автоматически использует формат XML для маршалинга данных (т.е. их передачи от одного компьютера к дру- тому). Эта возможность существенно упрощает разработку приложений на основе Web-служб, SOAP или удаленного доступа к данным на более низком уровне.
Кэширование данных. Объект DataSet может кэшировать данные во время создания распределенных приложений на основе ASP.NET или других технологий, сокращая объем передаваемых по сети данных.
201
Устойчивое хранение данных. Объект DataSet предлагает методы сохранения своих данных и информации о схеме данных в стандартном формате XML.
Взаимодействие с пользователями. Объект DataSet эффективно поддерживает взаимодействие с пользователями для разных графических интерфейсов пользователя, комбинируя функции сортировки, фильтрации и прокрутки с возможностями их связывания с разными представлениями данных на основе Windows Forms и Web Forms.
Данные в объекте DataSet организованы в одной или нескольких объектах DataTable. Каждый объект DataTable существует независимо от источника данных, т.е. он всегда отключен от источника данных, как и DataTable. Объект DataTable всего лишь хранит несколько таблиц данных и предлагает возможности манипулирования, передачи или связывания их с элементами управления пользовательского интерфейса. На рис. 5.1 показана схема взаимодействия объекта DataSet и связанных с ним подчиненных объектов.
РИС. 5.1. Схема взаимодействия объекта DataSet с подчиненными объектами
Объекты, которые являются подчиненными по отношению к объекту DataSet, обладают перечисленными ниже возможностями.
Объект DataTable содержит коллекции объектов DataRow, DataColumn и Constraint, а также коллекции объектов DataRelation, связанные с другими родительскими и дочерними объектами. Представление данных в этом объекте аналогично представлению данных в объекте Recordset в ADO 2.X.
Глава 5
202
ОбъектDataColumn является базовой единицей извлечения данных и определения схемы для объекта DataTable. Он содержит специализированную информацию для каждого поля объекта DataTable, включая имя, тип данных и другие атрибуты (например, свойства Unique, Readonly, AllowDBNull и AutoIncrement). Кроме того, он имеет свойство Expression для вычисления значения поля или создания итогового поля.
Объект DataRow представляет одну запись объекта DataTable и используется
для добавления, извлечения и изменения данных в объекте DataTable. С его помощью можно осуществлять последовательный или непосредственный доступ к нужным записям объекта DataTable.
Объект DataRelation определяет отношение между двумя таблицами объекта
DataSet. Он представляет классическое отношение между родительской и дочерней таблицей (т.е. между ключевым и внешним полями двух таблиц). Переходы между отношениями выполняются с помощью коллекций ChildRelations и ParentRelations (объектов DataRelation)объектаDataTable.
Объект Constraint определяет правило, согласно которому поддерживается целостность данных в объекте DataTable. Он содержит уже знакомое ограничение UniqueConstraint, гарантирующее уникальность значений таблицы, а также ограничение ForeignKeyConstraint, определяющее действия по OT- ношению к строкам в связанной таблице. Ограничение может относиться к одному или нескольким объектам DataColumn. Каждый объект DataTable имеет свойство Constraint с коллекцией ограничений для данной таблицы.
Ввод данных в объект DataSet
Для ввода данных в таблицы DataTable объекта DataSet предусмотрены перечисленные ниже способы.
Программирование определений метаданных и прямая вставка данных.
Использование объекта DataAdapter для создания запроса по отношению к источнику данных.
Загрузка XML-документа.
В этой главе представлен первый из перечисленных выше способов, в главе 6, “ADO.NET: объект DataAdapter", — второй, а в главе 10, “ADO.NET и XML", — третий. Этот раздел начинается с описания базовых функций объектов DataSet и DataTable.
Учтите, что после загрузки данных в объект DataSet, использованный метод загрузки далее уже не имеет никакого значения, потому что все последующие операции с данными выполняются идентично.
Определение схемы объекта DataTable
Для иллюстрации основных принципов работы следует создать простую форму с помощью перечисленных ниже действий.
Запустите интегрированную среду разработки приложений Visual Studio .NET.
ADO.NET: объект DataSet -203
Создайте новый проект Visual Basic Windows Application. Для этого в диалоговом окне New Project (Новый проект) выберите тип проекта Visual Basic Project в области Project Types (Типы проектов), а затем шаблон Windows Application (Приложение Windows) в области Templates (Шаблоны).
Назовите проект DataSetCode.
Укажите путь к файлам проекта.
Увеличьте размер формы Forml.
В окне Properties укажите значение frmDataSets для свойства (Name) и значение DataSets для свойства Text формы Forml.
В верхнем левом углу формы создайте кнопку, перетаскивая ее из панели эле- ментовуправления.
В окне Properties укажите значение btnCreateDS для свойства (Name) и значение Create DataSet для свойства Text этой кнопки.
В правой части формы создайте поле со списком, перетаскивая его из панели элементов управления.
В окне Properties укажите значение IstOutputдля свойства (Name).
Увеличьте размер поля со списком, чтобы оно занимало до 80% всей площади формы.
В верхней части файла введите следующий код:
Imports System
Imports System.Data
Затем в определении класса формы frmDataSets введите приведенный ниже код.
Private dsEmployeelnfo As DataSet
Private Sub btnCreateDS_Click(ByVai sender As System.Object, _ ByVai e As System.EventArgs) Handles btnCreateDS.Click CreateDataSet()
AddData()
DisplayDataSet()
End Sub
Подпрограмма btnCreateDS_Click обработки щелчков на кнопке Create DataSet вызывает три другие подпрограммы для каждой фазы работы приложения. Переменная dsEmployeelnfo является объектом DataSet, доступ к которому выполняется с помощью подпрограмм внутри подпрограммы btnCreateDS_Click. —
НА ЗАМЕТКУ
Обычно объекты DataTable содержатся в объекте DataSet, но объект DataSet можно использовать непосредственно, как правило в наиболее простых ситуациях, ) когда не требуется организовывать связь между несколькими таблицами.
Итак, прежде всего нужно определить схему (или структуру) всех используемых таблиц. Для этого нужно определить все объекты DataColumn таблицы и указать их свойства, как показано на примере подпрограммы CreateDataSet () в листинге 5.1.
ЛИСТИНГ 5.1. КОД СОЗДаНИЯ Объектов DataSet И DataTable
Private Sub CreateDataSet()
' Создание объекта dsEmployeelnfo.
dsEmployeelnfo = New DataSet ()
' Создание таблицы Employees.
Dim dtEmployees As DataTable = New DataTable("Employees")
dtEmployees.CaseSensitive = False
dtEmployees.Columns.Add("FirstName", Type.GetType("System.String")) dtEmployees.Columns.Add("LastName", Type.GetType("System.String")) dtEmployees.Columns.Add("DepartmentID", Type.GetType("System.Int32"))
' Вставка таблицы Employees в объект Employeeinfo.
dsEmployeelnfo.Tables.Add(dtEmployees)
Создание таблицы Departments с
помощью перегруженной версии конструктора.
Это более длительный способ при создании стандартных полей, но он позволяет задавать другие свойства полей
(например, Readonly & Unique)
до включения поля DataColumn в коллекцию Columns.
Dim dtDepartments As DataTable
dtDepartments = New DataTable ()
dtDepartments.TableName = "Departments"
dtDepartments.Minimumcapacity = 5
dtDepartments.CaseSensitive = False
Dim NewColumn As New DataColumn()
With NewColumn
.ColumnName = "ID"
.. DataType =
.Readonly = True
.Unique = True
.AutoIncrement = True
End With
dtDepartments.Columns.Add(NewColumn)
NewColumn = New DataColumn()
With NewColumn
.ColumnName =
.DataType =
.Unique = True
.AllowDBNull = False
End With
dtDepartments.Columns.Add(NewColumn)
' Включение таблицы Departments в объект dsEmployeelnfo.
dsEmployeelnfo.Tables.Add(dtDepartments)
End Sub
После создания экземпляра dsEmployeelnfo объекта DataSet создается таблица Employees с помощью перегруженных конструкторов объекта DataTable с использованием параметра-имени таблицы. Затем задается значение False для свойства CaseSensitivity объекта DataTable. Это свойство определяет, будут ли операции сортировки, поиска и фильтрации выполняться с учетом регистра символов. По умолчанию значение этого свойства определяется как значение свойства CaseSensitivity
205
родительского объекта DataSet или принимается равным False, если объект DataTable создан независимо от объекта DataSet.
НА ЗАМЕТКУ
Свойство CaseSensitivity применяется только для данных объекта DataTable и не влияет на имена самих объектов DataTable. Например, объект DataSet, может иметь две таблицы (или отношения) с именами mytable или Mytable. Для работы с ними нужно записывать имена с точным указанием регистра символов, поскольку их поиск ведется с учетом регистра (case-sensitive search). Однако это не обязательно при наличии только одной таблицы с таким именем, поскольку при этом используется поиск без учета регистра (case-insensitive search).
Затем создаются определения полей с помощью метода Add объекта Column по указанному имени поля и типу данных. Учтите, что здесь указываются .NET- совместимые типы данных, а не используемые в базе данных типы. При отсутствии типа данных для него по умолчанию принимается строковый тип. Наконец, таблица Employee включается в объект dsEmployeelnfо.
Далее этот процесс повторяется для таблицы Departments, но теперь уже с перегруженными конструкторами и функциями. Таким образом та же цель достигается другим способом. Разработчик может самостоятельно выбрать наиболее подходящий способ на основе собственного вкуса, корпоративных стандартов или особенностей поставленной задачи.
Для свойства Minimumcapacity объекта dtDepartments задается значение 5, т.е. экземпляр объекта DataTable создается уже с пятью записями. Указание для него другого значения (отличного от используемого по умолчанию значения 25) позволяет управлять выделением ресурсов и оптимизировать производительность в критических ситуациях. Конечно, на самом деле эти пять записей фактически появятся только после того, как пользователь добавит их в таблицу DataTable, а пока для них резервируется место.
Кроме того, перед добавлением полей в коллекцию Columns для них задаются значения других свойств. Перечисленные ниже свойства Readonly, Unique, AllowDBNull и AutoIncrement уже наверняка знакомы тем, кто имеет опыт создания приложений для работы с базами данных.
Присвоение свойству Readonly значения True указывает на то, что значение поля нельзя изменить.
ПрисвоениесвойствуUniqueзначенияTrue указываетнато,чтозначения
данного поля во всех записях таблицы должны быть уникальными. Это свойство реализуется с помощью автоматического создания ограничения UniqueConstraint для данного поля. Этот способ более подробно рассматривается в разделе об ограничениях таблицы далее в главе.
Присвоение свойству AllowDBNull значения True указывает наTO, что в данном поле допускается использование неопределенных значений.
Присвоение свойству Autolncrement значения True указывает на то, что
значение поля увеличивается при каждом добавлении в таблицу новой записи. Для указания начального значения и приращения используются свойства AutoIncrementSeed и AutoIncrementStep.
НА ЗАМЕТКУ
Объект DataTable принимает новую запись и присваивает автоматически увеличен- • ное значение для поля, свойство которого AutoIncrement имеет значение True, только если значение поля отличается от принимаемого по умолчанию.
Среди других свойств объекта DataColumn следует отметить MaxLength (для полей с данными типа String), DeafultValue и Table. Поле также можно определить с выражением для вычисления значения, создания итогового поля или фильтрования строк. Такое выражение может состоять из имен полей текущей записи или других записей, констант, операторов, символов подстановки, итоговых и других функций. Более подробную информацию и примеры таких выражений можно найти в справочных материалах для свойства Expression объекта DataColumn.
Вставка данных в объект DataTable
После определения объекта DataTable и его схемы можно начинать ввод данных. В листинге 5.2 приводится код вставки записей с данными в объект DataTable. Подпрограмма AddData включает четыре записи с данными в таблицу Departments и три записи с данными в таблицу Employees следующим образом.
Сначала создается новый экземпляр объекта DataRow для нужной таблицы с помощью метода NewRow.
Затем присваиваются значения полям этой записи.
После этого запись включается в коллекцию записей Rows таблицы с помощью метода Add свойства Rows таблицы.
ЛИСТИНГ 5.2. КОД Программного вводаданных В Объект DataTable
Private Sub AddData()
Dim dtDepartments As DataTable = dsEmployeelnfо.Tables("Departments")
Dim dtEmployees As DataTable = dsEmployeelnfо.Tables("Employees")
' Вставка четырех записей в таблицу Departments.
Dim rowDept As DataRow
rowDept = dtDepartments.NewRow
rowDept("ID") = 11
rowDept("DepartmentName") = "Administration"
dtDepartments.Rows.Add(rowDept)
rowDept = dtDepartments.NewRow
rowDept("ID") = 22
rowDept("DepartmentName") = "Engineering"
dtDepartments.Rows.Add(rowDept)
rowDept = dtDepartments.NewRow
rowDept("ID") = 33
rowDept("DepartmentName") = "Sales"
dtDepartments.Rows.Add(rowDept)
207
rowDept = dtDepartments.NewRow
rowDept("ID") = 44
rowDept("DepartmentName") = "Marketing" dtDepartments.Rows.Add(rowDept)
' Вставка трех записей в таблицу Employees. Dim rowEmployee As DataRow
rowEmployee = dtEmployees.NewRow rowEmployee("FirstName") = "Jackie" rowEmployee("LastName") = "Goldstein" rowEmployee("DepartmentID") = 22 dtEmployees.Rows.Add(rowEmployee)
rowEmployee = dtEmployees.NewRow rowEmployee("FirstName") = "Jeffrey" rowEmployee("LastName") = rowEmployee("DepartmentID") = 33 dtEmployees.Rows.Add(rowEmployee)
rowEmployee = dtEmployees.NewRow rowEmployee("FirstName") = "Sam" rowEmployee("LastName") = "Johnson" rowEmployee("DepartmentID") = 33 dtEmployees.Rows.Add(rowEmployee)
End Sub
HA ЗАМЕТКУ
Новую запись можно вставить в таблицу, передавая методу Add массив объектов, содержащих данные в порядке следования полей в определении таблицы.
В листинге 5.2 этот способ мог быть представлен так:
Dim empData(2) As Object।
empData(0) = "Sam"
: empData(1) = "Johnson"
empData(3) = 33
: dtEmployees.Rows.Add(empData)
Обновление данных в объекте DataSet
Для обновления отдельной записи таблицы необходимо просто организовать доступ к нужной записи и присвоить новое значение одному из полей. Например, для изменения номера отдела, к которому относится Sam Johnson, можно использовать следующую строкукода:
dtEmployees.Rows(2) ("DepartmentID") = 2
НА ЗАМЕТКУ
В этой строке кода номер записи (2) приводится из предположения, что нам известно расположение записей в таблице. Но с практической точки зрения этот способ не совсем удачен, более эффективный и безопасный способ основан на поиске нужной строки (или нескольких строк). Он описывается в разделе о доступе к данным в объекте DataTable далее в главе.
Таким образом, в данные можно вносить произвольное количество изменений, но все они не будут зафиксированы до тех пор, пока не будет вызван метод AcceptChanges. Для отката изменений, внесенных после загрузки данных или последнего вызова метода AcceptChanges, используется метод Re j ectChanges.
НА ЗАМЕТКУ
Метод AcceptChanges (и метод Rej ectChanges) можно использовать на нескольких разных уровнях, т.е. в классах DataTable, DataSet и DataRow. Вызов метода AcceptChanges объекта DataSet приведет к вызову этого метода для каждой таблицы объекта DataSet. Аналогично вызов метода AcceptChanges объекта DataTable приведет к вызову этого метода для каждой записи объекта DataTable. Таким образом можно зафиксировать изменения отдельно для каждой записи или сразу для всех данных объекта DataSet. To же самое относится к методу Rej ectChanges.
Строки можно вставлять и удалять целиком. До сих пор был показан только процесс создания записей, а удаление записи основано на методе Remove объекта Da- taRowCollection (т.е. свойство Rows объекта DataTable). Этот метод полностью удаляет запись из коллекции. Другой способ удаления записи основан на методе Delete объекта DataRow. Этот метод отмечает запись для удаления, которое на самом деле произойдет только после вызова метода AcceptChanges.
После вызова метода Remove все данные записи будут удалены необратимо, даже если после этого вызвать метод Re j ectChanges.
Состояние и версия записи
Каждый объект DataRow имеет свойство RowState, которое обозначает текущее состояние или статус записи. Кроме того, каждая запись хранит информацию о четырех разных версиях своего значения. По мере редактирования записи изменяется ее состояние и версия значения. В табл. 5.1 приведено краткое описание свойства RowState, а в табл. 5.2 — краткое описание свойства DataRowVersion.
ТабЛИЦа 5.1. СВОЙСТВО RowState
Член пере- Описание числения
Unchanged Никаких изменений не внесено с момента последнего вызова метода
AcceptChanges или после загрузки данных с помощью объекта DataAdapter
AddedЗапись вставлена в коллекцию DataRowCollection (т.е. в свойство Rows объ
екта DataTable), но метод AcceptChanges еще не вызывался
DeletedМетод Delete вызван для удаления записи, но метод AcceptChanges еще не
вызывался
ModifiedЗапись изменена, но метод AcceptChanges еще не вызывался
Detached Запись создана, но не добавлена в коллекцию DataRowCollection, либо метод Remove вызван для удаления записи из коллекции DataRowCollection, либо метод Delete вызвандляудалениязаписиивызванметод AcceptChanges
Таблица 5.2. СВОЙСТВО DataRowVersion
Член nqbe- Описание
числения
Original Исходные значения записи. Эта версия не существует для строки со значением Added свойства состояния записи RowState
CurrentТекущее (возможно измененное) значение записи. Эта версия не существует для
строки со значением Deleted свойства состояния записи RowState
DefaultИспользуемая по умолчанию версия записи, которая зависит от текущего состояния
записи. Если состояние записи RowState имеет значение Deleted, то по умолчанию используется версия Original, если значение Detached — версия Proposed. Во всех остальных случаях по умолчанию используется версия Current
Proposed Предлагаемое значение записи. Эта версия существует только во время редактирования (начинается с вызова метода BeginEdit и заканчивается вызовами методов EndEdit или CancelEdit) либо для записи, которая еще не включена в коллекцию DataRowCollection
Если запись находится в состоянии Deleted в момент вызова метода AcceptChanges, то она удаляется из коллекции DataRowCollection. В противном случае версия записи Original обновляется версией Current, а состояние записи становится равным Unchanged.
HA ЗАМЕТКУ
Посколькувсе четыре версии записей возможны далеко не всегда, то можно вызвать
• метод HasVersion для объекта DataRow, чтобы проверить конкретную версию в те- ( кущем состоянии. МетодуHasVersion передается значение одного из членов пере' | числения DataRowVersion, а он возвращает логическое значение, которое указыва- 1 ет, существует ли данная версия.
Llt^4JrV■■iпl,^^^^,.
Наоборот, если запись находится в состоянии Added в момент вызова метода RejectChanges, то запись удаляется из коллекции DataRowCollection. В противном случае версия записи Current обновляется версией Original, а состояние записи становится равным Unchanged.
НА ЗАМЕТКУ —
Доступ к версии поля DataRow (при условии, что она существует) можно получить, i указывая нужную версию в качестве второго параметра для метода Item объекта i DataRow при явном или неявном вызове этого метода:
j dtEmployees.Rows(2).Item("lastname", DataRowVersion.Proposed)
I или
I dtEmployees.Rows(2)("lastname", DataRowVersion.Original)
Здесь следует особое внимание обратить на версию Proposed объекта DataRow. При вызове метода BeginEdit объекта DataRow обычные действия и события приостанавливаются, что позволяет пользователю вносить несколько изменений в запись,
не применяя правил проверки вводимых значений. В этом режиме вносимые изменения отражаются не в версии Current, а в версии Proposed. И только после вызова метода EndEdit значения версии Proposed становятся (переносятся) значениями версии Current. Любые изменения можно отменить, вызывая метод CancelEdit до вызова метода EndEdit. Учтите, что изменения будут зафиксированы только после вызова метода AcceptChanges.
Обработка ошибок ввода данных в записи и поля
В модели ADO.NET предусмотрен гибкий механизм определения и обработки ошибок ввода данных в записи и поля объекта DataTable. Этот механизм позволяет использовать в приложении правила проверки вводимых данных, сообщать об обнаруженных ошибках, но откладывать их исправление до определенного момента в потоке выполнения приложения. (Не путайте их с обычными системными исключительными ситуациями времени выполнения, которые обрабатываются стандартными конструкциями платформы .NET Framework на основе операторов Try-Catch-Finally.)
Если приложение обнаруживает ошибку ввода данных, то оно создает описание найденной ошибки для записи или отдельного поля. Для обозначения наличия ошибки в отдельной записи используется свойство RowError объекта DataRow, например ему присваивается строка с комментарием "Something wrong here" (Здесь произошла какая-то ошибка).
myDataRow.RowError = "Something wrong here"
А для обозначения наличия ошибки в отдельном поле используется метод SetColumnError объекта DataRow, например с его помощью присваивается строка с комментарием "Bad data in this column" (Неверные данные в этом поле).
myDataRow.SetColumnError (2, "Bad data in this column")
Для извлечения строк с сообщениями о таких ошибках в полях или записях можно использовать свойство RowError или вызвать метод, GetColumnError. Для сброса значения свойства RowError нужно присвоить ему пустую строку ("") или использовать метод ClearErrors объекта RowError, который очищает свойство RowError и удаляет все сообщения об ошибках, заданные методом SetColumnError.
Объект DataRow содержит свойство HasErrors, которое имеет значение True, если в поле или записи обнаружены какие-либо ошибки. Значение этого свойства отображается на свойство HasErrors объекта DataTable, которое при наличии ошибок в поле или записи также равно True. Аналогично, если свойство HasErrors для любой таблицы DataTable объекта DataSet имеет значение True, то значение свойства HasErrors объекта DataSet также равно True. Метод GetErrors объекта DataTable возвращает массив объектов DataRow, которые содержат ошибки. В целом этот простой механизм позволяет быстро определить любые ошибки ввода данных, как показано в листинге 5.3.
Листинг 5.3. Пример обнаружения ошибок во всех таблицах объекта DataSet
Private Sub ResolveErrors (myDataSet as DataSet)
Dim rowsWithErrors() As DataRow
Dim myTable As DataTable
Dim myCol As DataColumn
211
Dim currRow As Integer
For Each myTable In myDataSet.Tables
If myTable.HasErrors Then
' Извлечение всех записей с ошибками.
RowsWithErrors =
For currRow = 0 To rowsWithErrors.GetUpper For Each myCol In myTable.Columns
' Найти поля с ошибками и выбрать ' способ их обработки.
' Ошибка в поле извлекается с помощью метода
' rowsWithErrors(currRow).GetColumnError(myCol). Next
' Очистка ошибок.
rowsWithErrors(currRow).ClearErrors Next currRow
End If
Next
End Sub
Доступ к данным с помощью объектаDataTable
Поскольку объект DataSet и содержащийся в нем объект DataTable всегда наполнены данными и не подключены к источнику данных, метод доступа к записям данных в них существенно отличается от методов доступа в ADO и других моделях доступа к данным (например, ODBC, DAO или RDO). Поскольку все данные доступны одновременно, в модели ADO.NET не существует понятия текущей записи. Поэтому нет никаких свойств или методов для перемещения от одной записи к другой. Каждый объект DataTable имеет свойство Rows, которое является набором объектов DataRow. Доступ к отдельному объекту осуществляется с помощью индекса или оператора For Each. Таким образом, в модели ADO.NET предлагается более простой и эффективный способ доступа и перемещения, аналогичный доступу к элементам массива.
В листинге 5.4 показан код подпрограммы DisplayDataSet, которая отображает содержимое ранее созданных таблиц с загруженными в них данными. В ней применяется циклический обход всех элементов коллекции, т.е. коллекций Rows и Columns, для отображения содержимого таблицы Employees. Далее используется альтернативный метод доступа к записям и полям с помощью числового индекса для отображения содержимого таблицы Departments.
ЛИСТИНГ 5.4. КОД Отображения данных В Объектах DataTable
Private Sub DisplayDataSet()
Dim dr As DataRow
Dim dc As DataColumn
Me.IstOutput.Items.Add("DISPLAY DATASET")
Me. IstOutput. Items .Add ("—=============")
Отображение данных из таблицы Employees.
For Each dr In dsEmployeelnfo.Tables("Employees").Rows
For Each dc In _
dsEmployeelnfо.Tables("Employees").Columns
Me.IstOutput.Items.Add( _ dc.ColumnName & " :& dr(do))
Next
Me . IstOutput. Items .Add ( "" )
Next
Me.IstOutput.Items.Add("")
' Отображение данных ИЗ таблицы Departments.
' Пример использования индексов вместо оператора For Each.
Dim row As Integer
Dim col As Integer
For row = 0 To dsEmployeelnfо.Tables("Departments").Rows.Count - 1
For col = 0 To dsEmployeelnfo.Tables("Departments").Columns.Count - 1
Me.IstOutput.Items.Add( _ dsEmployeelnfo.Tables("Departments").Columns(col).ColumnName _
& " : " & _
dsEmployeelnfo.Tables("Departments").Rows(row)(col))
Next col
Me . IstOutput. Items .Add ( "" )
Next row
End Sub
Аналогично можно создать подпрограмму более общего типа для обхода не только записейиполей, ноитаблицобъектаDataSet, какпоказановлистинге5.5.
ЛИСТИНГ 5.5. КОД обхода таблиц ИЗ Объекта DataSet
Private Sub DisplayDataSet(ByVai ds As DataSet)
' Общая подпрограмма для отображения содержимого объекта DataSet.
' Отображаемый объект DataSet передается как параметр.
Dim dt As DataTable
Dim dr As DataRow
Dim dc As DataColumn
Me.IstOutput.Items.Add("DISPLAY DATASET")
Me.IstOutput.Items.Add("===============")
For Each dt In ds.Tables
Me.IstOutput.Items.Add("")
Me.IstOutput.Items.Add("TABLE: " & dt.TableName)
Me. IstOutput. Items .Add ("" )
For Each dr In dt.Rows
For Each dc In dt.Columns
Me.IstOutput.Items.Add(dc.ColumnName S " :& dr(de))
Next
Me. IstOutput. Items .Add ("" )
Next
Next dt
End Sub
Обратите внимание, что здесь перегружается уже упомянутый ранее метод DisplayDataSet, которыйтеперьпринимаетвкачествепараметраобъектDataSet.
213
Попробуйте запустить полученное приложение; для этого введите упомянутый ранее код в проект DataSetCode и щелкните на кнопке Create DataSet. В результате этого действия создается объект DataSet и наполняется данными, которые затем отображаются в текстовом поле формы, как показано на рис. 5.2.
W DataSets
ВИВ
Create DataSet
DISPLAY DATASET
FirstN ame: Jackie
LastN ame Goldstein Department!D:22
FirstN ame Jeffrey LastName: McManus
Department! D :
FirstN ame: Sam LastN ame: Johnson DepactmentlD : 33
ID:11
DepartmentName :
ID: 22
DepartmentName: Engineering
ID:33
DepartmentName: Sale:
ID:44
DepartmentName: Marketing
РИС. 5.2. Результат создания объекта DataSet с таблицами Employees и Departments, наполнения их данными и посяе- дующего отображения
НА ЗАМЕТКУ
Для проверки обобщенной версии подпрограммы DisplayDataSet в подпрограмме з btnCreateDS_Click следует заменить вызов ее исходной версии DisplayDataSet о новой перегруженной версией DisplayDataSet (dsEmployeelnfo) с параметром dsEmployeelnfo.
Поиск, фильтрация и сортировка записей
Иногда нужно работать не со всеми, а только с некоторыми записями объекта DataSet, например с одной записью или подмножеством всех записей. Для этого можно использовать методы Find и Select.
Метод Find принадлежит свойству DataRowCollection объекта DataTable, который используется для поиска и возвращения единственной строки, указанной с помощью значения первичного ключа таблицы.
Перед использованием метода Find для обнаружения некоторой строки в таблице Departments, которая определена в листинге 5.1, нужно определить первичный ключ таблицы. Это можно сделать с помощью присвоения одного или нескольких полей свойству PrimaryKey таблицы. (Даже если первичный ключ создан на основе единственного поля, свойство PrimaryKey таблицы является массивом объектов DataColumn.)
В приведенных ниже строках кода, которые следует добавить в конце подпрограммы CreateDataSet из листинга 5.1, первичный ключ таблицы Departments создается на основе поля DepartmentName.
Dim рк(О) As DataColumn
pk(0) = dtDepartments.Columns("DepartmentName") dtDepartments.PrimaryKey = pk
НА ЗАМЕТКУ ;
При создании первичного ключа с помощью свойства PrimaryKey для объекта i DataTable на основе одного поля для свойства AllowDBNull этого поля автоматически задается значение False, а для свойства Unique — значение True. А если первичный ключ создан на основе нескольких полей, то только для свойства AllowDBNull этих полей автоматически задается значение False.
После определения первичного ключа метод Find используется так, как показано ниже.
Dim desiredRow As DataRow
desiredRow = dtDepartments.Rows.Find("sales")
Здесь переменной desiredRow присваивается объект DataRow с указанным значением первичного ключа или значение Nothing, если такая запись не будет найдена.
Если первичный ключ таблицы основан на нескольких полях, то соответствующие значения первичного ключа передаются в виде элементов массива (типа Object) методу Find.
' Указание первичного ключа.
Dim pk(0) As DataColumn
pk(0) = dtEmployees.Columns("FirstName")
pk(l) = dtEmployees.Columns("LastName") dtEmployees.PrimaryKey = pk
' Попытка поиска нужной записи.
Dim desiredRow As DataRow
Dim desiredValues(1) As Object
desiredValues(0) = "Sam"
desiredValues(1) = "Johnson"
desiredRow = dtEmployees.Rows.Find(desiredValues)
Метод Select объекта DataTable возвращает массив объектов DataRow. Возвращаемые строки могут соответствовать критерию фильтрования, порядку сортировки и/или спецификации состояния (объект DataViewRowState пространства имен System. Data).
Приведенный ниже код возвращает и отображает имена всех сотрудников с фами- лиейJohnson.
Dim selectedRows() As DataRow
selectedRows = dtEmployees.Select("LastName =
Dim i As Integer
For i = 0 to selectedRows.GetUpperBound(0)
MessageBox.Show(selectedRows(i)("FirstName"))
Next
Для возврата записей, отсортированных в порядке убывания, можно отредактировать строку с методом Select так, как показано ниже.
selectedRows = dtEmployees.Select("LastName ="FirstName DESC")
215
Наконец, указание состояния записи в качестве аргумента метода Select позволяет извлекать записи с определенной версией данных непосредственно в процессе их редактирования. Например, для извлечения всех исходных значений записей даже после их редактирования (но еще до вызова метода AcceptChanges) следует указать значение OriginalRows перечисления DataViewRowState, как показано ниже.
selectedRows = dtEmployees.Select(Nothing, Nothing,
DataViewRowState.OriginalRows)
Для отбора вновь добавленных записей с фамилией Johnson следует указать значение Added перечисления DataViewRowState, как показано ниже.
selectedRows = dtEmployees.Select("LastName =
Nothing, DataViewRowState.Added)
А если нужно отобрать вновь добавленные записи с фамилией Johnson и отсортировать их по имени, то в таком случае следует использовать приведенный ниже код. selectedRows = dtEmployees.Select("LastName =
"FirstName DESC", DataViewRowState.Added)
В табл. 5.3 приведены все возможные варианты состояния записи, которые могут быть представлены членами перечисления DataViewRowState. Упомянутые здесь изменения связаны с последней загрузкой данных или вызовом методаAcceptChanges.
Таблица 5.3. Члены Перечисления DataViewRowState
Член
Описание
Added
Вновь созданные записи
CurrentRows
Все текущие записи (включая новые, измененные или неизмененные записи)
Deleted
Все записи, отмеченные как удаленные
ModifiedCurrent
Текущая версия измененной записи
ModifiedOriginal
Исходная версия измененной записи
None
Нет сведений
OriginalRows
Все исходные записи, включая неизмененные и удаленные, кроме новых записей
Unchanged
Все неизмененные записи
Отношениямеждутаблицами
Поскольку объект DataSet может содержать несколько таблиц, то вполне естественно, что между ними могут существовать какие-то отношения (по крайней мере, если речь идет о реляционных базах данных). В модели ADO.NET для этого предусмотрен объект DataRelation.
Объект DataRelation устанавливает соответствие между полями в двух таблицах, которые имеют родительско-дочерние отношения или связаны первичным и внешним ключами. Классический пример такого отношения существует между таблицами с данными о клиентах и с данными о заказах, где одна запись клиента может быть связана с несколькими записями его заказов. Запись клиента является родительской, а
216
записи заказов — дочерними. Продолжим обсуждение этой темы на примере родительской таблицы Department и дочерней таблицы Employees, которые находятся в одном объекте DataSet.
Объект DataRelation выполняет две разные функции.
Позволяет переходить от одной связанной таблицы к другой и обратно, т.е. при работе с родительской записью можно получить доступ к ее дочерним записям, а при работе с дочерней записью — доступ к ее родительской записи.
Позволяет задавать и поддерживать ссылочную целостность, например каскадные обновления данных в связанных таблицах при выполнении каких-либо изменений в любой из связанных таблиц.
Продолжим работу с упомянутым ранее проектом DataSetCode.
Создайте новую кнопку непосредственно под кнопкой Create DataSet, перетаскивая ее из панели элементов управления.
в окне свойств Properties укажите значение btnCreateRelations для свойства (Name) и значение Create Relations для свойства Text.
Вставьте код, показанный в листинге 5.6.
Листинг 5.6. Код создания и отображения отношений между таблицами
Private Sub btnCreateRelations_Click( _
ByVai sender As System.Object, _
ByVai e As System.EventArgs) _
Handles btnCreateRelations.Click
Dim rel As DataRelation
CreateDataSet()
' Создание отношения между таблицами Departments и Employees.
rel = dsEmployeeInfo.Relations.Add( _
"relDepartmentEmployees", _
dsEmployeelnfo.Tables("Departments").Columns("ID"), _
dsEmployeelnfo.Tables("Employees").Columns("DepartmentID"))
DisplayRelations(dsEmployeelnfo)
End Sub
Private Sub DisplayRelations(ByVai ds As DataSet) Dim rel As DataRelation
' Вывод имен полей созданного отношения.
Me.IstOutput.Items.Add("")
Me.IstOutput.Items.Add("DISPLAY RELATIONS")
For Each rel In ds.Relations
' Вывод имени отношения.
Me.IstOutput.Items.Add("NAME: " & rel.RelationName) ' Вывод имени родительской таблицы и ее поля, ' которое входит в созданное отношение.
Me. IstOutput. Items. Add ("PARENT: " 6^ rel.ParentTable.ToString & " --&
rel.Parentcolumns(0).ColumnName)
Вывод имени дочерней таблицы и ее поля, ' которое входит в созданное отношение.
Me.IstOutput.Items.Add("CHILD: " &
ADO.NET: объект DataSet 217
rel.ChildTable.ToString & " —" 6 _
rel.Childcolumns(0).ColumnName)
Next
Me.IstOutput.Items.Add("")
End Sub
Сначала нужно создать объект DataRelation. Каждый объект DataSet содержит коллекцию отношений, которая доступна как свойство этого объекта Relations. Это свойство имеет тип DataRelationCollection и поддерживает несколько перегруженных версий метода Add. Версия, использованная в листинге 5.6, принимает три аргумента: имя отношения, ссылку на объект DataColumn в родительской таблице, а также ссылку на объект DataColumn в дочерней таблице. Если отношение между таблицами охватывает более одного поля, то следует использовать другую версию метода Add с аргументами-массивами объектов DataColumn.
Подпрограмма DisplayRelations циклически обходит все отношения свойства Relations объекта DataSet и выводит в текстовом поле имя отношения, имя родительской таблицы и ее поле, которое входит в созданное отношение, а также имя дочерней таблицы и ее поле, которое входит в созданное отношение.
НА ЗАМЕТКУ™j"
Для создания более обобщенной версии подпрограммы DisplayRelations можно было бы вставить код циклического обхода всех полей в свойствах-массивах Ра
. rentColumns иChildColumns, анепростоотображатьихпервыеэлементы.
После компоновки проекта DataSetCode и запуска полученного приложения щелкните на кнопке Create Relations, и в текстовом поле будет выведена информация о вновь созданном отношении между таблицами Employees и Departments.
Кроме коллекции Relations объекта DataSet, которая содержит все отношения, определенные между таблицами объекта DataSet, каждый объект DataTable также содержит две коллекции отношений (т.е. два свойства): ParentRelations и ChildRelations, которые содержат отношения между данным объектом DataTable и связанной с ним другой (дочерней или родительской) таблицей.
После создания отношения между таблицами можно организовать доступ к связанным данным в них. Начните с создания кнопки и вставки кода, приведенного в листинге 5.7.
Создайте новую кнопку непосредственно под кнопкой Create Relations, перетаскивая ее из панели элементов управления.
B окне свойств Properties укажите значение btnChildRows для свойства (Name) и значение Child Rows для свойства Text.
Вставьте код, показанный в листинге 5.7.
Листинг 5.7. Код отображения родительских и дочерних данных из связанных таблиц
Private Sub btnChildRows_Click(ByVai sender As System.Object, _
By Vai e As System.EventArgs) Handles btnChildRows.Click
Dim rel As DataRelation
CreateDataSet()
AddData()
' Создание отношения между таблицами Departments и Employees.
rel = dsEmployeelnfо.Relations.Add("relDepartmentEmployees", _ dsEmployeelnfo.Tables("Departments").Columns("ID"), _ dsEmployeelnfo.Tables("Employees").Columns("DepartmentID"))
DisplayChiIdRows(dsEmployeelnfo.Tables("Departments"))
End Sub
Private Sub DisplayChildRows(ByVai dt As DataTable)
Dim rel As DataRelation
Dim relatedRows() As DataRow
Dim row As DataRow
Dim col As DataColumn
Dim i As Integer
Dim rowData As String
Me.IstOutput.Items.Add("")
Me.IstOutput.Items.Add("CHILD ROWS")
For Each row In dt.Rows
For Each rel In dt.ChildRelations
Me.IstOutput.Items.Add( _
dt.TableName & „. „ &
rel.Parentcolumns(0).ColumnName &
" = "& row(rel.Parentcolumns(0).ToString))
r'elatedRows = row.GetChildRows(rel)
' Вывод значений записей.
For i = 0 To relatedRows.GetUpperBound(0)
rowData = ••****£
rel.ChildTable.TableName &
For Each col In rel.ChildTable.Columns rowData = rowData &&
relatedRows(i)(col.ToString)
Next col
Me.IstOutput.Items.Add(rowData)
Next i
Next rel
Next row
End Sub
Подпрограмма btnChildRows_Click для обработки щелчков на кнопке Child Rows сначала создает объект DataSet и объекты DataTable с помощью подпрограммы CreateDataSet (код которой приведен в листинге 5.1), а затем наполняет их данными с помощью подпрограммы AddData (код которой приведен в листинге 5.2). После этого между таблицами Employees и Departments создается отношение с no- мощью кода, который приведен в листинге 5.6. Наконец, для вывода данных из записей в текстовом поле формы вызывается подпрограмма DisplayChildRows, которой в качестве аргумента (родительской таблицы) передается таблица Departments.
Подпрограмма DisplayChildRows содержит трижды вложенный цикл для отображения всех полей из каждой связанной дочерней таблицы (в данном случае только одной) для каждой записи родительской таблицы. При этом каждая запись родительской таблицы передается циклу как аргумент, происходит обход всех отношений, оп-
ADO.NET: объект DataSet _219
ределенных в свойстве Chi IdRelat ions таблицы, отображается имя таблицы, имя поля в родительской таблице, а также значение поля в текущей записи. Затем вызывается метод GetChildRows с текущим отношением в качестве аргумента и возвращается массив объектов DataRow, содержащих дочерние записи. Для каждой записи отображаются все поля с префиксами в виде четырех звездочек и имени дочерней таблицы.
На заметку
Некоторые версии метода GetChildRows принимают дополнительный аргумент, который указывает на используемую версию возвращаемых записей с помощью членов перечисления DataRowVersion из табл. 5.2. Аналогичные методы предусмотрены для извлечения родительской записи (или нескольких родительских записей) по заданной дочерней записи.
Здесь у читателя может возникнуть вопрос: почему упоминается несколько родительских записей? Разве дочерняя запись может иметь несколько родительских записей? Дело в том, что кроме родительских полей, заданных на основе полей с уникальными значениями, могут создаваться родительские поля на основе полей с повторяющимися значениями. Поэтому в таких случаях вместо одной родительской записи (GetParentRow) приходится извлекать несколько родительских записей : (GetParentRows).
L—Ji^. —АД—__.—„..._—: —_"—.. 2™^2 : 2_—2122«а; .2:2 :....—_...... .J
После компоновки проекта DataSetCode и запуска полученного приложения щелкните на кнопке Child ROWS, и в текстовом поле будут выведены значения всех родительских записей из таблицы Employees для каждой родительской записи из таблицы Departments (рис. 5.3).
РИС. 5.3. Результаты отображенияродителъских и дочерних записей из таблиц Employees и Departments
Глава 5
Ограничения
Ограничениями называются правила, которые вводятся для поддержания целостности данных в таблице. В модели ADO.NET применяется два типа ограничений целостности данных: UniqueConstraint и ForeignKeyConstraint. Ограничение UniqueConstraint гарантирует, что все значения в указанных полях будут уникальны в рамках всей таблицы. Ограничение ForeignKeyConstraint определяет связь на основе первичного и внешнего ключа в двух таблицах и выполняемые действия в случае добавления, удаления или изменения родительской записи (т.е. первичного ключа). При нарушении заданных ограничений генерируется исключительная ситуация.
Учтите, что ограничения применяются только тогда, когда свойство EnforceCon- straints объекта DataSet имеет значение True, которое используется по умолчанию.
Хотя ограничения можно создавать непосредственно, довольно часто они создаются косвенно. Фактически в приведенном ранее примере уже было создано несколько ограничений. Объект UniqueConstraint автоматически создается и включается в коллекцию Constraints объекта DataTable, если для свойства Unique объекта DataColumn задано значение True. Кроме того, объекты UniqueConstraint и ForeignKeyConstraint автоматически создаются при создании отношения между двумя таблицами. В таком случае объект UniqueConstraint создается для связанных полей в родительской таблице, а объект ForeignKeyConstraint — для связанных полей в дочерней таблице.
НА ЗАМЕТКУ
Конечно, объект DataRelation можно создать без определения ограничений, но • эффективность такого способа весьма спорна.
Для создания и отображения ограничений таблиц используемого объекта DataSet в данном примере создадим еще одну кнопку и вставим код, приведенный в листинге 5.8.
Создайте новую кнопку непосредственно под кнопкой Child ROWS, перетаскивая ее из панели элементов управления.
В окне свойств Properties укажите значение btnConstraints для свойства (Name) и значение Constraints для свойства Text.
Вставьте код, показанный в листинге 5.8.
Листинг 5.8. Код отображения ограничений
Private Sub btnConstraints_Click(ByVai sender As
System.Object, ByVal e As System.EventArgs) _
Handles btnConstraints.Click
Dim dt As DataTable
Dim rel As DataRelation
CreateDataSet()
' Создание отношения между таблицами Departments и Employees.
rel = dsEmployeelnfo.Relations.Add( _
"relDepartmentEmployees", _
dsEmployeelnfo.Tables("Departments").Columns("ID") , _ dsEmployeelnfo.Tables("Employees").Columns("DepartmentID"))
221
For Each dt In dsEmployeelnfo.Tables Displayconstraints(dt)
Next dt
End Sub
Private Sub Displayconstraints(ByVai dt As DataTable)
Dim i As Integer
Dim cs As Constraint
Dim uCS As UniqueConstraint
Dim fkCS As ForeignKeyConstraint
Dim columns() As DataColumn
Me.IstOutput.Items.Add("")
Me.IstOutput.Items.Add( _ "CONSTRAINTS FOR TABLE:" & dt.TableName)
Me.IstOutput.Items.Add( _
For Each cs In dt.Constraints
Me.IstOutput.Items.Add( _ "Constraint Name: " & cs.ConstraintName)
Me.IstOutput.Items.Add( _ "Type: " & cs.GetType().ToString())
If TypeOf cs Is UniqueConstraint Then uCS = CType(cs, Uniqueconstraint) ' Обработка полей в виде массива. columns = uCS.Columns
’ Вывод имен полей.
For i = 0 То columns.Length - 1
Me.IstOutput.Items.Add( _ "Column Name: " & columns(i).ColumnName)
Next i
Elself TypeOf cs Is ForeignKeyConstraint Then fkCS = CTypefcs, ForeignKeyConstraint)
' Обработка дочерних полей и вывод их имен. columns = fkCS.Columns
For i = 0 To columns.Length - 1
Me.IstOutput.Items.Add( _"Column Name:&
columns(i).ColumnName)
Next i
' Вывод имени связанной родительской таблицы.
Me.IstOutput.Items.Add( _ "Related Table Name: " & fkCS.RelatedTable.TableName)
' Обработка связанных родительских полей и вывод их имен. columns = fkCS.RelatedColumns
For i = 0 То columns.Length - 1
Me.IstOutput.Items.Add( _ "Related Column Name: " & columns(i).ColumnName)
Next i
End If
Me . IstOutput. Items .Add ( "" )
Next cs
End Sub
Подпрограмма btnConstraints_Click обрабатывает щелчки на кнопке Constraints: создает объекты DataSet, DataTable и DataRelation (с помощью кода из прежних листингов), а затем вызывает подпрограмму DisplayConstraints для отображения информации о созданных ограничениях.
Подпрограмма общего типа DisplayConstraints принимает в качестве параметра объект DataTable и отображает информацию об ограничениях указанной таблицы. Для этого выполняется циклический обход всех членов свойства-коллекции Constraints указанной таблицы. Каждое найденное ограничение проверяется, т.е. выясняется, имеет ли оно тип UniqueConstraint или ForeignKeyConstraint. Оба они являются производными классами от класса Constraint, поэтому могут сосуществовать в рамках одной типизированной коллекции. Однако эти объекты все же обладают разным набором свойств, поэтому разработчику потребуется идентифицировать тип ограничения в коллекции и преобразовать его к соответствующему уточненному типу. Для ограничения UniqueConstraint отображаются имена всех (одного или нескольких) полей, определенных в данном ограничении. А для ограничения ForeignKeyConstraint отображается имя связанной родительской таблицы вместе с именами всех (одного или нескольких) связанных полей в этой таблице.
После компоновки проекта DataSetCode и запуска полученного приложения щелкните на кнопке Constraints, и в текстовом поле будет выведена информация о созданных ограничениях (рис. 5.4). Учтите, что все три показанных ограничения (одно для таблицы Employees и два для таблицы Departments) созданы автоматически, так как во время создания отношения между таблицами для свойства Unique объекта DataColumn задано значение True.
РИС. 5.4. Результаты отображения информации о созданных ограничениях для таблиц Employees и Departments
Объект ForeignKeyConstraint имеет три свойства-правила, которые управляют действиями, предпринимаемыми при редактировании данных в связанных таблицах. Например, свойства UpdateRule и DeleteRule определяют действия, выполняемые при обновлении и удалении записей в родительской таблице. Эти свойства могут принимать одно из значений перечисления Rule, члены которого описаны в табл. 5.4.
Таблица 5.4. Члены перечисления Rule
Член перечне- Описание ления
CascadeУдаление или обновление данных в родительской записи также выполняется
для связанных дочерних записей. Это значение используется по умолчанию
NoneУдаление или обновление данных в родительской записи не выполняется для
связанных дочерних записей. Это может привести к появлению дочерних записей, которые ошибочно ссылаются на отсутствующие родительские записи
SetDefault Удаление или обновление данных в родительской записи не выполняетсядля связанных дочерних записей, но для них задается используемое по умолчанию значение, указанное в свойстве DefaultValue
SetNul1Удаление или обновление данных в родительской записи не выполняется для
связанных дочерних записей, но для них задается значение DBNull
Еще одно свойство AcceptRejectRule может принимать значения Cascade (или None) при вызове метода AcceptChanges (или Rejectchanges) для связанных дочерних записей. По умолчанию для него используется значение Cascade, которое указывает на автоматический вызов методов AcceptChanges или Rejectchanges для дочерних записей при вызове этих методов для связанной с ними родительской записи. Если свойство AcceptRejectRule имеет значение None, то вызов одного из этих двух методов для родительской записи никак не повлияет на связанные с ней дочерние записи.
ПрименениеобъектаDataSet
Панель элементов управления Data среды Visual Studio .NET содержит компонент DataSet, который позволяет задавать значения свойств для набора данных с помощью окна свойств Properties вместо создания специального кода. Этот способ работы с компонентом DataSet аналогичен способам работы с компонентами Connection и Command, которые описываются в главе 4, "Модель ADO.NET: провайдеры данных". Задайте конфигурацию объекта DataSet и связанных с ними объектов с теми же определениями, которые используются в приведенных ранее фрагментах кода.
Для этого выполните перечисленные ниже действия.
Создайте другую форму frmDataSetComponent в проекте DataSetCode.
В окне свойств Properties формы укажите значение DataSet для свойства Text и значение frmDataSetComponentдля свойства (Name).
Увеличьте размер формы frmDataSetComponent.
Глава 5
Создайте в форме поле со списком, перетаскивая его из панели элементов управления.
В окне свойств Properties поля со списком укажите значение IstOutput для свойства (Name).
Увеличьте размер поля со списком IstOutput так, чтобы оно покрывало до 80% площади формы.
Из панели элементов управления Data перетащите в форму компонент DataSet. В появившемся на экране диалоговом окне выберите переключатель Untyped dataset (Нетипизированный набор данных) и щелкните на кнопке ОК. Этот компонент невидим во время выполнения приложения, поэтому в режиме создания приложения он будет находиться под формой.
В окне свойств Properties этого компонента укажите значение dsEmployee- Infо для свойства (Name).
В окне свойств Properties этого компонента выберите свойство Tables и щелкните на кнопке с многоточием в правой части этого свойства для отображения диалогового окна Tables Collection Editor (Редактор коллекции таблиц).
Щелкните на кнопке Add для отображения свойств первой таблицы создаваемого набора данных.
В панели свойств Tablel Properties укажите значение Employees для свойства TableName, как показано на рис. 5.5.
Рис. 5.5. Диалоговое окно Tables Collection Editor после указания таблицы Employees
В панели свойств Employees Properties выберите свойство Columns и щелкните на кнопке с многоточием в правой части этого свойства для отображения диалогового окна Columns Collection Editor (Редактор коллекции полей).
225
Щелкните на кнопке Add для отображения свойств первого ПОЛЯ таблицы Employees.
В панели свойств Columnl Properties укажите значение FirstName для свойства ColumnName первого поля.
Щелкните на кнопке Add для отображения свойств второго поля таблицы Employees.
В панели свойств Columnl Properties укажите значение LastName для свойства ColumnName второго поля.
Щелкните на кнопке Add для отображения свойств третьего поля таблицы Employees.
В панели свойств Columnl Properties укажите значение Department ID для свойства ColumnName и значение System. Int32 для свойства DataType третьего поля.
После выполнения этих действий диалоговое окно Columns Collection Editor будет выглядеть так, как показано на рис. 5.6.
Рис. 5.6. Диалоговое окно Columns Collection Editor со свойствами полей таблицы Employees
Щелкните на кнопке Close в диалоговом окне Columns Collection Editor, чтобы вернуться в диалоговое окно Tables Collection Editor для включения в набор данных dsEmployeelnfо еще одной таблицы Departments.
Щелкните на кнопке Add для отображения свойств второй таблицы набора данных dsEmployeelnf о.
В панели свойств Tablel Properties укажите значение Departments для свойства TableName второй таблицы.
Укажите значение 5 для свойства Minimumcapacity второй таблицы.
226
В панели свойств Departments Properties укажите свойство Columns и щелкните на кнопке с многоточием в правой части этого свойства для отображения диалогового окна Columns Collection Editor (Редактор коллекции полей).
Щелкните на кнопке Add для отображения свойств первого поля таблицы Departments.
Укажите значение ID для свойства ColumnName и значение System. Int32 для свойства DataType первого поля.
В панели свойств ID Properties укажите значение True для свойства Readonly, значение True для свойства Unique и значение True для свойстваAutoIncrement первого поля.
Щелкните на кнопке Add для отображения свойств второго поля таблицы Departments.
В панели свойств Columnl Properties укажите значение DepartmentName для свойства ColumnName.
В панели свойств DepartmentName Properties укажите значение True для свойства Unique и значение False для свойстваAllowDBNull первого поля.
Щелкните на кнопке Close в диалоговом окне Columns Collection Editor для возвращения в диалоговое окно Tables Collection Editor, а затем щелкните еще раз на кнопке Close для закрытия диалогового окна Tables Collection Editor.
Итак, вы создали набор данных dsEmployeesInfo с таблицами Employees и Departments, указывая для свойств компонентов те же значения, которые использовались в коде из листинга5.1.
Продолжим работу с этими компонентами и определим отношения между таблицами набора данных dsEmployeesInfo.
В окне свойств Properties компонента dsEmployeesInfo выберите свойство Relations, а затем щелкните на кнопке с изображением многоточия, чтобы отобразить на экране диалоговое окно Relations Collection Editor (Редактор коллекцииотношений).
Щелкните на кнопке Add для отображения в диалоговом окне Relation свойств первого отношения между таблицами набора данных dsEmployeesInfo.
В текстовом поле Name диалогового окна Relation укажите значение relDe- partmentEmployees свойства Name.
В текстовом поле ParentTable диалогового окна Relation выберите таблицу Departments свойства ParentTable.
В текстовом поле ChildTable диалогового окна Relation выберите таблицу Employees свойства ChildTable.
В поле со списком столбца Key Columns раздела Columns диалогового окна Relation выберите поле ID, в результате этого значение ID будет присвоено свойству Parentcolumns объекта relDepartmentEmployees.
Аналогично в поле со списком столбца Foreign Key Columns раздела Columns диалогового окна Relation выберите поле DepartmentID, в результате значе-
227
ние DepartmentID будет присвоено свойству ChildColumns объекта relDe- partmentEmployees.
Воспользуйтесь предлагаемыми по умолчанию значениями в списках Update rule (Правило обновления), Delete rule (Правило удаления) и Accept/Reject rule (Правило подтверждения / отказа), которые соответствуют свойствам UpdateRule, DeleteRule и AcceptRej ectRule.
Щелкните на кнопке OK для закрытия диалогового окна Relation, а затем на кнопке Close для закрытия диалогового окна Relations Collection Editor.
Теперь остается указать значение свойства PrimaryKey для каждой таблицы.
В окне свойств Properties компонента dsEmployeesInfo выберите свойство Tables, а затем щелкните на кнопке с изображением многоточия, чтобы отобразить на экране диалоговое окно Tables Collection Editor.
В списке членов набора данных Members выберите таблицу Employees.
В панели свойств Employees Properties выберите свойство PrimaryKey и щелкните на кнопке с изображением стрелки в правой части этого свойства для развертывания списка полей.
Установите флажки для поля (или нескольких полей) первичного ключа из списка доступных полей. Если первичный ключ охватывает несколько полей, то выберите их в правильном порядке. В данном примере выберите сначала поле FirstName, а затем LastName, как показано на рис. 5.7.
РИС. 5.7. Выбор нескольких полей для определения nep- вичного ключа
Нажмите клавишу <Enter> для утверждения созданного первичного ключа.
В списке членов набора данных Members выберите таблицу Departments.
В панели свойств Departments Properties выберите свойство PrimaryKey и щелкните на кнопке с изображением стрелки в правой части этого свойства для развертыванияспискаполей.
Установите флажок для поля DepartmentName первичного ключа из списка доступных полей.
Щелкните на кнопке Close для закрытия диалогового окна Tables Collection Editor.
Чтобы убедиться в идентичности результатов, полученных в режиме создания компонентов, по сравнению с созданным ранее кодом скопируйте и вставьте некоторые подпрограммы из формы frmDataSets в форму frmDataSetComponent, а затем запустите полученное приложение, выполнив перечисленные ниже действия.
Выберите и скопируйте подпрограмму AddData из формы frmDataSets в форму frmDataSetComponent..
Повторите п. 1 для подпрограмм DisplayDataSet и DisplayChildRows.
Включите следующий код в обработчик события загрузки формы frmDataSet- Component_Load в форме frmDataSetComponent:
Private Sub frmDataSetComponent_Load(ByVai sender As
System.Object, ByVal e As System.EventArgs) _ Handles MyBase.Load
AddData()
DisplayDataSet()
DisplayChildRows(dsEmployeelnfo.Tables("Departments")).
End Sub
Щелкните правой кнопкой мыши на проекте DataSetCode в окне Solution Explorer и выберите в контекстном меню команду Properties, чтобы открыть диалоговое окно DataSetCode Property Pages.
Выберите раздел General в папке Common Properties в правой части диалогового окна DataSetCode Property Pages, а затем выберите форму frmDataSetCom- ponent в поле Startup object.
После запуска созданного приложения в поле со списком формы frmDataSetCom- ponent будут отображены все данные набора dsEmployeelnfo и дочерние записи из таблицы Departments (рис. 5.8).
| НА ЗАМЕТКУ~:
Анализируя код, сгенерированный при редактировании свойств в режиме создания компонентов, можно убедиться в том, что он очень похож на созданный вручную код, описанный в предыдущих разделах. Для просмотра автоматически полученного кода нужно выбрать форму в окне Solution Explorer, щелкнуть правой кнопкой мыши, выбрать команду меню View Code в контекстном меню, а затем раскрыть раздел Windows Form Designer generated code.
229
РИС. 5.8. Отображение всех данных набора dsEmployeelnf и дочерних записей с помощью параметров объекта Qat^Set, заданных в режиме создания компонентов
Резюме
В этой главе подробно описываются объекты и концепции, которые (вместе с материалом из главы 4, "Модель ADO.NET: провайдеры данных") являются ключевыми для методов создания приложений баз данных на основе ADO.NET и Visual Basic .NET. Здесь показано, как объект DataSet и связанные с ним объекты DataTable, DataRelation, DataRow и DataColumn предоставляют разработчикам гибкую и, мощную модель программирования для обработки данных при отключении от физического источника данных. В главе 6, "ADO.NET: объект DataAdapter", демонстрируются способы использования объекта DataAdapter для наполнения данными объекта DataSet и автоматического обновления источника данных при изменении данных в объекте DataSet.
Вопросы и ответы
Насколько я понял, источник данных можно использовать непосредственно (с помощью команд управления данными) или косвенно (в неподключенном состоянии). В каких случаях предпочтительнее использовать каждый из этих способов?
Метод на основе объекта DataSet (в неподключенном состоянии) по сравнению с непосредственным применением команд управления данными обладает несколькими преимуществами. Он предлагает более простой и единообразный способ перемещения данных между разными уровнями и местоположениями в распределенной базе данных, а также между разными приложениями баз данных благодаря встроенной поддержке языка XML. В нем предусмотрен механизм кэширования данных, что позволяет сортировать, фильтровать и искать данные без доступа к источнику данных. Наконец, он позволяет извлекать данные из нескольких таблиц или даже нескольких разных источников данных, а также манипулировать ими индивидуально или совместно на основе заданных между ними отношений.
Прямой способ доступа к источнику данных с помощью объекта Command также обладает определенными преимуществами. Некоторые операции, например изменение структуры базы данных, можно выполнить только с помощью прямого доступа. При прямом доступе даже стандартные команды SQL или хранимые процедуры могут быть выполнены быстрее и эффективнее, что позволяет добиться более высокой производительности и масштабируемости. Кроме того, этот способ позволяет сократить объем оперативной памяти для объекта DataSet, особенно при отсутствии насущной необходимости в кэшировании данных, например при создании Web- страницы ИЛИ заполнении данными поля со списком.
Итак, когда же предпочтительнее использовать прямой доступ к базе данных вместо объекта DataSet? Прежде всего в тех случаях, когда операция может быть выполнена только с помощью объекта Command. Это относится к вызовам хранимых процедур, которые выполняют манипуляции с данными и возвращают только одно значение и/или значения параметров, а также к DDL-командам изменения структуры базы данных. Кроме того, не рекомендуется использовать объект DataSet, если данные используются только для чтения, используются недолго, а потому не оправданны их загрузка и хранение в оперативной памяти, либо используются сервером и их не нужно передавать на другие уровни приложения или компьютеры. В большинстве других случаев предпочтительнее обращаться к объекту DataSet.
ГЛАВАГЛАВАADO.NET: объектADO.NET: объектDataAdapterDataAdapterВ ЭТОЙ ГЛАВЕВ ЭТОЙ ГЛАВЕ****Передача данных из источника данных в объект DataSetОбновление источника данныхРезюмеВопросы и ответыПередача данных из источника данных в объект DataSetОбновление источника данныхРезюмеВопросы и ответы
ГЛАВА
ГЛАВА
ADO.NET: объект
ADO.NET: объект
DataAdapter
DataAdapter
В ЭТОЙ ГЛАВЕ
В ЭТОЙ ГЛАВЕ
*
*
*
*
Передача данных из источника данных в объект DataSet
Обновление источника данных
Резюме
Вопросы и ответы
Передача данных из источника данных в объект DataSet
Обновление источника данных
Резюме
Вопросы и ответы
При изучении объектов DataSet, DataTable, DataRelation, DataRow, DataColumn, DataRelation и Constraint в главе 5, “ADO.NET: объект DataSet", у читателя возможно не раз возникало желание воскликнуть: "А где же, собственно, сама база данных?”. Действительно, до сих пор ничего не говорилось о способе загрузки данных в объект DataSet из базы данных или другого источника, а также о последующем обновлении источника данных в соответствии с изменениями данных в объекте DataSet. Именно эту задачу и выполняет объект DataAdapter.
Объект DataAdapter является промежуточным звеном между объектом DataSet и физическим источником данных. Он играет роль моста между двумя мирами ADO.NET: подключенным к источнику данных миром провайдеров данных .NET (которые рассматриваются в главе 4, "Модель ADO.NET: провайдеры данных") и неподключенным к источнику данных миром объектов DataSet (которые рассматриваются в главе 5, "ADO.NET: объект DataSet").
В структуре провайдеров данных .NET объект DataAdapter занимает центральное место. Каждый такой провайдер должен иметь собственную реализацию объекта DataAdapter (например, SqlDataAdapter, OledbDataAdapter, OdbcDataAdapter и т.д.). Причина, по которой этот объект рассматривается только сейчас, заключается в том, что без понимания основных принципов работы с объектом DataSet и подчиненными объектами нельзя понять основные концепции работы с объектом DataAdapter. Теперь можно приступить к описанию объекта DataAdapter с учетом всех приведенных ранее сведений.
На рис. 6.1 показана схема использования объекта DataAdapter. Основным назначением этого объекта является управление процессом передачи данных от источника данных к объекту DataSet и от объекта DataSet к источнику данных. Это выполняется с помощью небольшого набора свойств и методов, которые описываются далее.
DataSetDataSetDataAdapter.FillDataAdapter.FillUpdateCommandUpdateCommandDeleteCommandDeleteCommandSelectCommandSelectCommandInsertCommandInsertCommandОтображения полей и таблицОтображения полей и таблицDataAdapterDataAdapterDataAdapter. FillDataAdapter. FillБаза данныхБаза данныхРИС. 6.1. Схема работы объекта DataAdapter как моста между источником данных и объектом DataSetРИС. 6.1. Схема работы объекта DataAdapter как моста между источником данных и объектом DataSet
DataSet
DataSet
DataAdapter.Fill
DataAdapter.Fill
UpdateCommand
UpdateCommand
DeleteCommand
DeleteCommand
SelectCommand
SelectCommand
InsertCommand
InsertCommand
Отображения полей и таблиц
Отображения полей и таблиц
DataAdapter
DataAdapter
DataAdapter. Fill
DataAdapter. Fill
База данных
База данных
РИС. 6.1. Схема работы объекта DataAdapter как моста между источником данных и объектом DataSet
РИС. 6.1. Схема работы объекта DataAdapter как моста между источником данных и объектом DataSet
233
Основными методами объекта DataAdapter являются Fill и Update. Метод Fill наполняет объект DataAdapter данными, полученными от источника данных, а метод Update обновляет источник данных в соответствии с изменениями данных в таблицах объекта DataSet.
Объект DataAdapter содержит набор из четырех свойств (SelectCommand, InsertCommand, UpdateCommand и De let eCommand), которые являются объектами ADO.NET типа Command, конфигурированными для выполнения одноименных операций. Например, объект SelectCommand выполняется при вызове метода Fill объекта DataAdapter, а остальные объекты выполняются для каждой измененной записи при вызове метода Update объекта DataAdapter. Разработчики базы данных обладают полным контролем над этими командами, которые позволяют настраивать команды, используемые для операций вставки, обновления и удаления.
Наконец, коллекция TableMappings объектов DataTableMapping позволяет указать соответствие между таблицей источника данных и объектом DataTable.
Передача данных из источника данных в объект DataSet
Для использования объекта DataAdapter требуется, как минимум, подключение к базе данных и команда Select. Для этого можно использовать объекты Connection и Command, т.е. создать их, конфигурировать и присвоить их свойствам Connection и SelectCommand объекта DataAdapter. Однако гораздо удобнее использовать для этого конструктор объекта DataAdapter с двумя строковыми параметрами: один для команды SELECT, а второй для строки подключения, как показано ниже.
Dim da As SqlDataAdapter = New SqlDataAdapter( _
"select * from tblDepartment"
"server=localhost;uid=sa;database=Novelty")
НА ЗАМЕТКУ
i Учтите, что указанная в объекте SelectCommand команда SQL может иметь парамет- ; | ры. В главе 4, "Модель ADO.NET: провайдеры данных", приводятся основныезведе- ния об определении этих параметров для разных провайдеров данных .NET.
Попробуем теперь вызвать метод Fill для извлечения данных из базы данных Novelty и загрузки их в объект DataSet. Для этого вернитесь к проекту DataSetCode из главы 5, "ADO.NET: объект DataSet”, и выполните перечисленные ниже действия.
Щелкните правой кнопкой мыши на проекте DataSetCode в окне Solution Explorer и выберите в контекстном меню команду Properties, чтобы открыть диалоговое окно DataSetCode Property Pages.
Выберите раздел General в папке Common Properties в правой части диалогового окна DataSetCode Property Pages, а затем выберите форму frmDataSets в поле Startup object.
Откройте форму frmDataSets в окне конструктора формы.
Создайте новую кнопку непосредственно под кнопкой Constraints, перетаскивая ее из панели элементов управления.
В окне свойств Properties укажите значение btnDataAdapterFill для свойства (Name) и значение DataAdapter Fill для свойства Text.
Для использования провайдера данных SqlClient нужно импортировать его пространство имен с помощью команды, отмеченной полужирным начертанием в следующем фрагменте кода:
Imports System
Imports System.Data
Imports Systern.Data.SqlClient
Вставьте код, показанный в листинге 6.1.
Листинг 6.1. Использование провайдера данных SqlClient для вставки
данных В Набор данных dsEmployeelhf о
Private Sub btnDataAdapterFill_Click(ByVai sender As
System.Object, ByVal e As System.EventArgs) _
Handles btnDataAdapterFill.Click
ReadData()
End Sub
Private Sub ReadData()
Dim rows As Integer
Dim "daDepartments As SglDataAdapter = New SqlDataAdapter( _
"select from tblDepartment", _
"server=localhost;uid=sa;database=novelty")
dsEmployeelnfo = New DataSet()
rows = daDepartments.Fill(dsEmployeelnfo, "Departments")
DisplayDataSet(dsEmployeelnfo)
End Sub
После создания объекта daDepartments с двумя аргументами, которые содержат команду Select и строку подключения, вызывается метод Fill для наполнения данными таблицы Departments набора данных dsEmployeelnfo. Метод Fill также возвращает количество записей, которые включены (или обновлены) в набор данных dsEmployeelnfo. При выполнении метода Fill провайдером данных неявно выполняются перечисленные ниже действия.
Для объекта SelectCommand открывается подключение к источнику данных, если оно еще не открыто.
Выполняется команда, указанная в свойстве CommandText объекта SelectCommand (вместе с параметрами, если таковые имеются).
235
Подключение к источнику данных закрывается, если оно было открыто объектом DataReader, в противном случае оно остается открытым.
НА ЗАМЕТКУ ——
S При выполнении одной команды по отношению к источнику данных обычно проще и эффективнее создавать объекты Command и Connection неявно при создании объек- t та DataAdapter. Однако при выполнении нескольких команд по отношению к одному | источнику данных эффективнее создать объект Connection явно и затем присвоить j его объекту DataAdapter. Это позволяет поддерживать подключение постоянно от- I крытым без часто повторяющихся операций его открытия и закрытия, что снижает | производительность.
Эквивалентный код представлен ниже.
Private Sub ReadData()
Dim rows As Integer
Dim daDepartments As SqlDataAdapter()
Dim conn As New SqlConnection( _
"server=localhost;uid=sa;database=novelty")
Dim cmdSelect As New SqlCommand (
"select * from tblDepartment")
dsEmployeeInfo = New DataSet()
cmdSelect.Connection = conn
daDepartments.SelectCommand = cmdSelect
' Открытие подключения перед выполнением команд.
conn.Open()
rows = daDepartments.Fill(dsEmployeelnfo, "Departments")
'1 Выполнение операций с базой данных.
; DisplayDataSet(dsEmployeelnfo)
' Закрытие подключения после выполнения всех команд.
. conn.Close()
End Sub
Конечно, для эффективного использования явно созданных объектов conn и cmdSelect | желательно, чтобы количество операций с базой данных было достаточно большим. .
Методу Fill передается ссылка на набор данных dsEmployeelnf о и имя таблицы Departments, в которую вставляются данные. Вместо имени таблицы можно было бы передать ссылку на объект DataTable. Еще один вариант указания параметров основан на передаче только ссылки на объект DataSet, а метод Fill в таком случае по умолчанию загрузит данные в объект DataTable по имени Table.
НА ЗАМЕТКУ |=
Обычно для вставки данных в объект DataTable используется объект DataSet, однако существует перегруженная версия метода Fill для загрузки данных в отдельный объект DataTable.
236
Для загрузки данных во вторую таблицу можно создать второй объект DataAdapter с другой командой Select. В данном примере для загрузки данных в таблицы Department и Employees из соответствующих таблиц базы данных нужно использовать приведенный ниже код для подпрограммы ReadData() вместо кода из листинга6.1.
Private Sub ReadData()
Dim rows As Integer
Dim daDepartments As SqlDataAdapter = New SqlDataAdapter ( _
"select * from tblDepartment", _
"server=localhost;uid=sa;database=novelty")
Dim daEmployees As SqlDataAdapter = New SqlDataAdapter( _ "select * from tblEmployee", _ "server=localhost;uid=sa;database=novelty")
dsEmployeelnfo = New DataSet()
rows = da Departments.Fill(dsEmployeelnfo, "Departments") rows = daEmployees.Fill(dsEmployeelnfo, "Employees")
DisplayDataSet(dsEmployeelnfo)
End Sub
Скомпонуйте проект DataSetCode, щелкните на кнопке DataAdapter Fill, и в поле со списком, как и прежде, будет отображена информация о содержании набора данных dsEmployeelnfo, но теперь она извлекается из базы данных под управлением SQL Server, а не генерируется локально кодом приложения.
Для установления родительско-дочерних связей между записями в этих таблицах можно создать объект DataRelation, который служит отношением между ними.
НА ЗАМЕТКУ ;;
Иногда предпочтительнее вставлять в объект DataSet результаты объединения двух таблиц. В таком случае потребуется только один объект DataTable и не понадобится создавать отношение между двумя исходными таблицами. Однако использовать две отдельные таблицы можно более гибко, особенно при обновлении источника данных, потому что обновление объединенной таблицы связано с некоторыми ограничения- j ми, в то время как для обновления независимых таблиц никаких ограничений не су- | ществует.
Совсем необязательно использовать разные объекты DataAdapter для таблиц одного набора данных. Иногда для них можно использовать один объект DataAdapter, только изменяя параметр с текстом команды SQL. Этот способ прекрасно подходит для многочисленных вызовов метода Fill, так как программирование всех отдельных операций создания и изменения команд обновления (вставки, обновления и удаления) каждого объектаDataTable в соответствии с изменениями источника данных требует много времени и усилий.
Итак, для использования одного объекта DataAdapter предыдущий код следует заменить приведенным ниже.
объект DataAdapter
237
Private Sub
Dim rows As Integer
Dim da As SqlDataAdapter = New "select * from
"server=localhost;uid=sa;database=novelty")
= New
rows ="Employees")
Изменение текста команды SQL.
= "select * from
rows =
DisplayDataSet(dsEmployeelnfo)
End Sub
| НА ЗАМЕТКУ~*T
Наиболее эффективный способ загрузки данных из двух таблиц в объект
I основан наобъектакоторый вызывает хранимую j
процедуру, возвращающую результирующий набор записей, или выполняет пакет
! команд SQL. В таком случае для извлечения данных требуется выполнить только од- i ) но обращение к серверу баз данных вместо многократного обращения, как в преды- | дущих примерах кода. Хотя этот способ упрощает и ускоряет извлечение данных из i нескольких таблиц, при этом значительно усложняется обновление данных при их
изменении в таблицах объекта DataSet, если между ними заданоЭтот
способ обновления рассматривается более подробно в бизнес-ситуациидалее в
j главе.
В листинге 6.2 демонстрируется еще один способ использования одного объекта DataAdapter для выполнения нескольких операций вставки данных в один объект DataTable.
Листинг 6.2. Использование одного объекта DataAdapter для нескольких операций вставки данных в один объект DataTable
Private Sub
DimAs SqlDataAdapterNew
"select * from tblEmployee where= 1",
"server=localhost;uid=sa;database=novelty")
= New
daEmployees.Fill(dsEmployeelnfo, "Employees")
Изменение текстаSQL.
=
"select * from tblEmployee where DepartmentID = 3"
daEmployees.Fill(dsEmployeelnfo, "Employees")
238
Обратите внимание, что в листинге 6.2 возвращаемое значение метода Fill уже не сохраняется в локальной переменной rows. Совсем необязательно сохранять возвращаемое значение, если только в дальнейшем не предполагается проверять его или использовать каким-то другим способом. Предлагаемый в листинге 6.2 код можно было бы расширить несколькими командами Select для обновления объекта DataTable наиболее свежими данными (например, измененными другими пользователями) из источника данных.
НА ЗАМЕТКУ
Существующие в объекте DataTable значения обновляются только при вызове соответствующего метода Fill, если для объекта DataTable задан первичный ключ. По умолчанию метод Fill вставляет в объект DataTable информацию о структуре данных и значения данных в записях без указания каких-либо ограничений, которые могут быть заданы в источнике данных. Для правильной установки значения свойства PrimaryKey (чтобы корректно выполнялись операции обновления данных и метод
■ Fill)еще до вызова метода Fill нужно выполнить одно из следующих действий:
вызвать методFillschema объектаDataAdapter;
указать значение AddWithKey для свойства MissingSchema объекта DataAdapter;
явно указать значение свойства PrimaryKey для соответствующих полей, если они известны в режиме создания приложения.
Обновление источника данных
Обычно после внесени'я всех необходимых изменений в таблицы набора данных DataSet потребуется сохранить эти изменения в источнике данных. Для этого нужно вызвать метод Update объекта DataAdapter, который анализирует изменения в указанной таблице набора данных (или сразу во всех таблицах, если ни одна из них не указана явно). Для каждой измененной записи по отношению к источнику данных выполняется команда вставки, обновления или удаления с помощью соответствующего объекта InsertCornmand, UpdateCommand или DeleteConunand.
НА ЗАМЕТКУ
Возможность прямого указания специализированных пользовательских команд SQL или хранимых процедур в целях их автоматического применения для обновления источника данных при изменении данных в объекте DataSet является наиболее значительным усовершенствованием способа работы с данными в модели ADO.NET по сравнению с моделью ADO 2.X. В модели ADO.NET предусмотрено не только управление способом обновления пакета записей, но и использование хранимых процедур для выполнения этой задачи. Помимо указания команд SQL для операций обновления, применение хранимых процедур позволяет существенно повысить производительность и использовать специализированную бизнес-логику. Далее приводится краткий пример использования этих преимуществ. Кроме того, в отличие от модели ADO 2.Х, которая может применяться только к SQL-совместимым источникам данных, механизм пакетного обновления модели ADO.NET может работать даже с источниками данных, не совместимыми с SQL.
239
Каждая измененная запись обновляется отдельно, а не как часть транзакции или пакетной операции. Причем порядок обновления записей определяется порядком их расположения в объекте DataTable.
Для явного управления порядком выполнения операций для заданной таблицы можно использовать методы GetChanges или Select, которые доступны на уровне либо объекта DataSet, либо объекта DataTable. Эти методы используются для извлечения отдельных наборов записей с разными состояниями записей.
Допустим, необходимо обновить базу данных Novelty данными из объекта dsEmployees с помощью объекта daDepartments. Причем сначала требуется выполнить все вставки, затем все обновления, а потом все удаления. Это можно сделать, трижды вызывая метод GetChanges с указанием соответствующих разных состояний записей. После каждого вызова метода GetChanges вызывается метод Update объекта DataAdapter с передачей объекта DataTable, возвращенного методом GetChanges. dt =
' Внести изменения для каждого типа состояния. dtChanged = dt.GetChanges(DataRowState.Added) daDepartments.Update(dtChanged)
dtChanged = dt.GetChanges(DataRowState.Modified)
daDepartments.Update(dtChanged)
dtChanged = dt.GetChanges(DataRowState.Deleted) daDepartments.Update(dtChanged)
Этот код можно записать более компактно.
dt = dsEmployeelnfo.Tables("Departments")
' Внести изменения для каждого типа состояния.
daDepartments.Update(dt.GetChanges(DataRowState.Added)) daDepartments.Update(dt.GetChanges(DataRowState.Modified)) daDepartments.Update(dt.GetChanges(DataRowState.Deleted))
Аналогичный результат можно получить с помощью метода Select.
dt = dsEmployeelnfo.Tables("Departments")
' Внести изменения для каждого типа состояния. daDepartments.Update(Nothing, Nothing,
dt.GetChanges(DataViewRowState.Added)) daDepartments.Update(Nothing, Nothing,
dt.GetChanges(DataViewRowState.ModifiedCurrent)) daDepartments.Update(Nothing, Nothing,
dt.GetChanges(DataViewRowState.Deleted))
Преимущество использования метода Select вместо GetChanges заключается в том, что он может выполнять фильтрацию и сортировку.
Здесь следует напомнить о разнице между операциями удаления записи с помощью методов Remove и Delete, о которой сообщалось в главе 5, “ADO.NET: объект DataSet". При использовании метода Remove запись навсегда удаляется из коллекции, а при удалении с помощью метода Delete — только отмечается как удаленная. При обновлении источника данных данными из объекта DataTable с помощью объекта DataAdapter нужно использовать метод Delete, а не Remove. Когда объект DataAdapter встречает помеченную для удаления запись, он автоматически выпол-
240
няет команду DeleteCommandдля базы данных, чтобы осуществить синхронизацию с объектом DataTable. Если вместо метода Delete использовать Remove, то объект DataAdapter не обнаружит уже удаленную запись из объекта DataTable и не сможет удалить ее из источника данных.
Указание команд обновления
Объект DataAdapter не создает автоматически команды INSERT, UPDATE и DELETE для обновления источника данных в соответствии с изменениями данных в объекте DataSet. Если при вызове команды метода Update не была указана команда INSERT, UPDATE или DELETE, то генерируется исключительная ситуация. Эти команды можно указать одним их следующих способов:
использовать объект CommandBuilder для автоматической генерации команд во время выполнения приложения;
явно запрограммировать эти команды;
использовать компонент DataAdapter Design-Time Component и мастер конфигурирования объекта DataAdapter Configuration Wizard.
Использование объекта CommandBuilder
Это самый простой способ, но он связан с существенными ограничениями. Он аналогичен применению метода BatchUpdate в модели ADO 2.X. Когда объект CommandBuilder связывается с соответствующим ему объектом DataAdapter, он автоматически может задавать значения свойств Insertcommand, UpdateCommand и DeleteCommand для данного объекта DataAdapter. А если эти свойства имеют ненулевые значения (т.е. для них не задано значение Nothing), то объект-команда уже существует и объект CommandBuilder не переопределяет ее.
НА ЗАМЕТКУ
Как и следовало ожидать, для каждого типа провайдера данных .NET используется специализированный объект: SqlDataAdapterOledbDataAdapteИЛИ OdbcDataAdapter. ;
Для автоматической генерации команд нужно указать объект SelectCommand адаптера данных DataAdapter. Объект CommandBuilder использует схему таблицы, полученную с помощью метода Select объекта SelectCommand для генерации соответствующей команды вставки, обновления или удаления. Учтите, что поля, возвращаемые объектом SelectCommand, должны содержать первичный ключ или поле с уникальными значениями.
Изменение текста команды SELECT после автоматической генерации команд обновления может привести к возникновению исключительных ситуаций при одновременном выполнении этой команды обновления. Если в исходной команде SELECT, на основании которой автоматически генерируются команды обновления данных, содержались поля, которых уже нет в генерированной команде, то при выполнении одной из команд обновления с помощью метода Update объекта DataAdapter может произойти попытка обращения к уже несуществующим полям и возникновение исключительной ситуации. Для исключения этой проблемы следует вызвать метод
RefreshSchema объекта CommandBuilder после изменения свойства SelectCommand объекта DataAdapter или после изменения текста (объект CommandText) данной команды.
НА ЗАМЕТКУ —
Даже после того как объект CommandBuilder автоматически сгенерировал команды i вставки, обновления или удаления, соответствующие им свойства объекта DataAdapter не изменяются. Объект CommandBuilder скрывает сгенерированные команды, и доступ к ним можно получить с помощью методовЗеtInsertCommand, GetUpdate-
Command иGetDeleteCommand.
: ; _
Хотя конструктор команд CommandBuilder прост в применении, нужно учитывать его ограничения. Основное ограничение связано с тем, что нельзя проконтролировать выполняемые им действия, т.е. задать его конфигурацию. Он просто генерирует команды обновления на основе заданной команды SELECT без каких-либо параметров. Кроме того, он предназначен для выполнения команд по отношению к одной таблице базы данных. Иначе говоря, объект DataTable с результатами объединения таблиц не может использоваться объектом CommandBuilder. Более того, команды генерируются для одной таблицы без учета ее возможных связей с другими таблицами базы данных, что может привести к нарушению целостности данных при наличии между таблицами родительско-дочерних отношений.
Еще одно ограничение объекта CommandBuilder связано с тем, что он не способен генерировать команды обновления, если имя таблицы или поля содержит специальные символы, например пробел, точку или другие не буквенно-цифровые символы, даже если это имя заключено в кавычки. Однако он может работать с полностью квалифицированными именами таблиц в формате база_даиных.владелец. таблица.
Чтобы извлекать и сохранять данные в базе данных с помощью объекта DataAdapter, создадим в проекте DataSetCode еще одну форму. Для этого выполните перечисленные ниже действия.
Создайте новую кнопку непосредственно под кнопкой DataAdapter Fill, перетаскивая ее из панели элементов управления. В окне свойств Properties укажите значение btnDataAdapterUpdate для свойства (Name) и значение DataAdapter Updates для свойства Text.
Создайте в проекте DataSetCode новую форму.
В окне свойств Properties укажите значение frmUpdates для свойства (Name) и значение DataAdapter Updates для свойства Text.
Увеличьте размер формы f rmUpdates.
Создайте в правой части формы сетку данных DataGrid, перетаскивая ее из панели элементов управления.
В окне Properties укажите значение grdDataGrid для свойства (Name) сетки данных.
Увеличьте размер сетки данных, чтобы она занимала до 80% всей площади формы.
242
В верхнем левом углу формы создайте кнопку, перетаскивая ее из панели элементов управления.
В окне Properties укажите значение btnLoad для свойства (Name) и значение Load для свойства Text этой кнопки.
Создайте новую кнопку непосредственно под кнопкой btnLoad, перетаскивая ее из панели элементов управления.
11.В окне Properties укажите значение btnLoad для свойства (Name), значение Load для свойства Text и значение False для свойства Enabled этой кнопки.
В верхней части файла с кодом формы frmUpdates введите следующий код:
Imports System
Imports System.Data
Imports System.Data.SqlClient
Затем в код формы frmUpdates введите код из листинга 6.3.
Листинг 6.3. Применение объекта SqlCommandBui1d для автоматической генерации команд обновления
Private dsEmployeelnfo As DataSet
Private daEmployees As SqlDataAdapter
Private conn As New SqlConnection( _
"server=localhost;uid=sa;pwd=;database=novelty")
' Применение объекта SqiCommandBuiider
' для автоматической генерации команд обновления.
Private cbEmployees As SqiCommandBuiider
Private Sub btnLoad_Click(ByVai sender As System.Object, _
ByVai e As System.EventArgs) Handles btnLoad.Click
dsEmployeelnfo = New DataSet()
LoadCommandBuilder()
' Конфигурирование объекта-сетки DataGrid.
Me. grdDataGrid.PreferredColumnWidth = 110
Me.grdDataGrid.AllowSorting = True
' Вставка данных в объект DataSet.
daEmployees.Fill(dsEmployeelnfo, "Employees")
' Присвоение объекта DataSet объекту DataGrid. =
■dsEmployeelnfo.Tables("Employees")
Me.btnUpdate.Enabled = True
End Sub
Private Sub btnUpdate_Click(ByVai sender As System.Object, _
ByVal e As System.EventArgs) Handles btnUpdate.Click
daEmployees.Update(dsEmployeelnfo, "Employees")
End Sub
243
Private Sub LoadCommandBuilder()
Dim param As SqlParameter
If conn.State =Then
conn.Open()
End If
' Создание нового объекта DataAdapter.
Dim SQL As String
SQL = "Select FirstName, LastName, -Department!D, Salary,
ID from tblEmployee"
daEmployees = New SqlDataAdapter(SQL, conn)
' Создание команды с помощью SqlCommandBuilder.
cbEmployees = New SqlCommandBuilder(daEmployees)
End Sub
Основная подпрограмма LoadCommandBuilder () вызывается после щелчка на кнопке Load. Она демонстрирует способ явного открытия подключения, возможность избежать возникновения исключительных ситуаций при нескольких последовательных щелчках на кнопке Load и конфигурирование адаптера данных SqlDataAdapter (объект daEmployees) и конструктора команд SqlCommandBuilder (объект cbEmployees). Эти два объекта создаются и инициализируются с помощью их конструкторов, которые принимают значения своих свойств в качестве параметров. Конструктор объекта daEmployees получает строку с командой SELECT и объект подключения, а объект cbEmployees — адаптер данных daEmployees.
НА ЗАМЕТКУ ^
Только один объект SqlDataAdapter может быть связан только с одним объектом [ SqlCommandBuilder.
Далее конфигурируется объект-сетка, вызывается метод Fillдля загрузки таблицы Employees в объект daEmployees, задается таблица Employees для свойства DataSource объекта daEmployees и автоматически отображаются данные в сетке.
Обработчик события щелчка на кнопке Update содержит только одну строку кода, которая просто вызывает метод Update для объекта daEmployees.
Скомпонуйте проект DataSetCode и щелкните на кнопке DataAdapter Updates в форме frmDataSets. После отображения на экране формы frmUpdates щелкните на кнопке Load. Это приведет к считыванию данных из таблицы Employees, загрузке данных в объект tblEmployees объекта dsEmployeelnfo и отображению их в сетке, как показано на рис. 6.2.
Теперь можно протестировать работу этой формы, внося в данные произвольные изменения. Например, добавьте в сетку новые записи, прокручивая ее вниз и вводя новые данные в последней пустой строке сетки. Удалите несколько строк, выделяя их и нажимая клавишу <Delete>. Измените значения некоторых данных в уже имеющихся записях. Учтите, что все эти изменения не будут отражены в базе данных до тех пор, пока пользователь не щелкнет на кнопке Update. Внесенные в базу данных изменения можно проверить, используя любые инструменты просмотра таблиц базы данных или щелкая на кнопке Load для повторной загрузки данных из таблиц базы данных в объект grdDataGrid формы f rmUpdates.
244
РИС. 6.2. Отображение данных из таблицыв сетке
grdDataGrid
НА ЗАМЕТКУ ,
Хотя использование объектадля генерации команд обновления
требует минимального количества кода, оно связано со значительными накладными расходами. Дело в том, что объектдолжен выполнить
тельное обращение к серверу базы данных для извлечения метаданных, необходимы для автоматической генерации команд обновления. Эта возможность очень полезна и позволяет создавать запросы "на лету". Однако, если запросы уже известны во время создания приложения, явное указание команд и их параметров в коде и использование явных команд обновления или программы-мастера DataAdapter Configuration Wizard приведет к повышению производительности.
Явное указание команд обновления
В отличие от довольно простого способа генерации нужных команд обновления с помощью объекта CommandBuilder, явное указание команд обновления в коде представляет собой хотя и более гибкий, но весьма трудоемкий способ. При его использовании каждая из четырех команд управления данными (Select, Insert, Update и Delete) должна быть создана отдельно. Чаще всего они кодируются в виде хранимой процедуры для каждой команды SQL.
В листинге 6.4 представлен сценарий SQL Server для генерации четырех хранимых процедур. Хранимая процедура SelectEmployees просто выбирает все поля таблицы tblEmployee. Хранимая процедура InsertEmployee принимает четыре параметра, т.е. по одному для каждого обновляемого поля. Хранимая процедура UpdateEmployee
245
принимает те же четыре параметра для обновляемых полей и еще один параметр для исходного значения идентификационного поля, которое используется в предложении WHERE для выбора обновляемой записи (на основании первичного ключа). Хранимая процедура DeleteEmployee принимает исходное значение идентификационного поля для удаления указанной записи.
Листинг 6.4. Сценарий SQL Server для создания хранимых процедур
ДЛЯ таблицы tblEmployee
IF EXISTS (SELECT * FROM sysobjects WHERE name
1 SelectEmployees 1 AND user_name(uid) =
DROP PROCEDURE [dbo].[SelectEmployees]
GO
CREATE PROCEDURE [dbo].[SelectEmployees].
AS
SET NOCOUNT ON;
SELECT FirstName, LastName, DepartmentID, Salary, ID FROM tblEmployee
GO
IF EXISTS (SELECT * FROM sysobjects WHERE name =
' InsertEmployee' AND user_name(uid) =
DROP PROCEDURE [dbo].[InsertEmployee]
GO
CREATE PROCEDURE [dbo].[InsertEmployee]
(
@FirstName varchar(50),
@LastName varchar(70),
@DepartmentID int, @Salary money
)
AS
SET NOCOUNT ON;
SELECT INTO tblEmployee(FirstName, LastName, DepartmentID, Salary) VALUES (@FirstName, @LastName, @DepartmentID, @Salary)
GO
IF EXISTS (SELECT * FROM sysobjects WHERE name =
'UpdateEmployee' AND user_name(uid) =
DROP PROCEDURE [dbo].[UpdateEmployee]
GO
CREATE PROCEDURE [dbo].[UpdateEmployee]
(
@FirstName varchar(50),
@LastName varchar(70),
@DepartmentID int,
@Salary money,
@Original_ID int
)
AS
SET NOCOUNT ON;
UPDATE tblEmployee SET FirstName = SFirstName, LastName = SLastName, DepartmentID = @Department ID, Salary = @Salary WHERE (ID = @Original_ID)
GO
IF EXISTS (SELECT * FROM sysobjects WHERE name
1DeleteEmployee' AND user_name(uid) =
DROP PROCEDURE [dbo].[DeleteEmployee]
GO
CREATE PROCEDURE [dbo].[DeleteEmployee]
(
@Original_ID int
)
AS
SET NOCOUNT ON;
DELETE FROM tblEmployee
WHERE (ID = @Original_ID) GO
Вернемся к коду приложения. Во-первых, изменим первую строку кода в подпрограмме btnLoad_Click, т.е. вместо вызова подпрограммы LoadCommandBuilder вставим вызов подпрограммы LoadExplicitCode. Кроме того, для отладки явно заданного кода для команды обновления нужно добавить блок Try-Catch в подпрограмму btnUpdate_Click, которая теперь будет иметь приведенный ниже вид.
Private Sub btnUpdate_Click(ByVai sender As System.Object, _
ByVai e As System.EventArgs) Handles btnUpdate.Click
Try
daEmployees.Update(dsEmployeelnfo, "Employees")
Catch es As SqlException
MessageBox.Show(es.Message)
End Try
End Sub
Наконец, код подпрограммы LoadExplicitCode будет выглядеть так, как показано в листинге 6.5.
Листинг 6.5. Подпрограмма LoadExplicitCode для четырех специализированных команд SQL для адаптера данных daEmployees
Private Sub LoadExplicitCode()
Dim param As SqlParameter
If conn.State =Then
conn.Open() End If
' Создание нового объекта DataAdapter.
daEmployees = New SqlDataAdapter()
' Создание специализированной
' хранимой процедуры для команды Select.
daEmployees.SelectCommand = New SqlCommand()
With daEmployees.SelectCommand
.Connection = conn
.CommandType =
.CommandText =
End With
ADO.NET: объект DataAdapter — 247
1 Создание специализированной
' хранимой процедуры для команды Insert. daEmployees.Insertcommand = New SqlCommand() With daEmployees.Insertcommand
.Connection = conn
.CommandType =
.CommandText =
End With
param =
New SqlParameter("@FirstName", SqlDbType.VarChar, 50) ) param.Direction = param.Sourcecolumn = param.Sourceversion =
param = daEmployees.Insertcommand.Parameters .Add( _ New SqlParameter("@LastName", SqlDbType.VarChar, 70)) param.Direction = param.Sourcecolumn = param.Sourceversion =
param = daEmployees.Insertcommand.Parameters.Add( _ New SqlParameter("@DepartmentID", SqlDbType.Int))
param.Direction =
param.Sourcecolumn =
param.Sourceversion =
param = daEmployees.Insertcommand.Parameters.Add( _ New SqlParameter("@Salary", SqlDbType.Money))
param.Direction =
param.Sourcecolumn = "Salary" param.Sourceversion =
' Создание специализированной
' хранимой процедуры для команды Update. daEmployees.UpdateCommand = New SqlCommand() With daEmployees.UpdateCommand
.Connection = conn
.CommandType =
.CommandText =
End With
param = daEmployees.UpdateCommand.Parameters.Add( _ New SqlParameter("@FirstName", SqlDbType.VarChar, 50)) param.Direction =
param.Sourcecolumn = "FirstName"
param.Sourceversion =
param = daEmployees.UpdateCommand.Parameters.Add( _ New SqlParameter("SLastName", SqlDbType.VarChar, 70) ) param.Direction = param.Sourcecolumn = "LastName" param.Sourceversion =
param = daEmployees.UpdateCommand.Parameters.Add( _ New SqlParameter("@DepartmentID", SqlDbType.Int))
param.Direction =
param.Sourcecolumn =
param.Sourceversion =
param = daEmployees.UpdateCommand.Parameters.Add( New SqlParameter("@Salary", SqlDbType.Money))
param.Direction =
param.Sourcecolumn = "Salary"
param.Sourceversion =
param = daEmployees.UpdateCommand.Parameters.Add(
New SqlParameter("@Original_ID", SqlDbType.Int))
param.Direction =
param.Sourcecolumn = "ID"
param.Sourceversion =
' Создание специализированной
' хранимой процедуры для команды Delete.
daEmployees.DeleteConunand = New SqlCommand()
With daEmployees.DeleteCommand
.Connection = conn
.CommandType =
.CommandText =
End With
param = daEmployees.DeleteCommand.Parameters.Add( New SqlParameter("@Original_ID", SqlDbType.Int)) param.Direction =
param.SourceColumn = "ID"
param.Sourceversion =
End Sub
НА ЗАМЕТКУ—
Код присвоения значений для каждого из объектов-параметров мог быть более компактным за счет вызова другой перегруженной версии метода Add. Эта альтернативная версия принимает значения для всех необходимых свойств-параметров в одном вызове метода с длинным списком параметров.
Невзирая на большой размер, код подпрограммы LoadExplicitCode имеет очень простую и понятную структуру, если определен интерфейс (параметры и типы) для хранимых процедур. Для всех свойств объекта Command создается новый экземпляр объекта SQLCommand. Ему присваивается общий объект Connection и задаются значения свойств CommandType и CommandText. Затем нужно создать и конфигурировать все параметры каждой команды.
Скомпонуем проект DataSetCode и снова проверим работоспособность полученного приложения. Оно будет работать как и прежде, но теперь в подпрограмме LoadExplicitCode используются специализированные команды для обновления базы данных. Этот подход требует больших усилий по созданию кода, но является более гибким, предлагает более высокую производительность и централизованное управление хранимыми процедурами.
Вставка бизнес-логики в команды обновления
Прежде уже описывалось, как хранимые процедуры для специализированных команд обновления можно использовать для вставки бизнес-логики в хранимые процедуры, которые вызываются автоматически. По сравнению с прежними версиями модели
ADO и другими моделями доступа к данным новизна заключается не во вставке логики в хранимые процедуры, поскольку эта функциональная возможность существовала и раньше. Дело в том, что эти хранимые процедуры вызываются автоматически при нии "пакетных" обновлений вместо явной организации вызовов в коде программы.
Для демонстрации этого подхода попробуем изменить хранимую процедуру. что бизнес-логика определяется следующим образом: если при вставке новой записи о сотруднике значение зарплаты в поле Salary не определено или равно 0, то для него автоматически задается значение, определяемое функцией отдела. Для реализации этой бизнес-логики применим очень простой механизм: автоматически присвоенное значение будет равно произведению номера отдела и значения 10 000. (Конечно, в реальных условиях компания может использовать более удачный алгоритм для указания этого значения!) Измененная хранимая процедура теперь будет выглядеть так, как показано ниже.
CREATE PROCEDURE
(
@FirstName' varchar(50) ,
@LastName varchar(70),
int, money
A:S
SET NOCOUNT ON;
if= 0 oris null)
begin
— Вычисления
set=* 1000
end
INSERT INTO tblEmployee
DepartmentID, Salary) VALUES
(@FirstName, SLastName, BDepartmentlD, @Salary)
GO
НАЗАМЕТКУ
Поскольку хранимая процедура уже существует, то для ее создания в новом виде нужно удалить существующую хранимую процедуру или заменить первую строку данного сценария строкой ALTER
Для выполнения этого сценария можно использовать программу SQL Query Analyzer.
Теперь можно вновь запустить приложение проекта DataSetCode без внесения каких-либо изменений в его код. Попробуйте вставить в форму несколько новых записей с пустыми значениями поля Salary и проверьте работоспособность новой хранимой процедуры, т.е. корректного автоматического присвоения значений для этого поля.
Использование компонента DataAdapter во время создания приложения
После описания в предыдущем разделе способа создания кода вручную продемонстрируем метод автоматического создания кода с помощью программы-мастера Data Adapter Configuration Wizard.
250
Программа-мастер Data Adapter Configuration Wizard предлагает целый набор параметров для конфигурирования объекта DataAdapter во время создания приложения. Она обладает более широкими функциональными возможностями, чем другие компоненты времени создания приложения. Она содержит графический интерфейс для управления многими внутренними объектами и свойствами компонента, а также инструменты автоматического создания кода.
Откройте форму frmUpdates в режиме конструктора формы.
Из вкладки Data выберите и перетащите в форму компонент SqlDataAdapter. Этот компонент невидим во время выполнения, поэтому он появится под формой среди других невизуальных компонентов. При этом на экране откроется первое диалоговое окно программы-мастера Data Adapter Configuration Wizard. Щелкните на кнопке Next.
В следующем диалоговом окне, Choose Your Data Connection (Выберите подключение к базе данных), выберите подключение к базе данных Novelty. Если оно не указано, то его следует создать, щелкнув на кнопке New Connection (Новое подключение) и указав характеристики подключения в стандартном диалоговом окне Data Link Properties (Свойства соединения с данными). После выбора подключения щелкните на кнопке Next.
В следующем диалоговом окне, Choose a Query Type (Выбрать тип запроса), можно выбрать тип запроса: Use SQL statements (Использовать команды SQL), Create new stored procedures (Создать новые хранимые процедуры) или Use existing stored procedures (Использовать существующие хранимые процедуры). В данном примере выберите предлагаемый по умолчанию тип запроса Use SQL Statements, хотя на практике бывает полезно использовать созданные программой-мастером хранимые процедуры.
В следующем диалоговом окне, Generate the SQL Statements (Создать SQL- команду), введите в текстовую область приведенную ниже команду SQL, которая будет использоваться объектом DataAdapter в качестве основы для других команд обновления.
SELECT FirstName, LastName, DepartmentID, Salary, ID FROM tblEmployee
В этом диалоговом окне есть еще две кнопки. После щелчка на кнопке Advanced Options (Дополнительные возможности) отображается диалоговое окно Advanced SQL Generation Options (Дополнительные возможности для генерации команд SQL) с тремя параметрами. Первый параметр, Generate Insert, Update and Delete Statements (Создать команды вставки, обновления и удаления), указывает на необходимость создания команд вставки, обновления и удаления на основе указанной команды SQL (или нужно использовать объект DataAdapter только для наполнения данными набора данных DataSet). Второй параметр, Use Optimistic concurrency (Использовать оптимистическую обработку параллельно выполняемых команд), означает включение в команду SQL предложения WHERE для всех значений полей в обновленной записи. Это позволяет обнаружить изменения данной записи, внесенные другими пользователями в базе данных, так как в этом случае такая команда SQL будет отвергнута. Третий параметр, Refresh the Dataset (Обновить набор данных Dataset), позволяет до-
251
бавить еще одну команду SELECT ккомандам вставки и обновле
ния данных для возвращения значений идентификационных полей, предлагаемых по умолчанию значений полей, а также вычисленных сервером значений. После щелчка на кнопке Query Builder (Конструктор запроса) отображается одноименное стандартное диалоговое окно для графического создания команды SELECT вместо непосредственного ввода ее в текстовой области диалогового окна Generate the SQL statements.
Щелкните на кнопке Next для отображения диалогового окна View Wizard Results (Просмотр результатов создания команды SQL с помощью
Щелкните на кнопке Finish для применения указанных параметров объекта DataAdapter.
НА ЗАМЕТКУ :
I Сразу после создания компонента DataAdapter можно изменить его свойства и па- • раметры с помощью окна свойствили повторного запуска программы-
\ мастера Data Adapter Configuration Wizard. Чтобы повторно запустить программу-
мастер для уже имеющегося компонента DataAdapter, щелкните на нем правой ; кнопкой мыши и выберите в контекстном меню команду Configure Data Adapter
| фигурировать адаптер данных).можно также запустить, выбрав уже имеющийся
компонент DataAdapter под формой, а затем щелкнув на ссылке Configure Data Adapterадаптер данных) в той части окна свойств Properties, кото
рая находится между списком свойств и их
Аналогично конструктору командпрограмма-мастер Data Adapter
Configuration Wizard предназначена для создания команд для одной независимой таблицы базы данных. Однако эта программа содержит несколько параметров кон-
I фигурации, например для создания новых или использования существующих хранимых процедур, что позволяет очень легко и быстро создавать код приложения.
Теперь нужно связать объект DataAdapter, которому автоматически присвоено имя SqlDataAdpaterl, с созданным кодом. Для этого нужно явно открыть созданное подключение SqlConnectionl.
Подпрограмму btnLoad_Click нужно изменить таким образом, чтобы она вызывала подпрограммувместо подпрограммы LoadExplicitCode. Да
лее нужно вызвать метод Fill вновь созданного объекта SqlDataAdpaterl. Кроме того, для использования нового объекта SqlDataAdpaterl потребуется изменить код подпрограммы btnLoad_Click. Наконец, нужно создать код подпрограммы LoadWizardCode, которая предназначена для открытия нового подключения. Эти три подпрограммы показаны в листинге 6.6.
Листинг 6.6. Измененные и новые подпрограммы для использования нового объекта SqlDataAdpaterl в ужеприложении
Private Subsender As
e AsHandles
= New
LoadWizardCode()
' Конфигурирование объекта-сетки DataGrid.
Me.grdDataGrid.PreferredColumnWidth = 110
Me.grdDataGrid.AllowSorting = True
' Вставка данных в объект DataSet.
daEmployees.Fill(dsEmployeelnfo, "Employees")
' Присвоение объекта DataSet объекту DataGrid.
=
dsEmployeelnfo.Tables("Employees")
Me.btnUpdate.Enabled = True
End Sub
Private Sub btnUpdate_Click(ByVai sender As System.Object, _ ByVai e As System.EventArgs) Handles btnUpdate.Click
Try
'daEmployees.Update(dsEmployeelnfo, "Employees")
SqlDataAdapterl.Update(dsEmployeelnfo, "Employees")
Catch es As SqlException
MessageBox.Show(es.Message)
End Try
End Sub
Private Sub LoadWizardCode()
If SqlConnectionl.State =Then
SqlConnectionl.Open()
End If
End Sub
Для просмотра кода, автоматически созданного программой-мастером, нужно открыть раздел Windows Form Designer generated code (Код, сгенерированный конструктором Windows Form). Он напоминает код, созданный вручную в листинге 6.5.
НА ЗАМЕТКУ
В данном примере используются предлагаемые по умолчанию имена объектов SqlDataAdapterl и SqlSelectCommand. Однако им можно присвоить более инфор- I мативные имена в соответствии с принятым соглашением об именах. Во время создания компонентов их имена изменяются с помощью установки соответствующего
| значения для свойства (Name) в окне свойств Properties избранного компонента.
| Для изменения имен отдельных объектов-команд (например, объектов SelectCom- I ma nd И InsertCommand)нужно выделить объект DataAdapter, открыть окно свойств Properties, найти нужную команду, раскрыть список ее свойств и указать соответствующее значение для ее свойства (Name) • Аналогично можно изменить все остальные свойства объекта-команды.
Скомпонуйте проект DataSetCode и запустите полученное приложение для проверки его работоспособности. Как видите, программа-мастер Data Adapter Configuration
253
Wizard позволяет с удивительной легкостью и быстротой создавать полностью функциональный код. Это усовершенствование смогут по достоинству оценить те программисты, которым раньше приходилось вручную создавать код для хранимых процедур.
Во время создания приложения можно воспользоваться еще одной функциональной возможностью компонента DataAdapter— инструментом предварительного просмотра данных. Для этого щелкните правой кнопкой мыши на объекте DataAdapter и выберите в контекстном меню команду Preview Data (Предварительный просмотр данных) или выберите объект DataAdapter и щелкните на ссьике Preview Data в той части окна свойств Properties, которая находится между списком свойств и их описанием. В списке Data adapters (Объекты — адаптеры данных) выберите нужный объект-адаптер и щелкните на кнопке Fill Dataset (Вставить данные в набор данных). На рис. 6.3 показан результат выполнения этих действий для объекта SqlDataAdapter в форме frmUpdates.
РИС. 6.З. Данные, вставленные в набор данных из таблицы tblEmployee с помогцъю диалогового окна предварительного ПрОсмотра данных Data Adapter Preview
Бизнес-ситуация 6.1: комбинация нескольких связанных таблиц
Как уже отмечалось, ни один из перечисленных выше методов указания команд обновления не позволяет обновлять данные сразу в нескольких таблицах, особенно если они связаны родительско-дочерним отношением. Значит ли это, что в модели
254
ADO.NET не поддерживается обработка такой ситуации? Нет, это не так. В данной бизнес-ситуации для доказательства этого утверждения демонстрируются функциональные возможности модели ADO.NET, в частности применение пакета команд SQL для вставки данных из двух таблиц за счет одного обращения к серверу. Итак, программист компании Jones Novelties, Inc. создает форму для отображения и обновления данных о клиентах и их заказах. Для создания такой формы выполните перечисленные ниже действия.
Запустите среду разработки Visual Studio .NET.
Создайте новый проект Visual Basic Windows Application.
Назовите проект BusinessCase6.
Укажите путь к файлам проекта.
Увеличьте размер формы Forml и в окне Properties укажите значение frmCus- tomersOrders для свойства (Name) и значение Customers and Orders для свойства Text формы Forml.
Перетащите в форму кнопку и в окне Properties укажите значение bntFill для ее свойства (Name) и Fill для свойства Text; перетащите в форму кнопку и в окне Properties укажите значение bntUpdate для ее свойства (Name) и значение Update для свойства Text; перетащите в форму сетку данных и в окне Properties укажите значение grdCustomersOrders для ее свойства (Name).
Расположите все элементы управления, как показано на рис. 6.4.
РИС. 6.4. Расположение элементов управления в формеfrmCustomersOrders
255
В верхней части файла с исходным кодом вставьте следующие строки кода для импорта пространств имен System. Data и System. Data . SqlClient:
Imports System.Data
Imports System.Data.SqlClient
ВтелоопределенияклассадляформыfrmCustomersOrders включите код из листинга 6.7.
Листинг 6.7. Код загрузки и обновления данных сразу в нескольких связанных таблицах
Private ds As DataSet
Private cn As New SqlConnection( _ "server=localhost;uid=sa;database=Novelty")
Private Sub btnFill_Click(ByVai sender As System.Object, _ ByVai e As System.EventArgs) Handles btnFill.Click
Dim da As New SqlDataAdapter() grdCustomersOrders.Datasource = Nothing ds = New DataSet ()
. * Создание команды SELECT.
da.SelectCommand = New SqlCommand() da.SelectCommand.Connection =
da.SelectCommand.CommandType =
= _ "select * from tblCustomer; select * from tblOrder"
' Указание информативных имен для таблиц. da.TableMappings.Add("Table", "Customers") da.TableMappings.Add("Tablel", "Orders")
' Загрузка данных. da.Fill(ds)
Создание отношения.
ds.Relations.Add(nCustomer_Orders”, _ ds.Tables("Customers").Columns("ID"), _ ds.Tables("Orders").Columns("CustomerlD"))
' Отображение данных.
grdCustomersOrders.Datasource = ds
End Sub
Private Sub btnUpdate_Click(ByVai sender As System.Object, _ ByVal e As System.EventArgs) Handles btnUpdate.Click
' Создание адаптеров данных.
Dim daCustomers As New SqlDataAdapter( _ "select * from tblCustomer", cn)
Dim daOrders As New SqlDataAdapter( _ "select * from tblOrder", cn)
Dim cbCustomers As New SqlCommandBuilder(daCustomers)
Dim cbOrders As New SqlCommandBuilder(daOrders)
Try
’ Внесение изменений в таблицы в "правильном"
' порядке (см. далее в тексте).
Dim ChangedTable As New DataTable()
' Удаление записей в дочерней таблице.
ChangedTable =
ds.Tables("Orders").GetChanges(DataRowState.Deleted)
If Not ChangedTable Is Nothing Then
daOrders.Update(ChangedTable)
End If
Все измененные записи в родительской таблице.
ChangedTable = ds.Tables("Customers").GetChanges
If Not ChangedTable Is Nothing Then
daCustomers.Update(ChangedTable)
End If
Новые или измененные записи в дочерней таблице.
ChangedTable =
ds.Tables("Orders").GetChanges(DataRowState.Added _
Or DataRowState.Modified)
If Not ChangedTable Is Nothing Then
daOrders.Update(ChangedTable)
End If
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
Первая подпрограмма btnFill_Click считывает обе таблицы из базы данных посредством одного обращения к базе данных благодаря выполнению пакета команд SQL. В объекте CommandText отдельные команды пакета отделяются точкой с запятой.
Обратите внимание, что предлагаемые по умолчанию имена таблиц Table и Tablel в приведенных ниже строках кода отображаются на более информативные имена Customers и Orders.
' Указание информативных имен для таблиц.
da.TableMappings.Add("Table", "Customers")
da.TableMappings.Add("Tablel", "Orders")
НА ЗАМЕТКУ'— —
Более подробно способы отображения таблиц и полей рассматриваются в главе 7, “ADO.NET: дополнительные компоненты".
После вставки данных в набор данных ds между таблицами Customers и Orders создается отношение DataRelation, где Customers является родительской таблицей, a Orders — дочерней. Последняя строка кода в этой подпрограмме связывает набор данных DataSet с сеткой для отображения данных.
Вторая подпрограмма, btnUpdate_Click, вносит в базу данных изменения данных в объектах-таблицах с учетом родительско-дочерних связей между ними. К сожалению, ссылочная целостность данных не поддерживается автоматически, а потому ее нужно организовать вручную. Для этого разработчику необходимо сгруппировать типы изменений, а затем выполнить их в правильном порядке. Для двух таблиц, между которыми существуют родительско-дочерние связи, изменения следует вносить в приведенном ниже порядке.
257
Сначала удалить записи в дочерней таблице.
Вставить, обновить и удалить записи в родительской таблице.
Вставить и обновить записи в дочерней таблице.
Для получения соответствующих изменений подпрограмма должна вызвать для данной таблицы метод GetChanges с фильтром состояния записи. Каждый вызов метода GetChanges возвращает объект DataTable только с измененными записями и заданным состоянием. Если таких записей нет, то возвращается значение Nothing. Если есть хотя бы одна измененная строка с заданным состоянием, то для фактического обновления базы данных вызывается метод Update объекта DataAdapter. Код этой подпрограммы окружен блоком операторов Try-Catch для обработки исключительных ситуаций, которые могут возникнуть в процессе обновления базы данных.
Скомпонуйте проект BusinessCase6 и проверьте полученное приложение, выполнив перечисленные ниже действия.
Запустите полученное приложение BusinessCase6 и щелкните на кнопке Fill. Это приведет к вставке данных в объект DataSet из базы данных Novelty. Однако строка кода
grdCustomersOrders.DataSource = ds
связывает с сеткой весь объект DataSet, а не какую-то одну таблицу DataTable. Поэтому сетка содержит раскрывающийся список таблиц возле кнопки с изображением знака "плюс", как показано на рис. 6.5.
Щелкните на пиктограмме с изображением знака "плюс", раскроется список ссылок на две таблицы объекта DataSet.
РИС. 6.5. Исходный вид формы f rmCustomersOrders после вставки данных в объект DataSet
258
Щелкните на ссылке Customers, и в сетке будут отображены данные из таблицы Обратите внимание, что каждая строка в таблице
имеет кнопки с изображением знака "плюс" с левой стороны, что означает связь этой таблицы с другими таблицами. После щелчка на такой кнопке раскрывается список объектов DataRelations для данной таблицы. В нашем примере имеется только одна ссылка для отношения Customer_0rders, созданного в подпрограмме btnFillClick (рис. 6.6).
РИС. 6.6. Ссылкадля первой записи из таблицы Customers
Щелкните на ссылке Customer_0rders в первой записи. На основании ления отношения Customer_Orders будут вставлены и отображены записи из таблицы Orders, которые относятся к текущей записи из таблицы Customers.
НА ЗАМЕТКУ
При переходах между разными таблицами и отношениями можно всегда вернуться исходному положению виспользуя кнопку Navigates back to the parent rows
(Обратный переход к родительским записям) с изображением стрелки в правом верхнем углу
Теперь с помощью этой формы пользователи могут просматривать имеющиеся и вводить новые данные. При вводе новой дочерней записи значение 1 в поле указывается автоматически, потому что сетка способна определить его в связанной записи из родительской таблицы. Продолжим и добавим значения для полей OrderDate и Amount. При этом не нужно задавать значе-
259
ние для поля ID, потому что это идентификационное поле, которому значение присваивается автоматически.
Щелкните на кнопке Update для выполнения подпрограммы btnUpdate_ Click из листинга 6.7, которая вносит указанные изменения данных в базу данных.
Чтобы проверить корректность внесенных изменений, щелкните на кнопке Fill для повторной загрузки информации из базы данных в объект DataSet и сетку. Откройте первую запись таблицы Customers и найдите ее дочерние записи. Убедитесь в том, что среди них находится введенная вами дочерняя запись.
Попробуйте внести дополнительные изменения в базу данных вставляя, удаляя и изменяя записи в обеих таблицах и проверяя выполнение обновлений.
НА ЗАМЕТКУ
Успешное удаление записи из родительской таблицы Customer вместе с ее дочерними записями из таблицы Orders возможно благодаря заданному по умолчанию ограниче- . нию ForeignKeyConstraint для отношения Customer_Orders, которое заключается в каскадном обновлении (удалении) данных в родительской и дочерней таблицах.
Резюме
В этой главе рассмотрен объект DataAdapter, который является одним из основных объектов модели ADO.NET. Он играет роль моста между неподключенными объектами DataSet (и связанными с ними объектами) и подключенными объектами DataProvider, которые фактически связаны и подключены к физическому источникуданных.
Объект DataAdapter используется для вставки данных в объект DataSet из источника данных с помощью явно заданных команд или хранимых процедур. Объект DataAdapter также автоматически обновляет источник данных теми изменениями, которые произошли в объекте DataSet, что позволяет полностью настроить команды вставки, обновления и удаления для источника данных.
Вопросы и ответы
Иногда требуется программно создать и вставить в набор несколько записей, а затем внести эти обновления в базу данных. В модели ADO 2.X можно создать набор записей, но нельзя обновить базу данных. А как обстоит дело в модели ADO.NET?
В модели ADO.NET это требование реализуется очень просто. Как отмечалось в главе 5, "ADO.NET: объект DataSet", объект DataSet является контейнером данных, который не зависит от фактически используемого источника данных. Для обновления базы данных изменениями из объекта DataSet достаточно только подключиться к уже сконфигурированному объекту DataAdapter и вызвать его метод Update, который фактически обновит базу данных. Это верно и для программно созданных объектов DataSet, а не только для созданных с помощью команды Select объектов DataAdapter. В момент готовности к внесению изменений в физический источник данных достаточно подключиться к сконфигурированному объекту DataAdapter и вызвать его метод Update.
ГЛАВА
11ADO.NET:
1
1
дополнительные компоненты
В ЭТОЙ ГЛАВЕ...
» Обнаружение конфликтов при параллельном доступе к данным
Отображения таблиц и полей
Объект DataVfew
Строго типизированные наборы данных
Резюме
Вопросы и ответы
В предыдущих главах рассматривается архитектура модели доступа к данным ADO.NET, объекты модели ADO.NET, их основные свойства и методы. В данной главе описываются четыре дополнительных компонента модели ADO.NET, которые имеют огромное значение для создания профессиональных приложений.
Обнаружение конфликтов при параллельном доступе к данным
Разработчики, имеющие опыт создания многопользовательских баз данных, знают о потенциальных проблемах при параллельном доступе к данным, возникающих при попытке одновременного обновления одних и тех же данных со стороны нескольких пользователей. Допустим, что пользователь А считывает какие-то данные и пользователь Б считывает те же данные, затем пользователь А изменяет эти данные, а пользователь Б также пытается их изменить. Как можно решить эту проблему? Существует два основных способа решения конфликтных ситуаций при параллельном доступе к данным.
Первый способ — пессимистическая блокировка (или пессимистическое управление параллельностью) — решает проблему перезаписи пользователем Б изменений, внесенных пользователем А. Для этого после считывания данных пользователем А для их редактирования на эти данные накладывается блокировка. Эта блокировка запрещает доступ к данным со стороны других пользователей до тех пор, пока пользователь А не завершит работу с заблокированными данными и не снимет блокировку.
Этот способ особенно полезен при высокой степени параллельности выполняемых операций с данными или при необходимости всегда просматривать наиболее свежие данные. Такая ситуация обычно возникает в системах оперативного учета материальных запасов или системах учета заказов, когда пользователь для принятия заказа должен убедиться в наличии товаров на складе.
Основными недостатками пессимистического управления параллельностью являются дополнительные накладные расходы на управление блокировками данных, необходимость постоянного подключения к базе данных и недостаточная масштабируемость. Плохая масштабируемость, особенно при работе в распределенной среде (например, в Internet), может привести к блокировке записей на несколько десятков секунд или даже минут.
Второй способ — оптимистическая блокировка (или оптимистическое управление параллельностью) — основан на очень кратковременной блокировке записей во время их фактического обновления. Этот способ решает проблему управления блокировками и масштабируемости, а также прекрасно подходит для редактирования наборов данных, отключенных от базы данных. Но что произойдет, если пользователь Б захочет обновить данные, уже обновленные пользователем А? Один из вариантов решения этой проблемы основан на признании только последнего обновления. Однако этот способ годится только для ограниченного круга приложений.
Для использования оптимистической блокировки нужно определить, изменялись ли данные после их последнего извлечения, что называется нарушением параллельного доступа (concurrency violation). Для этого применяются две основные стратегии.
263
Первая основана на создании временной метки или уникального номера версии для каждой записи, которые изменяются при обновлении записи. Исходное значение или номер версии включаются в состав предложения WHERE в команде обновления.
Вторая стратегия заключается в сохранении исходных значений полей. Эти значения становятся дополнительными условиями предложения WHERE в команде обновления.
В обоих способах, если исходная запись изменена, условие предложения WHERE не удовлетворяется, запись не будет найдена и не будет обновлена.
На заметку
j При использовании оптимистической блокировки в модели ADO 2.X в случае нару- | шения параллельного доступа появляется сообщение о невозможности найти запись, а не о нарушении параллельного доступа.
В моделиподдерживается только оптимистическая блокировка и в те
кущей версии нет встроенной поддержки пессимистической блокировки. В Visual Studioпредусмотрено несколько возможностей для реализации оптимистиче
ской блокировки. Эта поддержка находится в полном согласии с расширенной поддержкой распределенных, отключенных и асинхронных приложений.
Команды SQL UPDATE и DELETE, сгенерированные объектоми
программой-мастером Data Adapter Configuration Wizard, содержат предложение WHERE для определения конфликтов параллельного доступа. Рассмотрим более внимательно код из главы 6,объектсозданный с помощью про
граммы-мастера Data Adapter Configuration Wizard. Для этого нужно открыть раздел кода с заголовком Windows Form Designer generated code (Код, сгенерированный конструктором Windows Form) в коде формы rmUpdates. Но сначала рассмотрим команду SQL UPDATE, которая приводится в листинге 7.1.
Листинг 7.1. КомандаUPDATE, созданная программой-мастером Data
Adapter Configuration Wizard
UPDATE tblEmployee
SET=
LastName = @LastName, =
Salary = @Salary
WHERE
(ID = @Original_ID) AND
(DepartmentID =OR
@Original_DepartmentID IS NULL AND DepartmentID IS NULL) AND
(FirstName = @Original_FirstName) AND
(LastName = @Original_LastName) AND
(Salary = @Original_Salary OR
@Original_Salary IS NULL AND Salary IS NULL)
SELECT FirstName, LastName, DepartmentID, Salary, ID
FROM tblEmployee WHERE (ID@ID)
Эта команда SQL выглядит как обычная команда UPDATE, которая задает новые значения для четырех обновляемых полей в качестве параметров объекта Update-
264
Command. Предложение WHERE содержит первичный ключ (ID), а также исходные значения для каждого поля и проверяет их соответствие текущим значениям записи в базе данных. Более того, эта команда SQL проверяет наличие неопределенных значений NULL в базе данных и полях таблицы tblEmployee.
Команда SELECT (заданная при конфигурировании объекта DataAdapter) располагается вслед за командой UPDATE после точки с запятой. Точка с запятой всегда используется для разделения команд в пакете команд SQL, а команда SELECT добавляется по умолчанию для возвращения обновленной записи в приложение.
Рассмотрим код указания параметров для объектакак показано в
листинге7.2.
Листинг 7.2. Код установки параметровсгенерированной
спомощьюпрограммы-мастераDataAdapterConfigurationWizard
Me.SqlUpdateCommandl.Parameters.Add( 9
New
50,
Me.SqlUpdateCommandl.Parameters.Add( <5
New
70,
Me.SqlUpdateCommandl.Parameters.Add( 9
New
4,
Me.SqlUpdateCommandl.Parameters.Add( 9
New
8,
Me.SqlUpdateCommandl.Parameters.Add( 9
New
4,
False, CType(0,
"ID",
System.Data.DataRowVersion.Original, Nothing))
Me.SqlUpdateCommandl.Parameters.Add( 9
New
4,
False, CType(0,
CType(0,
System.Data.DataRowVersion.Original, Nothing))
Me.SqlUpdateCommandl.Parameters.Add( 9
New
50,
False,
CType(0,"FirstName",
System.Data.DataRowVersion.Original, Nothing))
Me.SqlUpdateCommandl.Parameters.Add( 9
New
70,
False, "LastName", System.Data.DataRowVersion.Original, Nothing))
Me.SqlUpdateCommandl.Parameters.Add( 9
New
System.Data.ParameterDirection.Input, False, <£
"Salary",
System.Data.DataRowVersion.Original, Nothing))
Me.SqlUpdateCommandl.Parameters.Add( <0
New
Для данного объекта-команды заданы десять параметров. Первые четыре параметра являются текущими (возможно, измененными) значениями полей, которые следует обновить в базе данных. Как сообщается в главе 5,объект
каждая запись может иметь одну из четырех версий значения в каждой записи. По умолчанию используется текущее значение из считываемого поля.
Следующие пять параметров являются исходными значениями полей, которые упоминаются в предложении WHERE. Учтите, что для извлечения исходной версии значения поля (а не предлагаемой по умолчанию текущей версии) нужно явно указать исходную версию, как показано ниже.
System.Data.DataRowVersion.Original
НА ЗАМЕТКУ :
I Необязательно использовать исходные версии значения для всех полей в
нии WHERE. Можно настроить нужным образом любые команды обновления, чтобы I уведомить об обновлении только одного или двух полей записи. В таком случае достаточно обновить только отдельные поля, а не все поля сразу.
Последнийтекущее значение поля применяется в качестве
параметра команды SELECT, используемой для возвращения обновленных значений записи.
После каждой операции вставки, обновления или удаления объект DataAdapter проверяет количество строк, охваченных операцией. Если количество строк равно нулю, то генерируется исключительная ситуация DBConcurrencyException, поскольку обычно предполагается, что это результат конфликта при параллельном доступе к данным. Для ее перехвата и обработки следует включить блок Try-Catch в подпрограмму btnUpdate_Click, которая показана в листинге 7.3.
Листинг 7.3. Блок Try-Catch для обработки исключительной ситуации
DBConcurrencyException
Private Subsender As
e AsHandles
Try
daEmployees.Update(dsEmployeelnfo, "Employees")
Catch ec As DBConcurrencyException
' Выполнить какие-то действия.
Catch es As DBConcurrencyException
MessageBox.Show(es.Message)
End Try
End Sub
Отображения таблиц и полей
Объект DataAdapter содержит коллекцию объектов DataTableMapping. Они предназначены для отображения имен таблиц и полей в источнике данных на их имена в объекте DataSet. Конечно, сразу после конфигурирования этот способ работает в обоих направлениях, т.е. при чтении данных из источника в объект DataSet и при записи данных в источник из объекта DataSet.
В данном примере в методе Fill объекта DataAdapter указывается объект DataSet и имя таблицы в этом наборе данных.
daEmployees.Fill(dsEmployeelnfo, "Employees")
Однако на самом деле второй аргумент метода Fill содержит имя объекта — отображения имени таблицы. Объект DataAdapter ищет определенное отображение с таким же именем и, если находит, использует полученную информацию для выполнения метода Fill. А если отображение не найдено, то создается таблица с именем переданного параметра и в нее вставляются данные.
Это значит, что можно, например, добавить отображение MappingName и отобразить его на таблицу empDataSetTable с помощью кода
daEmployees. TableMappings.Add("MappingName", "empDataSetTable")
А затем можно вызвать метод Fill следующим образом:
daEmployees.Fill(dsEmployeelnfo, "MappingName")
При этом считанные данные вставляются в таблицу empDataSetTable объекта dsEmployeelnfo.
После определения отображения таблицы можно приступать к созданию отображений полей. Эта операция имеет особенно большое значение при использовании в коде приложения имен полей, которые отличаются от имен, использованных в источнике данных. При работе с отображением таблицы для вставки данных в объект DataSet объект DataAdapter ищет все имеющиеся отображения полей для заданного отображения таблицы и использует их для отображения имен полей. При этом в объекте DataSet для всех полей, у которых нет отображения, будут использованы имена полей из источника данных.
Например, тестовая баз данных pubs, которая входит в состав SQL Server, была создана много лет назад, когда существовали очень строгие ограничения для имен полей, поэтому сейчас они имеют вид малопонятных аббревиатур. Отображения полей позволяют загружать данные из базы данных pubs в объект — набор данных с более понятными именами полей. Благодаря отображению удается удовлетворить все самые строгие требования и соглашения об именах, которые могут предъявляться со стороны администратора базы данных и архитектора приложения.
Продолжим работу с примером отображения таблицы и попробуем отобразить имена полей из источника данных, которые соответственно соглашению об именах, заданному архитектором приложения, начинаются с трехсимвольного префикса. В таком случае код отображения будет выглядеть так, как в листинге 7.4.
Листинг 7.4. Отображение таблицы и полей
daEmployees.TableMappings.Add("MappingName", "empDataSetTable") With daEmployees.TableMappings("MappingName").ColumnMappings
.Add("ID", "empEmployeelD")
.Add("FirstName", "empFirstName")
.Add("LastName", "empLastName")
..Add("DepartmentID", "empDepartmentID")
.Add("Salary", "empSalary")
End With
daEmployees.Update(dsEmployeelnfo, "MappingName" )
В главе 6, “ADO.NET: объект DataAdapter (см. листинги 6.1 и 6.2) представлена функция ReadData, которая вставляет в объект DataSet данные из источника данных и отображает содержимое объекта DataSet в списке формы frmDataSets (см. проект DataSetCode из главы 5, “ADO.NET: объект DataSet"). Если в подпрограмме btnDataAdapterFill_Click обработки щелчков на кнопке DataAdapter Fill вместо функции ReadData вызвать ее измененную версию ReadDataMapped, которая содержит код, показанный в листинге 7.5, то после запуска приложения проекта DataSetCode будет получен результат, показанный на рис. 7.1.
РИС. 7.1. Содержимое объекта DataSet с отображениями таблицы и полей
Листинг 7.5. Измененная версия функции ReadDataMapped для отображения таблицы и полей
Private Sub ReadDataMapped()
Dim daEmployees As SqlDataAdapter = New
SqlDataAdapter("select * from tblEmployee", _
"server-localhost;uid=sa;database=novelty")
dsEmployeelnfo = New DataSet (
' Конфигурация отображений таблицы и полей.
daEmployees.TableMappings.Add("MappingName", "empDataSetTable")
With daEmployees.TableMappings("MappingName").ColumnMappings
.Add("ID", "empEmployeelD")
.Add("FirstName", "empFirstName")
.Add("LastName", "empLastName")
.Add("DepartmentID", "empDepartmentID")
.Add("Salary", "empSalary")
End With
daEmployees.Fill(dsEmployeelnfo, "MappingName")
DisplayDataSet(dsEmployeelnfo)
End Sub
НА ЗАМЕТКУ
По умолчанию отображение таблицы называется Table. Оно применяется в том случае, если в методе Fill (или методе Update) используется только имя объекта DataSet. Однако его можно указать явно вместе с нужным именем таблицы с помощью приведенного ниже кода.
daEmployees.TableMappings.Add("Table", "MyTableName") daEmployees.Fill(dsEmployeelnfo)';
Это приведет к созданию таблицы MyTableName и вставке данных в нее.
Объект DataView
Объект DataView позволяет одновременно создавать разные представления данных из объекта DataTable и обладает перечисленными ниже свойствами, которые позволяют настраивать способ отображения данных.
Порядок сортировки (нисходящий или восходящий) по одному или нескольким полям.
Выражение для фильтрации записей, которое указывает критерии отображения записей на основе значений полей.
Фильтр состояния записи, который указывает критерии отображения запи
сей на основе состояния записи (см. перечисление DataViewRowState, показанное в табл. 5.3).
Хотя этот способ может показаться аналогичным способу на основе использования метода Select объекта DataTable, они существенно отличаются. С одной стороны, объект DataView — это полностью динамичное представление данных. Помимо изменений значений полей, вставки и удаления записей в таблице-источнике немедленно отражаются в объекте DataView. С другой стороны, метод Select возвращает массив фиксированный длины со ссылками на записи, которые отражают изменения значений полей в таблице-источнике, но не отражают вставку и удаление записей или их упорядочение. Этот динамический аспект объекта DataView особенно эффективно применяется для создания кода, связанного с данными.
269
НА
Хотя объект DataView аналогичен классическому представлению базы данных, он j все же отличается от него следующим:
не может использоваться как таблица;
не может быть объединением нескольких таблиц;
не может исключать поля, которые присутствуют в таблице-источнике;
не может включать дополнительные поля (например, вычисленные поля), которых ; нет в таблице-источнике.
ИИ
Объекты DataView используются с помощью свойства DefaultView объекта Da- taTable. Предположим, нужно создать представление для таблицы Customers, причем клиенты в нем должны быть упорядочены по почтовым индексам и иметь фамилии, начинающиеся с символа С. Для этого нужно использовать указанные ниже значения двух соответствующих свойств.
= _ =
= "Zip"
Если необходимо отобразить в представлении текущие значения только из тех писей, которые были изменены (но еще не сохранены), то нужно указать новое значение для свойства RowFilter и значение свойства RowState.
=
=
Defaultview.RowState.ModifiedCurrent
НА ЗАМЕТКУ
Объект DataView также имеет метод Find для поиска одной записи и метод FindRows для поиска и возвращения нескольких записей. Если нужно извлечь или набор записей, которые соответствуют заданному критерию, вместо динамического представления данных, то с помощью методов Find и FindRows (вместо указания свойства RowFilter) будут возвращены только интересующие нас записи. Этот метод обладает более высокой производительностью, чем метод на основе | тановки значения свойства RowFilter. Дело в том, что указание значения свойства \ RowFilter вызывает перестройку индекса представления, а методы Find и FindRows используют уже существующие индексы.
Для таблицы могут быть созданы также дополнительные объекты — представления данных. Для определения еще одного представления таблицы Customers нужно создать еще один объект dvView2 и указать его свойства так, как показано ниже.
dvView2 = New
"LastName",
Теперь, после определения объекта DataView, можно изменить его свойства.
= "LastName > = "LastName =
Глава 7
|270
НА ЗАМЕТКУ ;:—:——-
Объект DataViewManager предлагает удобный централизованный способ управле-
I ния параметрами представлений для всехтаблиц набора данных DataSet.
В большинстве других случаев объект DataView аналогичен объекту DataTable. Доступ к отдельным записям и полям представления осуществляется с помощью объекта DataRowView. Этот объект также поддерживает отношения между таблицами объекта DataSet.
' Объект DataView имеет модель редактирования, аналогичную модели редактирования объекта DataTable. Указание значения True для свойств AllowNew, AllowEdit и AllowDelete означает разрешение на выполнение соответствующих операций редактирования (вставки, изменения и удаления). Методы BeginEdit, EndEdit и CancelEdit объекта DataRoweView управляют внесением изменений в объект DataTable. Метод EndEdit вносит изменения в объект DataRow с версией Current. Эти изменения затем принимаются (или отвергаются) базовым объектом DataTable с помощью методаAcceptChanges (или Rejectchanges).
Рассмотрим теперь, как описанные выше идеи применяются на практике. Создайте новую форму frmDataViews в проекте DataSetCode (готовая версия этой формы находится в каталоге с примерами главы 5, “ADO.NET: объект DataSet") для создания двух разных представлений одной таблицы Customers. Каждый объект DataGrid отображает данные представления и содержит кнопки управления сортировкой и фильтрацией. Для этого выполните перечисленные ниже действия.
В форме frmDataSets проекта DataSetCode создайте новую кнопку под кнопкойDataAdapter Updates.
В окне Properties укажите значение btnDataViews для свойства (Name) и значение Data Views для свойства Text этой кнопки.
Создайте новую форму.
В окне Properties укажите значение frmDataViews для свойства (Name) и значение Dueling DataViews для свойства Text новой формы Forml.
Увеличьте размер формы frmDataViews.
Создайте в правой части формы сетку данных DataGridl, текстовое поле txtFilterl с надписью, поле со списком cboSortl и надписью, флажок chkDescl с надписью, поле со списком cboRowStatel и надписью, кнопку btnApplyl, перетаскивая их из панели элементов управления.
В окне Properties укажите значение Descending для свойства Text флажка chkDescl и значение Apply для свойства Text кнопки btnApplyl. Укажите значение DropDownList для свойства DropDownStyle полей со списком cboSortl и cboRowStatel. Укажите для свойства Text надписей значения Filter:,Sort by Column: и Row State.
Укажите значение Default DataView для свойства CaptionText сетки DataGridl.
Расположите элементы управления так, как показано на рис. 7.2.
271
РИС. 7.2. Расположение элементов управления в верхней части формы frmDa ta Views
РИС. 7.З. Окончательный вид формы frmDa t a Views
Выберите все элементы управления и скопируйте их в нижнюю часть формы frmDataViews. Переименуйте все элементы управления (за исключением надписей) так, чтобы их имена заканчивались цифрой 2, а не 1 (например, btnApply2).
Укажите значение DataView2 для свойства CaptionText сетки DataGrid2.
Окончательный вид формы frmDataViews показан на рис. 7.3, а ее код представлен в листинге 7.6.
Листинг 7.6. Код создания двух объектов-сеток для отображения разных представлений одной таблицы данных
Imports System
Imports System.Data
Imports System.Data.SqlClient
Public Class frmDataViews
Inherits System.Windows.Forms.Form
"Windows Form Designer generated code"
Private dsCustomers As New DataSet()
Private dvView2 As DataView
Private Sub frmDataViews_Load(ByVai sender As System.Object, _ ByVai e As System.EventArgs) Handles MyBase.Load
Dim i As Integer
Dim col As DataColumn
' Инициализация объекта DataAdapter.
Dim daCustomers As SqlDataAdapter = New
SqlDataAdapter("select * from tblCustomer", _ "server=localhost;uid=sa;database=novelty")
' Вставка данных только в ОДНУ таблицу.
daCustomers. Fill (dsCustomers, "Customers’”)
' Создание второго объекта DataView.
dvView2 = New DataView(dsCustomers.Tables("Customers") , _
"", "LastName", DataViewRowState.CurrentRows)
' Вставка списка из имен полей.
For Each col In dsCustomers.Tables("Customers").Columns
cboSortl.Items.Add(col.ColumnName)
cboSort2.Items.Add(col.ColumnName)
Next
Вставка объекта DataViewRowState.
Dim names As String()
names = DataViewRow-
State .None.GetNames(DataViewRowState.None.GetType)
For i = 0 To names.GetUpperBound(0)
cboRowStatel.Items.Add(names(i) )
cboRowState2.Items.Add(names(i))
Next
' Указание значений по умолчанию. = =
273
cboSortl.Selectedltem = "ID"
cboSort2.Selectedltem = "ID" chkDescl.Checked = False chkDesc2.Checked = False cboRowStatel.Selectedltem = cboRowState2.Selectedltem = "CurrentRows"
dsCustomers.Tables("Customers").Defaultview.Sort = "ID" dvView2.Sort = "ID"
' Связывание сеток данных с таблицей. DataGridl.DataSource = DataGrid2.DataSource = dvView2
End Sub
Private Sub btnApplyl_Click(ByVai sender As System.Object, _
ByVai e As System.EventArgs) Handles btnApplyl.Click
Dim sort As String
Dim rowState As DataViewRowState
' Указание фильтра.
= txtFilterl.Text
' Указание сортировки.
sort = cboSortl.Selectedltem
If chkDescl.Checked Then
sort = sort & " DESC"
End If
dsCustomers.Tables("Customers").Defaultview.Sort = sort
' Указание состояния записи.
=
rowState.Parse(rowstate.GetType, cboRow-
Statel .Selectedltem)
End Sub
Private Sub btnApply2_Click(ByVai sender As System.Object, _ ByVal e As System.EventArgs) Handles btnApply2.Click
Dim sort As String
Dim rowState As DataViewRowState
' Указание фильтра. dvView2.RowFilter =
' Указание сортировки. sort = cboSort2.Selectedltem If chkDesc2.Checked Then sort = sort & DESC" End If dvView2.Sort = sort
' Указание состояние записи.
dvView2.RowStateFilter = cboRowState2.Selectedltem) End Sub
End Class
Глава 7 274
Подпрограмма frmDataViews_Load инициализирует разные объекты формы. Сначала создается объект DataAdapter, который затем используется для загрузки данных из таблицы базы данных в таблицу Customers набора данных DataSet. Далее создаются два представления данных: одно будет содержать создаваемое по умолчанию представление, а другое - новое представление dvView2. Далее объект- представление dvView2 инициализируется для отображения всех текущих записей с сортировкой по фамилии, т.е. по полю LastName.
После этого инициализируются два набора полей со списками. В поле со списком cboSort загружается список имен полей таблицы Customers, а в поле со списком cboRowState — список значений перечисления DataViewRowState.
НА ЗАМЕТКУ
В Visual Basic .NET больше не поддерживается свойство ItemData. Дело в том, что вместо него для преобразования значений перечисления в строки ДЛЯ загрузки их в поле со списком используется метод GetNames перечисления. Аналогично, метод Parse перечисления используется для преобразования строк в значения перечисления при присвоении избранныхзначений свойству RowStateFilter.
Затем для элементов управления с критериями отбора записей задаются значения по умолчанию, т.е. исходный порядок сортировки для обоих представлений задается по полю ID. Наконец, каждое представление связывается с одной из сеток данных, что приводит к отображению в них всех текущих данных.
Избранные значения критериев применяются для соответствующего представления после щелчка на кнопке Apply. Две подпрограммы btnApplyl_Click и btnApply2_ Click идентичны, за исключением того, что они относятся к разным наборам элементов управления. Фильтр записей (значение свойства RowFilter) задается на основании указанного текста в текстовом поле txtFilter, порядок сортировки (значение свойства Sort) — на основании выбранного поля в поле со списком cboSort (с дополнительным модификатором DESC нисходящего порядка), а фильтр состояния записи (значение свойства RowStateFilter) — на основании значения в поле со списком cboRowState2. Изменение свойств представлений, связанных с сетками данных, приводит к автоматическому отображению данных с новыми параметрами представления.
Скомпонуем проект DataSetCode и запустим полученное приложение. Щелкните на кнопке Data Views для отображения новой формы frmDataViews. Внесите необходимые изменения в критерии сетки и щелкните на кнопке Apply для внесения этих изменений в соответствующую сетку. На рис. 7.4 показан пример отображения данных из одной таблицы в двух разных представлениях.
Попробуйте выбрать разные поля сортировки и состояния записей для организации разных представлений. Попробуйте использовать разные фильтры, например сложные выражения ID > 10 AND ID < 18 или LastName Like с. Более подробные сведения о правилах создания фильтров приводятся в разделе с описанием свойства DataColumn . Expression справки Visual Studio .NET.
Кроме того, сетки автоматически поддерживают операции вставки, изменения и удаления записей, поэтому попробуйте отредактировать записи и выбрать соответствующее состояние для отображения только измененных записей.
275
РИС. 7.4. Пример отображения данных из одной таблицы в двух разных представлениях формы frmData Views
НА ЗАМЕТКУ :
Обратите внимание, как разные представления одной таблицы данных отображают- j ся в сетках данных. Если вставить, изменить или удалить запись в одном представ- i лении (и применить изменение за счет перехода к следующей записи), то внесенные изменения будут автоматически отображены в другом представлении (если, конечно, эта запись соответствует заданному фильтру).
Бизнес-ситуация 7.1: просмотр данных
из разных источников
Когда компания Jones Novelties, Inc. приняла решение создать новую систему обработки данных, она уже имела прежнюю систему на основе базы данных Access, в которой директор компании Брэд Джонс хранил данные о заказах. Для директора очевидны преимущества перехода к новой системе на основе СУБД SQL Server, но прежде всего он хотел бы гарантировать сохранность унаследованных данных. Поэтому было решено вносить все изменения постепенно, особенно для сохранения данных компании запрошлые годы.
Итак, Джонс хотел бы создать новую систему на основе SQL Server, но иногда использовать старые данные из базы данных Access до тех пор, пока не будет закончена работа над новой системой. Поэтому часть данных должна храниться в одной базе
276
данных, а часть — в другой. Две базы данных должны совместно использовать и даже объединять свои данные, хотя они относятся к разным типам баз данных.
К счастью, это требование легко удовлетворяется в модели ADO.NET. Как уже описывалось выше в главе, для объекта DataSet совершенно не имеет значения, откуда, т.е. из каких источников, берутся данные. Поэтому разработчик базы данных может создать приложение, загружая данные о заказах из таблицы tblOrder базы данных Access, а данные о клиентах— из таблицы tblCustomer базы данных SQL Server. В дальнейшем, после создания в базе данных SQL Server таблицы tblOrder, достаточно будет только изменить строку подключения в объекте DataAdapter, и приложение будет функционировать, как и прежде.
Попробуем создать форму, которая демонстрирует описанный способ работы с данными из разных источников.
Запустите интегрированную среду разработки Visual Studio .NET.
Создайте новый проект Visual Basic Windows Application. Для этого в диалоговом окне New Project (Новый проект) выберите тип проекта Visual Basic Project в области Project Types (Типы проектов), а затем шаблон Windows Application (Приложение Windows) в области Templates (Шаблоны).
Назовите проектBusinessCase71 .
Укажите путь к файлам проекта.
Увеличьте размер формы Forml.
В окне Properties укажите значение frmShowOrders для свойства (Name) и значение Show Orders для свойства Text формы Forml.
В верхнем левом углу формы создайте текстовое поле и укажите значение txtCustomerlD для его свойства (Name), создайте кнопку и укажите значение btnFinds для ее свойства (Name) и значение Find для ее свойства Text, создайте поле со списком и укажите значение IstCustomer для его свойства (Name). В правой части формы создайте сетку данных и укажите значение grdOrders для ее свойства (Name).
Расположите все эти элементы управления так, как показано на рис. 7.5.
Перетащите в форму из панели элементов управления компонент DataSet и укажите значение dsCustOrders для его свойства (Name).
Создайте новый объект SqlDataAdapter для таблицы tblCustomers базы данных Novelty СУБД SQL Server, перетаскивая его из панели элементов управления. После запуска программы-мастера DataAdapterConfiguration Wizard выберите подключение к базе данных Novelty. Затем в диалоговом окне Choose a Query Туре выберите переключатель Use SQL statements.
В диалоговом окне Generate the SQL statements укажите команду SELECT * FROM tblCustomer для загрузки данных в объект — набор данных.
По окончании работы с программой-мастером Data Adapter Configuration Wizard укажите значение daCustomers для свойства (Name) для созданного объекта — адаптера данных.
277
РИС. 7.5. Расположение элементовуправленияв формеfrmShowOrders
Для таблицы tblOrder, которая находится в базе данных Novelty.MDB Access создайте объект OledbDataAdapter, перетаскивая его из панели элементов управления. После запуска программы-мастера Data Adapter Configuration Wizard создайте новое подключение, щелкнув на кнопке New Connection. Затем в диалоговом окне Data Link Properties выберите вкладку Provider и провайдер Microsoft Jet 4.0 OLE DB Provider.
Затем выберите вкладку Connection и укажите путь к файлу базы данных Novelty.MDB.
Затем в диалоговом окне Choose a Query Type выберите переключатель Use SQL statements.
В диалоговом окне Generate the SQL statements укажите команду SELECT * FROM tblOrder для загрузки данных в объект — набор данных.
По окончании работы с программой-мастером Data Adapter Configuration Wizard укажите значение daOrders для свойства (Name) для созданного объекта — адаптера данных.
В верхней части файла введите следующий код:
Imports System
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.OleDb
Затем в определении класса формы frmOrders введите код из листинга 7.7.
1 Глава 7
278— :——
Листинг7.7. Код объединения данныхизисточниковданных разного типа
Private dvOrders As New DataView ()
Private Sub frmShowOrders_Load(ByVai sender As System.Object, _ ByVai e As System.EventArgs) Handles MyBase.Load
Dim rel As DataRelation
' Вставка таблиц в набор данных dsCustOrders.
daCustomers.Fill(dsCustOrders, "Customers")
daOrders.Fill(dsCustOrders, "Orders")
' Создание отношения между таблицами.
rel = dsCustOrders.Relations.Add("relCustOrders", _ dsCustOrders .'Tables ("Customers" ) .Columns ("ID" ) , dsCustOrders.Tables("Orders").Columns("CustomerID"))
' Указание первичного ключа для таблицы Customers.
Dim pk(0) As DataColumn
pk(0) = dsCustOrders.Tables("Customers").Columns("ID")
dsCustOrders.Tables("Customers").PrimaryKey = pk
Указание сортировки по умолчанию для метода Find.
dsCustOrders.Tables("Customers").Defaultview.Sort = "ID"
End Sub
Private Sub btnFind_Click(ByVai sender As System.Object, _
ByVal e As System.EventArgs) Handles btnFind.Click
Dim RowNum As Integer
Dim dvRow As DataRowView
Dim i As Integer
If IsNumeric(txtCustID.Text) Then RowNum = dsCustOrders.Tables("Customers"). _ Defaultview.Find(txtCustID.Text)
If RowNum <> -1 Then
dvRow = dsCustOrders.Tables("Customers").Defaultview(RowNum) ' Вставка в поле со списком имен полей из таблицы Customer.
IstCustomer.Items.Clear()
For i = 0 To dsCustOrders.Tables("Customers").Columns.Count - 1 IstCustomer.Items.Add(dvRow.Item(i))-
Next
=
"Orders for customer # " & txtCustID.Text
' Извлечение связанных дочерних записей для ' указанного клиента таблицы Customer.
dvOrders = dvRow.CreateChildView("relCustOrders")
grdOrders.DataSource = dvOrders
Else
MessageBox.Show( _
"CustomerlD not found - Please try again.")
"Клиент CustomerlD не найден - Попробуйте еще раз." txtCustID.Clear()
End If
Else
Beep ()
End If
End Sub
279
Все параметры источников данных задаются в подпрограмме frmShowOrders_ Load. Затем создаются две таблицы для набора данных DataSet и отношение DataRelation между ними. Наконец, задаются значения свойств PrimaryKey и Sort для таблицы Customers и его представления по умолчанию Defaultview, чтобы для поиска записей можно было использовать метод Find.
Щелчок на кнопке Find приводит к выполнению нескольких операций, которые определены в коде подпрограммы btnFind_Click. После проверки числового типа значения в поле txtCustID начинается поиск этого значения в представлении DefaultView таблицы Customers. Если указанное значение найдено, то в поле со списком DataRowView будут показаны значения всех полей искомой записи с заданным идентификатором из таблицы Customers, а в сетке данных справа будут показаны ее дочерние записи из таблицы Orders.
Обратите внимание, что в данном примере создано отношение между двумя таблицами из баз данных совершенно разного типа!
Скомпонуйте проект BusinessCase? и попробуйте найти родительскую запись в таблице Customers и ее дочерние записи из таблицы Orders, как показано на рис. 7.6.
РИС. 7.6. Пример представлениярезультатов в формеfrmShowOrders
НА ЗАМЕТКУ
Пусть вас не смущает, что сетка данных Orders (см. рис. 7.6) будет содержать поле с именем OrderAmount, а не поле Amount, как определено в таблице SQL Server. Дело в том, что данные о клиенте поступают из базы данных SQL Server, а данные о его заказах — из совершенно другой "унаследованной" базы данных Access типа MDB. Поэтому вполне естественно, что имена объектов в разных базах данных могут иметь различные имена.
Для исправления имени поля можно использовать приведенную ниже команду с предложением AS для изменения имени поля в данных, которые возвращаются для объекта daOrders с помощью команды SELECT.
SELECT CustomerID, ID, OrderAmount As Amount, OrderDate
FROM tblOrder
С течением времени необходимость комбинирования данных из разных источников будет расти, поскольку современные компании стремятся расширять круг своих партнеров и объединяются с другими компаниями, которые могут хранить и использовать свои бизнес-данные в совершенно разных форматах. В последнее время растет популярность формата XML, который позволяет очень просто и быстро переносить данные о заказах и продуктах между разными компаниями. Для объекта DataSet формат XML представляет собой еще один способ вставки данных в таблицы из разных источников данных. Более подробно способы вставки данных в объект DataSet в формате XML описываются в главе 10, “ADO.NET и XML".
Строго типизированные наборы данных
До сих пор обсуждались варианты использования нетипизированного объекта DataSet. Однако в модели ADO.NET и среде Visual Studio .NET предусмотрены способы создания типизированных объектов DataSet. Типизированный объект DataSet является производным от нетипизированного класса DataSet, но содержит объекты, методы, свойства и события, специфические для используемой схемы данных. Она определяется в файле схемы с расширением . xsd, а в среде Visual Studio .NET предусмотрены графические инструменты для генерации набора классов на основе этой схемы.
Поскольку типизированный объект DataSet является производным от нетипизи- рованного класса DataSet, он наследует все функции, свойства, методы и события, а значит, может использоваться в тех же ситуациях, где применяется нетипизирован- ный класс DataSet. Однако его элементы являются строго типизированными и предлагают дополнительные функции, что способствует повышению производительности и надежности.
Типизированные объекты DataSet, DataTable и DataRow являются специализированными для используемой схемы.
Типизированные объекты DataColumn и DataRelation предлагаются как специализированные именованные свойства, а не универсальные элементы коллекций.
Длятипизированныхобъектовможновыполнятьпроверкутиповвовремя
компиляции.
В редакторе кода в среде Visual Studio .NET предусмотрено использование средства IntelliSense.
Код имеет более краткую и читабельную форму.
281
Например, команда указания нового значения для поля таблицы будет иметь следующий вид:
dsCustomers.tables("Customers").Rows(row)("FirstName") =
Здесь row— это индекс в коллекции записей Rows таблицы Customers, FirstName— поле таблицы Customers, a NewValue— новое присваиваемое значение. При таком кодировании нетипизированного набора данных могут возникать разные ошибки. Соответствует ли тип нового значения NewValue типу поля, которому оно присваивается? Существует ли таблица Customers? Существует ли поле FirstName? Правильно ли указано имя поля или таблицы? При этом получить сообщение об ошибке можно будет только во время выполнения приложения.
Однако при использовании типизированного набора данных все свойства имеют специализированные значения для заданной схемы, а потому проверка типов выполняется еще во время создания приложения. Соответствующий код для типизированного набора данных DataSet с таблицей Customers будет иметь вид dsCustomers.Customers(row).FirstName = NewValue
Обратите внимание, что объект-таблица Customers теперь является специализированным свойством типизированного набора данных DataSet, а поле FirstName — специализированным свойством типизированного объекта Customers. Продемонстрируем кратко, как в редакторе кода среды Visual Studio .NET используется средство IntelliSense.
Вернемся к таблице Departments для демонстрации этого способа работы на примере проекта DataSetCode из главы 5, “ADO.NET: объект DataSet".
Откройте проект DataSetCode в среде Visual Studio .NET.
В форме под кнопкой Data Views создайте кнопку, перетаскивая ее из панели элементов управления.
В окне Properties укажите значение btnTypedDataSet для свойства (Name) и значение Typed DataSet для свойства Text этой кнопки.
Теперь нужно включить в проект используемую схему: либо обратиться к уже готовому файлу с расширением . xsd, либо создать новый с помощью компонентов вкладки Data панели элементов управления.
Вставьте в форму новый компонент SqlDataAdapter. В диалоговом окне программы-мастера Data Adapter Configuration Wizard выберите подключение к базе данных Novelty. Затем в диалоговом окне Choose a Query Type выберите переключатель Use SQL statements. В диалоговом окне Generate the SQL statements укажите команду SELECT * FROM tblDepartment для загрузки данных в объект — набор данных. По окончании работы с программой-мастером Data Adapter Configuration Wizard укажите значение daDepartments для свойства (Name) для созданного объекта — адаптера данных.
Откройте диалоговое окно Generate DataSet (Создать набор данных), выбрав команду меню Data^ Generate DataSet или щелкнув правой кнопкой мыши на форме и выбрав в контекстном меню команду Generate DataSet. В группе переключателей Choose a dataset (Выбрать набор данных) выберите переключатель New (Создать), введите в текстовом поле имя DepartmentDS, установите
флажок Add this dataset to the designer (Включить этот набор данных в приложение) и щелкните на кнопке ОК.
Укажите значение dsDepartments для свойства (Name) для созданного объекта — набора данных.
После выполнения этих действий генератор набора данных включит в проект файл схемы. xsd и файл. vb, который реализует
специализированные классы набора данных DataSet. Файлы отображаются только после щелчка на кнопке Show all files (Показать все файлы) в верхней части окна Solution Explorer.
Щелкните дважды на файле DepartmentDS . xsd в окне Solution Explorer для отображения диалогового окна редактирования схемы
Попробуйте внести небольшие изменения в имена элементов схемы. Вместо имени tblDepartment укажите в самом верхнем левом текстовом поле имя Departments, как показано на рис. 7.7.
РИС. 7.7. Схемав окне редактирования схемы
Щелкните правой кнопкой мыши на окне редактирования схемы и убедитесь в том, что в контекстном меню установлен флажок параметра Generate Dataset. Он устанавливается по умолчанию, но если он все же не установлен по какой-то причине, то установите его снова.
283
Сохраните схему и закройте окно редактирования схемы. В результате этих действий будет автоматически сгенерирован файл DepartmentsDS . vb.
НА ЗАМЕТКУ —
Для просмотра содержимого файла DepartmentsDS. vb откройте его в окне редактирования кода. Учтите, что для указанного имени таблицы Departments генератор кода в дополнение к уже имеющемуся типизированному объекту Depa rtment s DS. создаст следующие объекты:
DepartmentsDataTable
DepartmentsDataRow
DepartmentsRowChangeEvent
Теперь следует создать код для использования типизированного объекта Depart- mentsDS, который показан в листинге 7.8.
Листинг 7.8. Код отображения содержимого типизированного
Объекта DepartmentsDS
Private Sub btnTypedDataSet_Click(ByVai sender As System.Object, ByVai e As System.EventArgs) Handles btnTypedDataSet.Click
daDepartments.Fill(dsDepartments, "Departments") DisplayDepartments(dsDepartments)
End Sub
Private Sub DisplayDepartments(ByVai ds As DepartmentsDS)
Me.IstOutput.Items.Clear()
Me.IstOutput.Items.Add("DISPLAY TYPED DATASET")
Me.IstOutput.Items.Add("=====================")
Каждое поле теперь является свойством объекта DepartmentsDS.
Dim row As DepartmentsDS.DepartmentsRow
For Each row In ds.Departments.Rows
Me.IstOutput.Items.Add( _
ds.Departments.IDColumn.ColumnName _
&" :& row.ID.ToString)
Me.IstOutput.Items.Add( _
ds.Departments.DepartmentNameColumn.ColumnName _
&" :& row.DepartmentName)
Next
End Sub,
В подпрограмме btnTypedDataSet_Click сначала используется метод Fill объекта daDepartments для вставки данных в таблицу Department. Затем вызывается подпрограмма DisplayDepartments для отображения содержимого таблицы с помощью циклического обхода всех записей таблицы с указанием имени поля и значения в нем. Этот подход несколько отличается от описанных ранее.
284
Переменная row создается с указанием типа записи DepartmentsDS. DepartmentsRow, а не как универсальный объект DataRow.
Таблица Departments доступна как свойство объекта ds . Departments, а не как элемент коллекции таблиц Tables, т.е. ds .Tables("Departments").
Поля таблицы доступны как свойства объекта-таблицы, например ds. Departments.IDColumn и ds.Departments.DepartmentNameColumn, а не как элементы коллекции полей Columns, т.е. ds.Tables("Departments") . Columns ("ID") и ds - Tables ("Departments") .Columns ("DepartmentName").
Значения полей доступны как свойства объекта-записи, например row. ID и row. DepartmentName, а не как элементы коллекции значений Items объекта- записи Rows, т.е. row("ID") и row ("DepartmentName").
На рис. 7.8 показано, как типизированные свойства (например, DepartmentName) представлены в контекстном меню средства IntelliSense, которое ускоряет процесс создания кода.
РИС. 7.8. Контекстное меню средства IntelliSense со свойствами типизированного объекта DataSet
285
Резюме
В этой главе рассматривается несколько важных аспектов создания приложений баз данных в модели ADO.NET. Обработка параллельных попыток доступа к данным является важным компонентом многопользовательской системы, особенно при работе с набором данных в неподключенном режиме. Отображения таблиц и полей существенно улучшают читабельность и упрощают процесс сопровождения кода. С помощью объектов-представлений одни и те же данные можно представлять по-разному со своим собственным фильтром и параметрами сортировки. Наконец, строго типизированный объект — набор данных позволяет применять усовершенствованные функции и повышает читабельность кода при использовании в особых ситуациях.
Вопросы и ответы
Мне понятно, как определить нарушение параллельного доступа, но как его устранить?
Ответ на этот вопрос зависит от конкретного приложения. Во-первых, при обнаружении нарушения параллельного доступа можно повторно отправить запрос к базе данных и начать работу с начала. Во-вторых, можно позволить второму пользователю переопределить изменения, внесенные первым пользователем. В-третьих, право окончательного выбора можно предоставить пользователю, предлагая ему все три набора данных: исходные значения, текущие значения и измененные им значения. Наконец, можно предложить алгоритм или логические правила для определения оптимального варианта самим приложением. При этом определяющим фактором выбора может быть приоритет или роль пользователя (например, изменения, внесенные руководителем, имеют более высокий приоритет, чем внесенные рядовым сотрудником). В модели ADO.NET и среде Visual Studio .NET предусмотрены специальные инструменты для обнаружения нарушений при параллельном доступе к данным. Разработчику остается только выбрать наиболее подходящие для создаваемого приложения.
Всегда ли следует использовать типизированные наборы данных?
Как обычно, ответ на этот вопрос зависит от конкретного приложения. В данной главе описаны преимущества типизированных наборов данных, но иногда предпочтительнее использовать нетипизированные наборы данных. Это верно в тех случаях, когда схема набора данных не известна или может изменяться. Классическим примером такой ситуации является использование незапланированных запросов, которые создаются пользователем при работе с приложением. Кроме того, схема может находиться в XML-файле, содержимое которого не известно во время создания приложения и генерации типизированного набора данных.
Еще одним недостатком использования типизированных наборов данных является необходимость повторной генерации и компиляции типизированных объектов — наборов данных при изменении схемы данных.
Наконец, стандартный нетипизированный объект DataSet настолько прост в использовании, что он может служить альтернативным вариантом для работы с данными без применения какой-то определенной схемы. Дополнительными преимуществами объекта DataSet является возможность работы с данными в формате XML.
ГЛАВА
Работа с проектом базы данных в среде
Visual Studio .NET
В ЭТОЙ ГЛАВЕ...
Создание проекта базыданных
Ссылки на базы данных
Сценарии
Запросы
Резюме
Вопросы и ответы
Проект базы данных — это особый тип проекта Visual Studio .NET, предназначенный для создания и управления SQL-сценариями баз данных.
Чтобы разработка приложений для работы с базами данных была проще и удобнее, в последнюю версию Visual Studio .NET включено множество новых полезных инструментов, а также усовершенствованных прежних инструментов, которые присутствовали в ранних версиях программы.
Инструменты Visual Studio .NET для работы с базой данных позволяют пользователю, не выходя за пределы среды Visual Studio .NET, быстро создавать, изменять и тестировать объекты баз данных: таблицы, представления, запросы, хранимые процедуры и т.д. Главное достоинство данного продукта состоит в существенном повышении эффективности проектирования и создания программных продуктов. Кроме того, значительно упростилась установка и лицензирование программы.
Некоторые из рассмотренных далее инструментов описаны в предыдущих главах, а их использование часто имеет интуитивно понятный характер. Многие инструменты должны быть хорошо известны пользователям, которые имеют опыт работы с базами данных Microsoft Access. Тем же, кто сталкивается с такими инструментами впервые, будет полезно познакомиться со справочными материалами Visual Studio .NET.
В этой главе описываются способы применения инструментов проекта базы данных Visual Studio .NETдля работы с базами данных.
Создание проекта базы данных
Проект базы данных Visual Studio .NET предназначен для разработки, тестирования и выполнения сценариев и запросов SQL, а также для управления ими. Этот проект не связан напрямую ни с одним из .NET-совместимыхязыков программирования, например Visual Basic или c#. При разработке многоуровневого приложения данный тип проекта упрощает процесс проектирования и создания уровня базы данных, а также управления им.
В одних приложениях проект базы данных может быть частью кода приложения, а в других — отдельным администрирующим решением, которое используется для установки и сопровождения базы данных создаваемого приложения.
Для включения проекта базы данных в решение Visual Studio .NET выполните перечисленные ниже действия.
Откройте диалоговое окно Add New Project (Создать новый проект), выбрав команду меню File^Add Project^New Project (Файл^Создать проекта Новый проект) или щелкнув правой кнопкой мыши на названии решения в окне Solution Explorer и выбрав в контекстном меню команду Add^New Project (Создать^ Ho- вый проект).
В левой части Project Types (Типы проектов) диалогового окна Add New Project найдите каталог Other Projects (Другие проекты) и раскройте его, щелкнув на пиктограмме с изображением знака "плюс".
Выберите каталог Database Projects (Проекты баз данных), а затем выберите шаблон Database Project (Проект баз данных) в правой части окна Templates (Шаблоны).
289
В текстовом поле Name (Имя) укажите имя проекта NoveltyData, в текстовом поле Location (Расположение) укажите путь к файлам проекта, а затем щелкните на кнопке ОК.
На экране отобразится диалоговое окно SQL Server Login (Регистрация в SQL Server) с предложением выбрать используемый сервер баз данных и ввести учетное имя и пароль. В списке Server (Сервер) выберите сервер (local) и yc- тановите флажок параметра Trusted Connection (Доверительное подключение).
Если ранее в окне Server Explorer не были определены никакие подключения к базе данных, то на экране отобразится диалоговое окно Data Link Properties (Свойства подключения к базе данных), в котором можно определить новое подключение к базе данных.
Если в окне Server Explorer уже определено хотя бы одно подключение к базе данных, то на экране появится диалоговое окно Add Database Reference (Создать ссылку на базу данных). В списке имеющихся ссылок выберите нужную ссылку или щелкните на кнопке Add New Reference (Создать новую ссылку), чтобы открыть диалоговое окно Data Link Properties (Свойства подключения к базе данных), в котором можно определить новое подключение к базе данных.
В списке имеющихся ссылок выберите ссылку на базу данных Novelty и щелкните на кнопке ОК. (Если этой ссылки все же нет, ее придется создать, щелкнув на кнопке Add New Reference.)
На рис. 8.1 показан итоговый вид созданного проекта и его подчиненных папок в окнеSolution Explorer.
РИС. 8.1. Итоговый вид проекта NoveltyData в окне Solution Explorer
Обратите внимание на то, что проект NoveltyData содержит следующие папки:
Change Scripts (Сценарии измененияданных);
Create Scripts (Сценарии создания данных);
Queries (Запросы);
Database References (Ссылки на базы данных).
Рассмотрим подробнее эти папки и их содержимое. Начнем с папки Database References, потому что с нее начинается любая работа с базами данных.
Ссылки на базы данных
Ссылка на базу данных является указателем на нее, но она не позволяет осуществлять доступ к объектам базы данных и их просмотр, так как за это отвечает подключение к базе данных.
Ссылка на базу данных — это хранимая на жестком диске часть проекта базы данных. При открытии проекта она автоматически считывается для учета всех определенных в окне Server Explorer подключений к базам данных и поиска соответствующего ссылке подключения. Если указанного подключения нет, то ссылка автоматически создает его.
Первая определенная ссылка на базу данных становится для данного проекта используемой по умолчанию. Поэтому все сценарии и запросы проекта по умолчанию будут выполняться именно для этой ссылки на базу данных.
НА ЗАМЕТКУ ——
Пиктограмма используемой по умолчанию ссылки на базу данных содержит красную, ' зеленую или белую стрелку ярлыка, а все пиктограммы всех других ссылок на базы 1 данных содержат черную илибелую стрелки.
Для указания другой ссылки в качестве используемой по умолчанию следует щелкнуть на нужной ссылке и в контекстном меню установить флажок Set as Project Default (Использовать по умолчанию).
Для создания новых ссылок на базы данных щелкните правой кнопкой мыши на папке Database References (Ссылки на базы данных), а затем выберите в контекстном меню команду New Database Reference (Создать ссылку на базу данных).
СОВЕТ ~|—
Можно одновременно создать ссылку на базу данных и указать ее в качестве используемой по умолчанию. Для этого щелкните правой кнопкой мыши на названии проекта базы данных в окне Solution Explorer и выберите в контекстном меню команду Set Default Reference (Указать используемую по умолчанию ссылку). На экране появится диалоговое окно Set Default Reference (Указать используемую по умолчанию ссылку). После выбора нового подключения к базе данных ссылка на него включается в проект ;и впредь используется по умолчанию.
Сценарии
Шаблон проекта базы данных автоматически создает две папки для хранения сценариев SQL. Папка Create Scripts (Сценарии создания данных) содержит сценарии SQL, которые отражают предполагаемые изменения в базе данных. Изменения могут быть отложены, так как разработчик не имеет достаточных полномочий для изменения рабочей базы данных (иногда это бывает весьма полезно) или несколько изменений предполагается объединить и внести одновременно.
Любой сценарий SQL можно создать вручную, щелкнув правой кнопкой мыши на имени папки (или имени проекта) в окне Solution Explorer, а затем выбрав в контекстном
меню команду Add New Item (Создать новый элемент) или Add SQL Script (Создать сценарий SQL). Выбрав один из стандартных шаблонов сценариев (рис. 8.2), их можно отредактировать вручную в редакторе Visual Studio .NET.
НА ЗАМЕТКУ
Хотя имена каталогов даются в соответствии с принятым соглашением об именах, их все же можно изменить. Однако при этом следует учитывать вероятность того, что это может запугать других разработчиков проекта.
В проект базы данных можно включить другие папки. Например, отдельную папку можно создать для хранения сценариев обновления базы данных до указанной версии или для других видов действий. Аналогично, можно создать разные папки для хранения запросов разного типа. Для создания новой папки в проекте базы данных щелкните правой кнопкой мыши на имени проекта и выберите в контекстном меню команду New Folder (Создать папку).
РИС. 8.2. Стандартные шаблоны сценариев SQL в диало го- вом окне Add New Item
Следует отметить, что сценарии создания и изменения данных могут создаваться автоматически.
НА ЗАМЕТКУ —;
Сценарии создания данных можно генерировать только в SQL Server 2000 или SQL Server 7.0. Более того, для их использования на компьютере с Visual Studio .NET нужно установить клиентские инструменты SQL Server.
Сценарии создания данных
Как уже упоминалось ранее, папка Create Scripts содержит сценарии создания новых объектов базы данных, включая таблицы, представления, хранимые процедуры и
ограничения. Обычно они используются для создания сценариев установки рабочей базы данных или возвращения тестовой базы данных к исходному состоянию.
Для генерации сценариев создания данных выполните перечисленные ниже действия.
Откройте окно Server Explorer и щелкните правой кнопкой мыши на элементе, для которого предполагается создать сценарий. Этим элементом может быть вся база данных или отдельный объект (таблица, представление, хранимая процедура или функция). В этом примере следует выбрать базу данных Novelty.
Выберите в контекстном меню команду Generate Create Script (Генерировать сценарий создания данных). При условии успешного ввода учетной записи и пароля на предыдущем этапе создания ссылки на эту базу данных на экране отобразится диалоговое окно Generate Create Scripts (Генерировать сценарии создания данных).
Диалоговое окно Generate Create Scripts будет сконфигурировано только для избранного объекта. Например, после выбора базы данных Novelty оно будет иметь такой вид, как на рис. 8.3, с отображением всех объектов этой базы данных, но еще без выбора отдельных объектов для работы с генерируемым сценарием.
Рис. 8.З. Диалоговое ОКНО Generate Create Scripts для базы данных Novelty
Во вкладке General (Общие) можно указать объекты, включаемые в генерируемый сценарий, выбрав один или несколько объектов в списке Objects on Novelty (Объекты базы данных Novelty) и щелкнув на кнопке Add (Включить в сценарий) либо дважды щелкнув на выбранном объекте. Для включения в сценарий целой группы объектов, например всех объектов одного типа, т.е. всех таблиц или всех представлений, нужно установить соответствующий флажок, т.е. Script all Objects (Включить все объекты), All tables (Все таблицы), All views (Все представления) и т.д. В данном примере выберите флажок Script all objects.
293293Выберите вкладку Formatting (Форматирование) и, помимо указанных по умолчанию параметров, установите флажок Include descriptive headers in the script files (Включить в сценарий описательные заголовки).Щелкните на кнопке ОК, и на экране появится диалоговое окно Browse for Folder (Выбор папки), где необходимо выбрать папку, в которой следует сохранить файл генерируемого сценария. Обратите внимание, что по умолчанию выбирается папка Create Scripts текущего проекта, хотя ее, конечно, можно выбрать по своему желанию. В данном примере используется предлагаемая по умолчанию папка Create Scripts. Для продолжения работы щелкните на кнопке ОК.Поскольку в диалоговом окне Generate CreateВыберите вкладку Formatting (Форматирование) и, помимо указанных по умолчанию параметров, установите флажок Include descriptive headers in the script files (Включить в сценарий описательные заголовки).Щелкните на кнопке ОК, и на экране появится диалоговое окно Browse for Folder (Выбор папки), где необходимо выбрать папку, в которой следует сохранить файл генерируемого сценария. Обратите внимание, что по умолчанию выбирается папка Create Scripts текущего проекта, хотя ее, конечно, можно выбрать по своему желанию. В данном примере используется предлагаемая по умолчанию папка Create Scripts. Для продолжения работы щелкните на кнопке ОК.Поскольку в диалоговом окне Generate CreateSolution Explorer Nov^ityCataSolution Explorer Nov^ityCataCd Create Scripts1® dbo.DeleteEmplOyee.prcdbo.EmployeeDepartment_view.vlw dbo.GetCustomerFromID.prc(1dbo. InsertEmployee. pre$5 dbo.InsertEmpioyeeOrQ.prc, sw dbo.LastNamelookup.prc $ш dbo.procEmployees5orted.prc■dbo.qryEmployee_vlew.viws® dbo.5electEmployees.prc•dbo.tblCustomer.extt® dbo.tblCustomer.fky s® dbo.tbICustomer.kci $® dbo.tWCustomer.tab ji dbo.ttiDeoartment.ext j5 dbo.tblDepartment.fky■ s{ 2 dbo.tblDepartment.hci,1 2 dbo.tblDepartment.tabdbo.tblEmpbye9.ext dbo.ttiEmployee.fky dbo.tblEmployee.kciCd Create Scripts1® dbo.DeleteEmplOyee.prcdbo.EmployeeDepartment_view.vlw dbo.GetCustomerFromID.prc(1dbo. InsertEmployee. pre$5 dbo.InsertEmpioyeeOrQ.prc, sw dbo.LastNamelookup.prc $ш dbo.procEmployees5orted.prc■dbo.qryEmployee_vlew.viws® dbo.5electEmployees.prc•dbo.tblCustomer.extt® dbo.tblCustomer.fky s® dbo.tbICustomer.kci $® dbo.tWCustomer.tab ji dbo.ttiDeoartment.ext j5 dbo.tblDepartment.fky■ s{ 2 dbo.tblDepartment.hci,1 2 dbo.tblDepartment.tabdbo.tblEmpbye9.ext dbo.ttiEmployee.fky dbo.tblEmployee.kciI^C.tbEmpIo^,^I^C.tbEmpIo^,^^Solution ExplorerJ ^ Од»? View^Solution ExplorerJ ^ Од»? ViewScriptsбыливыбранывсеобъектыбазыдан-РИС. 8.4.Итоговыйвидокнаных, то в проект базы данных будет включено Solution Explorer с файлами сце- несколько файлов сценария, как показано на нариев для базы данных Novelty рис. 8.4. Учтите, что данный сценарий можновключить в один файл. Для этого нужно во вкладке Options (Параметры) диалогового окна Generate Create Scripts выбрать в группе Files to Generate (Генерировать файлы) переключатель Create one file (Создать один файл).Scriptsбыливыбранывсеобъектыбазыдан-РИС. 8.4.Итоговыйвидокнаных, то в проект базы данных будет включено Solution Explorer с файлами сце- несколько файлов сценария, как показано на нариев для базы данных Novelty рис. 8.4. Учтите, что данный сценарий можновключить в один файл. Для этого нужно во вкладке Options (Параметры) диалогового окна Generate Create Scripts выбрать в группе Files to Generate (Генерировать файлы) переключатель Create one file (Создать один файл).
293
293
Выберите вкладку Formatting (Форматирование) и, помимо указанных по умолчанию параметров, установите флажок Include descriptive headers in the script files (Включить в сценарий описательные заголовки).
Щелкните на кнопке ОК, и на экране появится диалоговое окно Browse for Folder (Выбор папки), где необходимо выбрать папку, в которой следует сохранить файл генерируемого сценария. Обратите внимание, что по умолчанию выбирается папка Create Scripts текущего проекта, хотя ее, конечно, можно выбрать по своему желанию. В данном примере используется предлагаемая по умолчанию папка Create Scripts. Для продолжения работы щелкните на кнопке ОК.
Поскольку в диалоговом окне Generate Create
Выберите вкладку Formatting (Форматирование) и, помимо указанных по умолчанию параметров, установите флажок Include descriptive headers in the script files (Включить в сценарий описательные заголовки).
Щелкните на кнопке ОК, и на экране появится диалоговое окно Browse for Folder (Выбор папки), где необходимо выбрать папку, в которой следует сохранить файл генерируемого сценария. Обратите внимание, что по умолчанию выбирается папка Create Scripts текущего проекта, хотя ее, конечно, можно выбрать по своему желанию. В данном примере используется предлагаемая по умолчанию папка Create Scripts. Для продолжения работы щелкните на кнопке ОК.
Поскольку в диалоговом окне Generate Create
Solution Explorer Nov^ityCata
Solution Explorer Nov^ityCata
Cd Create Scripts
1® dbo.DeleteEmplOyee.prc
dbo.EmployeeDepartment_view.vlw dbo.GetCustomerFromID.prc
(1dbo. InsertEmployee. pre
$5 dbo.InsertEmpioyeeOrQ.prc
, sw dbo.LastNamelookup.prc $ш dbo.procEmployees5orted.prc
■dbo.qryEmployee_vlew.viw
s® dbo.5electEmployees.prc
•dbo.tblCustomer.ext
t® dbo.tblCustomer.fky s® dbo.tbICustomer.kci $® dbo.tWCustomer.tab ji dbo.ttiDeoartment.ext j5 dbo.tblDepartment.fky
■ s{ 2 dbo.tblDepartment.hci
,1 2 dbo.tblDepartment.tab
dbo.tblEmpbye9.ext dbo.ttiEmployee.fky dbo.tblEmployee.kci
Cd Create Scripts
1® dbo.DeleteEmplOyee.prc
dbo.EmployeeDepartment_view.vlw dbo.GetCustomerFromID.prc
(1dbo. InsertEmployee. pre
$5 dbo.InsertEmpioyeeOrQ.prc
, sw dbo.LastNamelookup.prc $ш dbo.procEmployees5orted.prc
■dbo.qryEmployee_vlew.viw
s® dbo.5electEmployees.prc
•dbo.tblCustomer.ext
t® dbo.tblCustomer.fky s® dbo.tbICustomer.kci $® dbo.tWCustomer.tab ji dbo.ttiDeoartment.ext j5 dbo.tblDepartment.fky
■ s{ 2 dbo.tblDepartment.hci
,1 2 dbo.tblDepartment.tab
dbo.tblEmpbye9.ext dbo.ttiEmployee.fky dbo.tblEmployee.kci
I^C.tbEmpIo^,^
I^C.tbEmpIo^,^
^Solution ExplorerJ ^ Од»? View
^Solution ExplorerJ ^ Од»? View
Scriptsбыливыбранывсеобъектыбазыдан-РИС. 8.4.Итоговыйвидокна
ных, то в проект базы данных будет включено Solution Explorer с файлами сце- несколько файлов сценария, как показано на нариев для базы данных Novelty рис. 8.4. Учтите, что данный сценарий можно
включить в один файл. Для этого нужно во вкладке Options (Параметры) диалогового окна Generate Create Scripts выбрать в группе Files to Generate (Генерировать файлы) переключатель Create one file (Создать один файл).
Scriptsбыливыбранывсеобъектыбазыдан-РИС. 8.4.Итоговыйвидокна
ных, то в проект базы данных будет включено Solution Explorer с файлами сце- несколько файлов сценария, как показано на нариев для базы данных Novelty рис. 8.4. Учтите, что данный сценарий можно
включить в один файл. Для этого нужно во вкладке Options (Параметры) диалогового окна Generate Create Scripts выбрать в группе Files to Generate (Генерировать файлы) переключатель Create one file (Создать один файл).
СОВЁТ —
Устанавливая флажок Script database (Сгенерировать сценарий базы данных) во вкладке Options диалогового окна Generate Create Scripts, можно даже сгенерировать сценарий создания самой базы данных. в этом примере проект базы данных NoveltyData содержит набор сценариев для создания всех объектов базы данных Novelty. Cnoco- бы применения этих сценариев демонстрируются в следующих разделах.
Для просмотра (или изменения) содержимого файла сценария дважды щелкните на нем в окне Solution Explorer. На рис. 8.5 показано содержимое сценария dbo.tblOrder. tab, который создает таблицу tblOrder в базе данных Novelty.
Обратите внимание на то, что эти сценарии создают только схему базы данных, но не вставляют в созданные таблицы никаких данных. Способы копирования данных в таблицы будут показаны далее в разделе о командных файлах.
Сценарииизмененияданных
Для внесения изменений в существующую схему базы данных используются сценарии изменения данных. Хотя они могут создаваться вручную, все же предпочтительнее для этого использовать специальные инструменты. В главах 1, "Основы построения баз данных", и 2, "Запросы и команды на языке SQL", рассматриваются инструменты Visual
294
Studio .NET, предназначенные для работы с базой данных. Они способны автоматически создавать сценарий для любых изменений схемы базы данных.
РИС. 8.5. Содержимое сгенерированного сценария создания таблицы tblOrder
Допустим, требуется создать новое поле StartDate в таблице tblEmployee для даты зачисления каждого сотрудника. Для этого выполните перечисленные ниже действия.
В окне Server Explorer раскройте папку Tables базы данных Novelty и щелкните правой кнопкой мыши на таблице tblEmployee.
Выберите в контекстном меню команду Design Table (Создание таблицы) для перехода в режим конструктора таблицы.
Создайте новое поле StartDate и укажите для него тип данных datetime, как показано на рис. 8.6.
Чтобы внести данное поле во все уже инсталлированные и доставленные базы данных в географически разных местах, потребуется создать сценарий изменений. Для этого выполните перечисленные ниже действия.
Выберите команду меню Diagram^Generate Change Script (Диаграмма^Генера- ция сценария изменения) или щелкните на кнопке Generate Change Script (Генерация сценария изменения) в панели инструментов. На экране появится
295
диалоговое окно Save Change Script (Сохранение сценария изменения) с областью предварительного просмотра текста сценария (рис. 8.7).
РИС. 8.6. Создание нового поля Star Da te в таблице tblEmployee
РИС. 8.7. Диалоговое окна Save Change Script с CO- держимым сценария вставки поля StartDate в таблиг^ tblEmployee
Щелкните на кнопке Yes, и на экране появится стандартное диалоговое окно Save As (Сохранить как) с предложением сохранить данный сценарий в папке Change Scripts текущего проекта базы данных.
Щелкните на кнопке Save для сохранения сценария с принимаемым по умолчанию именем tblEmployee . sql.
Закройте режим конструктора таблицы и в ответ на предложение сохранить внесенные изменения щелкните на кнопке No. В следующем разделе описываются способы внесения изменений с помощью запуска только что созданного сценария SQL.
Щелкните дважды на сценарии tblEmployee . sql для просмотра содержимого сохраненного файл сценария, которое показано на рис. 8.8.
Friday, February 08, 2002 4:17:29 ЕМ
User: sa
Server: HP426
Database: Novelty
Application:
BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
ЗЕТ CONCAT_NUL L_YIBLD 3_NULL ON
SET AN3I_NULLS ON
SET AN3I_PADDING ON
SET AN3I_WARNING3 ON
COMMIT
BEGIN TRANSACTION
ALTEP. TABLE dbo.tblEmployee ADD StartDate datetime HULL
GO COMMIT
РИС. 8.8. Просмотр содержимого файла сценария tblEmployee. sql
(Учтите, что вместо учетного имени sa в строке User: sa может быть указано другое используемое вами учетное имя пользователя SQL Server, а вместо имени сервера HR426 в строке Server: HR42 б — имя другого используемого вами сервера. — Прим. ред.)
Запуск сценария
Сценарий можно запустить непосредственно в окне Solution Explorer. Проще всего перетащить сценарий и опустить его на ссылку на базу данных, в которую нужно внести изменения. Или иначе, щелкните правой кнопкой мыши на сценарии и выберите в кон-
297
текстном меню команду Run (Запустить) или Run On (Запустить для). Команда Run выполняет сценарий по отношению к используемой по умолчанию ссылке на базу данных, а команда Run On позволяет выбрать другую базу данных в одноименном диалоговом окне (рис. 8.9). Учтите, что, кроме выбора уже существующей ссылки на базу данных, можно создать совершенно новую ссылку. (Перед этим на экране появится диалоговое окно SQL Server Login для выбора сервера и регистрации на нем. — Прим. ред.)
РИС. 8.9. Диалоговое окно Run On
НА ЗАМЕТКУ :
Для запуска сценария можно выбрать временную ссылку на базу данных. Щелкните дважды на последнем элементе ctemporary reference> списка ссылок для отображения уже знакомого диалогового окна Data Link Properties, предназначенного для определения новой ссылки на базу данных. Однако эта ссылка не обязательно ; должна быть включена в проект или окно Server Explorer.
Для внесения изменений в базу данных с помощью сценария tblEmployee.sql выполните перечисленные ниже действия.
Убедитесь в том, что таблица tblEmployee не содержит поле StartDate. Для этого в окне Server Explorer раскройте папку таблицы tblEmployee для просмотра всех полей данной таблицы, как показано на рис. 8.10.
Раскройте папку Change Scripts в окне Solution Explorer и выберите сценарий tblEmployee.sql.
В окне Solution Explorer перетащите и опустите данный сценарий на ссылку на базу данных Novelty.
После этого на экране появится диалоговое окно Execute Scripts and Queries (Выполнить сценарии или запросы) для подтверждения запуска сценария для указанной базы данных. Щелкните на кнопке Yes для запуска сценария и продолжения работы.
Повторите действия, описанные в п. 1, чтобы вновь отобразить поля таблицы tblEmployee (или просто щелкните на кнопке Refresh в панели инструментов окна Server Explorer) для того, чтобы убедиться в наличии нового поля StartDate в таблице tblEmployee.
Set vet Explore»Set vet Explore»
Set vet Explore»
Set vet Explore»
• Data Connections
s % HP426.Movety.dbo ffi -Database Diagrams
& Г® Tables !
: ®" Q ttiOepartment
> I Ф • О S33ESS53 ffl ID
•
S
• ffl
|. ® A tbUnventory
ф-
i Й □ tbIOrderltem
Views
Stored Procedures Functions
ffl ^ Servers
РИС. Отображение полей таблицы
tblEmployee
Командные файлы \
Создав большое количество сценариев создания и изменения разных объектов базы данных, неплохо объединить их в одной логической группе для одновременного запуска. Именно для этого в Visual Studio предусмотрены командные файлы с расширением . Они могут использоваться в операционных системах Windows 2000 или Windows XP, которые способны распознать такие файлы. Например, командный файл можно применить для загрузки данных во вновь созданную таблицу, экспортируя данные из уже существующей базы данных.
НА ЗАМЕТКУ
Способность простого и автоматического создания сценария загрузки данных, по; мимо создания схемы и объектов базы данных, является именно тем компонентом , среды Visual Studioкоторого нет в программе SQL Server Enterprise Manager.
-•.
Итак, попробуем создать один командный файл, чтобы автоматически запустить все сценарии для создания совершенно новой версии базы данных Novelty на другом компьютере. Хотя новая версия базы данных будет иметь собственные данные о клиентах, сотрудниках и заказах, данные о товарах в таблицебудут оди
наковыми. Для этого в новой базе данных нужно наполнить таблицу tbl Inventory данными о товарах из уже существующей таблицы
Чтобы создать командный файл загрузки данных о товарах из существующей базы данных в новую, необходимо сначала экспортировать данные, а затем продолжить процесс создания командного файла.
299
В окне Server Explorer щелкните правой кнопкой мыши на таблице tbllnventory и выберите в контекстном меню команду Export Data (Экспорт данных).
В диалоговом окне Browse for Folder выберите предлагаемую по умолчанию папку Create Scripts проекта базы данных и щелкните на кнопке ОК.
Послерегистрациив диалоговомокнеSQLServerLogin будетсозданфайл
dbo.tbllnventory.dat.
Выберите папку в проекте базы данных для сохранения нового командного файла. В данном примере для этого используется предлагаемая по умолчанию папка Create Scripts.
В окне Solution Explorer щелкните правой кнопкой мыши на папке Create Scripts и выберите в контекстном меню команду Create Command File (Создать командный файл). На экране появится диалоговое окно Create Command File (рис. 8.11).
Рис. 8.11. Диалоговое окно Create Command File для
базы данных Novel ty
В списке Available Scripts (Доступные сценарии) в диалоговом окне Create Command File перечислены все сценарии SQL в выбранной папке, которые можно включить в командный файл. В список Scripts to be added to the command file (Сценарии, включаемые в командный файл) можно вставить все или только некоторые сценарии. Для включения всех сценариев щелкните на кнопке Add АН(Включитьвсе).
Поскольку в списке сценариев находится по крайней мере один сценарий экспорта данных с расширением . tab, в диалоговом окне Create Command File станет активной кнопка Add Data (Включить данные).
Щелкните на кнопке Add Data, и на экране отобразится одноименное диалоговое окно (рис. 8.12). В этом окне перечислены все выбранные сценарии создания таблиц и соответствующие им файлы данных.
В этом примере автоматически задается соответствие между файлом данных dbo.Inventory.dat и сценарием dbo. Inventory . tab. Для возвращения в диалоговоеокноCreateCommandFileщелкнитенакнопкеОК.
После указания сценариев и файлов данных щелкните на кнопке ОК для создания командного файла. В результате этого в папке Create Scripts появится файл Create Scripts . cmd, содержимое которого показано в листинге 8.1.
ЛИСТИНГ 8.1. Содержимое КОМаНДНОГО файла Create Scripts . cmd
@echo off
REM: Command File Created by Microsoft Visual Database Tools
REM: Date Generated: 8/29/2002
REM: Authentication type: Windows NT
REM: Usage: CommandFilename [Server] [Database]
i f '%!'== ii goto usage
.: f: '%2' == " goto usage
if ' %1' ==goto usage
if • % 1 • == '-?' goto usage
if ' %1 * ==goto usage
if * %1 ’ ==goto usage
osql -S %1 -d Master -E -b -i "JACKIE-TECRA.Novelty .DBS"
if %ERRORLEVEL% NEQ 0 goto errors
osql —S %1 —d Master -E -b -i "Novelty.DBS"
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 -d %2 -E -b -i "dbo.tblCustomer.tab"
if %ERRORLEVEL% NEQ 0 goto errors
bcp "%2.dbo.tblCustomer" in "dbo.tblCustomer.dat" -S %1 -T -k -n - q
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 - d %2 - E -b -i "dbo . tblDepartment . tab"
if %ERRORLEVEL% NEQ 0 goto errors
bcp "%2. dbo. tblDepartment" in "dbo.tblDepartment.dat" -S %1 - T -k — n — q
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 — d %2 - E -b -i "dbo.tblEmployee .tab"
if %ERRORLEVEL% NEQ 0 goto errors
bcp "%2 .dbo.tblEmployee" in "dbo.tblEmployee.dat"— S %1 - T -k — n -q
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 -d %2 -E -b -i "dbo.tblInventory.tab"
if %ERRORLEVEL% NEQ 0 goto errors
bcp "%2 . dbo.tblInventory" in "dbo.tbllnventory.dat" -S%1- T -k -- n — q
if %ERRORLEVEL% NEQ 0 goto errors
osql - S %1 — d %2 - E - b -i "dbo.tblOrder.tab"
if %ERRORLEVEL% NEQ 0 goto errors
301
bcp"%2.dbo.tblOrder"in"dbo.tblOrder.dat"-S%1-T-k-n-q
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 -d %2 -E -b -i "dbo.tblOrderItem.tab"
if %ERRORLEVEL% NEQ 0 goto errors
bcp " %2.dbo.tblOrderItem" in "dbo.tblOrderItem.dat" -S %1 -T -k -n -q
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 —d %2 -E —b -i "dbo.tblRegion.tab"
if %ERRORLEVEL% NEQ 0 goto errors
bcp "%2.dbo.tblRegion" in "dbo.tblRegion.dat" -S %1 -T -k -n -q
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 —d %2 —E —b —i "dbo.tblCustomer.kci"
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 -d %2 —E —b -i "dbo.tblDepartment.kci"
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 -d %2 -E —b -i "dbo.tblEmployee.kci"
if %ERRORLEVEL% NEQ 0 goto errors
osql —S %1 -d %2 -E -b -i "dbo.tblInventory.kci"
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 -d %2 -E -b -i "dbo.tblOrder.kci"
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 -d %2 -E -b -i "dbo.tblOrderItem.kci"
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 -d %2 -E -b -i "dbo. tblRegion. kci"
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 -d %2 -E -b -i "dbo.tblCustomer.fky"
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 -d %2 -E -b -i "dbo.tblDepartment.fky"
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 -d %2 -E -b -i "dbo.tblEmployee.fky"
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 -d %2 -E -b -i "dbo.tbllnventory.fky"
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 -d %2 -E -b -i "dbo.tblOrder. fky"
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 -d %2 -E -b -i "dbo.tblOrderltem.fky"
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 -d %2 -E -b -i "dbo.tblRegion.fky"
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 -d %2 -E -b -i "dbo.tblCustomer.ext"
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 -d %2 -E -b -i "dbo.tblDepartment.ext"
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 -d %2 —E —b —i "dbo.tblEmployee.ext"
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 -d %2 -E -b -i "dbo.tblInventory.ext"
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 -d %2 -E -b -i "dbo.tblOrder.ext"
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 -d %2 -E -b -i "dbo.tblOrderltem.ext"
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 -d %2 -E -b -i "dbo.tblRegion.ext"
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 —d %2 -E —b —i "dbo.EmployeeDepartment_view.viw"
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 -d %2 —E —b -i "dbo.qryEmployee_view.viw"
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 -d % 2 —E -b -i "dbo.DeleteEmployee.pre"
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 -d %2 -E -b -i "dbo.GetCustomerFromID.pre"
if %ERRORLEVEL% NEQ 0 goto errors
osql -S %1 -d %2 -E -b -i "dbo.InsertEmployee.pre"
ifNEQosql -S %1%2ifNEQosql%1%2if %ERRORLEVEL% NEQosql -S %1ifNEQosql%1-EifNEQosql -S%2ifNEQifNEQosql -S %1%2ifNEQosql%1%2if %ERRORLEVEL% NEQosql -S %1ifNEQosql%1-EifNEQosql -S%2ifNEQ0 goto errors
ifNEQ
osql -S %1%2
ifNEQ
osql%1%2
if %ERRORLEVEL% NEQ
osql -S %1
ifNEQ
osql%1-E
ifNEQ
osql -S%2
ifNEQ
ifNEQ
osql -S %1%2
ifNEQ
osql%1%2
if %ERRORLEVEL% NEQ
osql -S %1
ifNEQ
osql%1-E
ifNEQ
osql -S%2
ifNEQ
-b
0 goto errors
—b —i "dbo.LastNameLookup.prc"
0 goto errors
-b
0 goto errors
—b —i "dbo.SelectEmployees.pre"
0 goto errors
-b -i
0 goto errors
goto finish
REM: How to use screen
:usage
echo.
echo Usage: MyScript Server Database
echo Server: the name of the target SQL Server echo Database: the name of the target database echo.
echo Example:
echo.
echo.
goto done
REM: error handler
:errors
echo.
echo WARNING! Error(s) were detected!
eel".
echo Please evaluate the situation and, if needed, echo restart this command file. You may need to echo supply command parameters when executing echo this command file.
echo.
pause
goto done
REM: finished execution
:finish
echo.
echo Script execution is complete!
: done
@echo on
НА
В данном командном файле используются утилиты командной строки osql и которые входят в состав SQL Server. Утилита osql предназначена для выполнения ко; манд SQL, системных процедур и сценариев, а утилита — для пакетного копирования данных в файл данных из SQL Server и обратно.
Данный командный файл можно запустить в окне Solution Explorer, щелкнув на нем правой кнопкой мыши и выбрав в контекстном меню команду Run. Его также можно выполнить извне, независимо от Visual Studio аналогично другим командным файлам.
303
СОВЕТ
Учтите, что при запуске данного командного файла по отношению к базе данных в ней будут удалены все данные!
Запросы
Аналогично сценариям создания и изменения, с помощью Visual Studio .NETмож- но генерировать запросы. За исключением тривиальных запросов, конструктор запросов с графическим пользовательским интерфейсом позволяет более эффективно и безошибочно создавать самые сложные запросы.
Далее работа в режиме конструктора запросов демонстрируется на примере создания параметрического запроса на обновление, который обновляет оптовые цены всех товаров на складе на указанную в процентах величину.
Откройте окно Solution Explorer и щелкните правой кнопкой мыши на любой папке, за исключением Database References. В контекстном меню выберите команду Add Query (Создать запрос), и на экране отобразится диалоговое окно Add New Item (Создать новый объект), показанное на рис. 8.2.
РИС. 8.13. Режим конструктора запросов с диалоговым окном Add Table
Выберите в области шаблонов Templates шаблон Database Query (Шаблон базы данных), укажите в текстовом поле Name имя нового запроса UpdateWholesale. dtq и щелкните на кнопке Open. (После этого на экране отобразится диалоговое окно Set Database Reference for Query для указания используемой ссылки на базу данных. — Прим. ред.) Далее в режиме конструктора запросов отобразится диалоговое окно Add Table (Включить таблицу), которое показано на рис. 8.13.
В диалоговом окне Add Table выберите таблицу tbllnventory и щелкните на кнопке Add, а затем на кнопке Close.
Далее нужно изменить тип запроса с помощью команды меню Query ф Change Type1^ Update (Запрос^Изменить тип1* Обновление).
В панели Diagram (Схема) конструктора запросов установите флажок в поле WholesalePrice, данные в котором предполагается обновить.
В панели Grid (Сетка) в столбце NewValue введите формулу: WholesalePrice*(1 +?/100).
Эта формула принимает один параметр, который обозначает, на сколько процентов повышается оптовая цена товаров. При выполнении запроса символ подстановки в виде вопросительного знака будет заменен фактическим значением этого параметра. После выполнения этих действий окно конструктора запросов будет иметь такой вид, как на рис. 8.14.
РИС. 8.14. Запрос на обновление поля WholesalePrice в таблице tbllnventory
305
Хотя данный запрос можно запустить сразу же после его создания в режиме конструктора запросов, в данном примере он будет запущен из окна Solution Explorer.
Закройте окно конструктора запросов и щелкните на кнопке Yes в ответ на предложение сохранить созданный запрос UpdateWholesale. dtq.
Щелкните дважды на таблице tbllnventory в окне Server Explorer для отображения всех текущих данных в поле WholesalePrice этой таблицы. Именно эти данные об оптовых ценах на товары будут изменены в результате выполнения запроса UpdateWholesale.dtq.
СОВЕТ
Для сверки исходного и последующего после выполнения запроса состояний можно сделать снимок экрана. Например, можно выделить и скопировать все строки, а затем вставить их в текстовый файл с помощью программы Notepad.
Как и при запуске сценария, запрос можно запустить, просто перетаскивая и опуская его на нужную ссылку базы данных. Кроме того, можно щелкнуть правой кнопкой мыши на запросе и выбрать в контекстном меню команду Open.
НА ЗАМЕТКУ
Данное контекстное меню также содержит команды Design и Design On, которые открывают конструктор запросов по отношению к используемой по умолчанию или указанной ссылке на базу данных.
Перетащите и опустите запрос UpdateWholesale.dtq на ссылку на базу данных Novelty в окне Solution Explorer.
РИС. 8.15. Обновленные данные в таблице tbllnventory после увеличения значений в поле WholesalePric на 10%
После этого в диалоговом окне Execute Scripts ОТ Queries (Выполнить сценарий или запрос) подтвердите желание выполнить запрос по отношению к выбранной ссылке на базу данных, щелкнув на кнопке Yes.
В диалоговом окне Define Query Parameters (Определить параметры запроса) введите значение 10 в столбце Parameter Value (Значение параметра) и щелкните на кнопке ОК.
После этого на экране появится диалоговое окно с сообщением о результате выполнения запроса и количестве охваченных им записей. Для продолжения работы закройте его, щелкнув на кнопке ОК.
Повторите действия, описанные в п. 9, для отображения обновленных значений В полеWholesalePrice таблицы tbllnventorу, как показано на рис. 8.15.
Резюме
В этой главе рассмотрены способы работы с проектами баз данных в среде Visual Studio .NET. Этот тип проекта не содержит кода на языке Visual Basic, потому что предназначен для создания, тестирования и запуска сценариев баз данных, запросов и командных файлов. Эти сценарии и команды позволяют создавать новые схемы базы данных, вносить изменения в существующие схемы, а также запрашивать и обновлять существующие данные. Эти эффективные инструменты следует использовать в максимальной степени во время создания и доставки приложения.
Вообще говоря, задачи создания, отладки и усовершенствования сценариев и запросов обычно выполняют аналитики баз данных, но все чаще эти задачи возлагаются на плечи прикладных программистов. Вот почему им необходимо иметь хотя бы базовые представления о способах выполнения таких задач.
Вопросы и ответы
В среде Visual Studio .NET и программе SQL Server Enterprise Manager совпадают многие инструменты. Какие из них предпочтительнее использовать?
Ответ прост: какие хотите. Для выполнения многих операций в среде Visual Studio .NET и программе SQL Server Enterprise Manager предусмотрены совершенно одинаковые инструменты. Однако есть и такие операции, для выполнения которых предусмотрен только один инструмент в одном из этих программных продуктов. Администратор базы данных обычно выполняет все операции с помощью инструментов программы SQL Server Enterprise Manager, а прикладной программист— с помощью инструментов среды Visual Studio .NET. Учтите, однако, что при работе с базой данных другого типа (например, Oracle) вам потребуются совершенно другие инструменты для создания или изменения объектов базы данных. Инструменты среды Visual Studio .NET позволяют просматривать и запрашивать данные в таких базах данных, но не могут изменять их.
В этой ГЛАВЕ.
» Обзор XML
Классы XML на платформе.NET
Утилита SOLXML
Применение XML, XSLT и SQLXML для создания отчета
Резюме
Вопросы и ответы
Вероятно, большинство читателей уже знакомы в той или иной мере с расширяемым языком разметки (extensible Markup Language — XML). По сути, пользователи встречаются с ним уже при установке среды Visual Studio .NET или CLR, даже если и не подозревают об этом. В свое время XML считался панацеей от всех бед, способной решить все проблемы обмена данными, независимо от используемой платформы или аппаратного обеспечения. На начальном этапе развития XML эти необоснованные ожидания породили массу избыточных и совершенно бесполезных затрат при создании приложений с применением XML. Разработчики стали повсюду использовать дескрипторы XML без учета их реальной необходимости для конкретных бизнес-ситуаций или технических причин. Мощь XML заключается в эффективном использовании метаданных и структурированных элементов для хранения данных. XML не является языком программирования, поскольку не содержит никаких директив для выполнения каких-либо действий, а потому обладает полной независимостью от используемой платформы.
Разработчики, имеющие большой опыт работы с HTML, смогут легко понять преимущества надежного и эффективного использования XML. Пространство имен представлено элементом с префиксом xmlns:, оно содержит уникальное имя для контейнера, который предлагает функции и/или данные аналогично имени класса в языке Visual Basic. В среде Visual Basic .NET этот подход упрощается за счет использования расширенного набора классов для синтаксического анализа и манипулирования данными в формате XML. Рассмотрим обычный файл конфигурации web.config. Он имеет формат XML, а не "унаследованный" формат файла конфигурации . ini, хотя доступ к его данным можно осуществить с помощью аналогичных простых методов. Основное отличие заключается в том, что файл web. conf ig обладает большими возможностями для расширения. Доказательством этого отличия является то, что управление файлом web.config осуществляется так же, как и любым другим документом в формате XML, хотя результаты подобных манипуляций могут быть совершенно иными.
Основным преимуществом формата XML (и его аналогов, соответствующих протоколу Simple Object Access Protocol — SOAP) является его нейтральность. Благодаря этому всегда можно создать приложение с расширением логики путем расширения данных, а не создания уникального кода для доступа к централизованно хранящимся данным.
В настоящей главе основное внимание сосредоточено на использовании XML в целях идентификации данных для приложений и пользователей. В ней описываются способы применения XML на платформе .NET в практических ситуациях. Более глубокое описание способов взаимодействия с базами данных и наборами данных DataSet с помощью классов модели ADO.NET приведено в главе 10, "ADO.NET и XML”.
В настоящее время описанию XML посвящено много книг и ресурсов. Поэтому вместо подробного рассмотрения этой темы основное внимание в этой главе уделяется вопросам интеграции XML с платформой .NET. Читатели, которые хорошо знакомы с основами XML, могут пропустить обзорные разделы вплоть до раздела с описанием классов XML для платформы .NET.
обзор XML
Язык XML предназначен только для работы с данными. Точнее говоря, он предназначен для создания набора сведений, или метаданных, которые описывают данные. Рассмотрим приведенный ниже простой фрагмент кода на языке HTML.
— —309
<form name="frmMain" action="mypage.aspx" method="POST"> </form>
Этот фрагмент содержит определение элемента formс атрибутами name, action и method. Атрибуты описывают отдельные элементы формы, сообщают Web-броузеру о выполняемых с ней действиях и являются простейшим примером метаданных. Обратите внимание на замыкающий дескриптор </form>, который обозначает конец контейнера элементов формы. В данном примере form — это универсальный контейнер с универсальными элементами. Экземпляр контейнера form образуется при размещении данных в его структуре.
В приведенном ниже коде показан способ использования элементов и атрибутов XML для описания конкретного фрагмента данных. В нем создается профиль пользователя, который сохраняется в виде ^зла. Узел (или элемент) Person содержит атрибуты, которые описывают данного пользователя.
<Person firstName="John" lastName = "Doe"
addressl = "123 Main Street" address2 =
city = "Sometown"
state = "OH" zip = "22222" phone = "111-242=5512" />
Обратите внимание, что вся информация, независимо от конкретного пользователя, хранится в элементе Person. Любое приложение, которое может анализировать данные в формате XML (а это подвластно практически любому современному коммерческому приложению для работы с данными), способно извлечь из этого кода сведения о пользователе: имя, фамилию, адрес и номер телефона. Кроме того, поскольку с этим пользователем никакие другие данные не связаны, все сведения заканчиваются замыкающим дескриптором </Person>. В формате XML к синтаксису предъявляются очень строгие требования, например если не указать замыкающий дескриптор или пропустить какой-то элемент, то чтение всего документа может закончиться неудачей.
| НА ЗАМЕТКУ '
Семантика XML интересна тем, что на элемент можно ссылаться как на узел и наоборот. В некоторых публикациях элемент также называют дескриптором. Во избежание путаницы здесь и далее используются только термины "элемент" и"узел”.
Элементы не только могут иметь атрибуты, но и подчиненные элементы; аналогично, подчиненные элементы могут иметь свои собственные атрибуты, как показано в листинге 9.1.
Листинг 9.1. Пример элемента Person с подчиненными элементами
<Person firstName="John">
lastName = "Doe"
addressl = "123 Main Street"
address2 =
city = "Sometown"
state = "ОН" zip = "22222" phone = "111-242=5512" <orders>
<order id="lll" itemid-"2932" itemdesc="Super Foo Widget" </order>
</orders>
Как видите, элемент Person здесь нужно закрыть явно, потому что изменился диапазон контейнера, т.е. теперь он содержит сведения о заказах orders. Приведенный в листинге 9.1 код является нейтральным, т.е. не содержит никакой информации о приложении, только данные и метаданные.
I НА ЗАМЕТКУ |
Языку XML посвящено множество материалов, опубликованных в печатных изданиях и
в Internet. Прекрасное введение в XML можно найти по адресу: http: //www.w3. org/
; XML/1999/XML-in-10-points.html. Вот несколько других адресов Web-узлов, ко-
I торые содержат наиболее полные сведения по этой теме:
- e Web-узел консорциума World Wide Web Consortium (http://www.w3. org) содержит официальные и хорошо документированные спецификации XML;
Web-узел компании Microsoft(http: //msdn. microsoft. com) содержит постоянно обновляемые ресурсы, которые охватывают все вопросы использования XML и родственных технологий с программными продуктами Microsoft;
i • Web-узел компании TopXML/VBXML.com (http: I/www.vbxml.com) предлагает разные ресурсы и примеры кодов для работы с технологией XML.
СемействотехнологийXML
Язык XML не одинок и сосуществует с несколькими другими родственными технологиями.
XML Path Language (XPATH) предлагает способ извлечения информации из XML-документа. Хотя синтаксис ХРАТН радикально отличается от синтаксиса XML, он работает аналогично SQL-запросам.
Extensible StyleSheet Language Transformations (XSLT) предлагает способ преобразования (т.е. вставки, удаления или изменения) данных XML-документа в данные, которые могут использоваться в другом XML-документе. XSLT может использовать технологию ХРАТН для извлечения преобразуемых данных.
Extensible StyleSheet Language (XSL) — это XSLT плюс объекты, которые позволяют разработчику описывать способ отображения информации в броузере или другом XSL-совместимом приложении.
Document Object Model (DOM) содержит стандартный набор функций, которые позволяют программными средствами извлекать данные из XML- или HTML-документа.
311
Simple Object Access Protocol (SOAP) — это спецификация вызовов Web-служб или других Web-ориентированных приложений и служб, а также форматирования отклика. Более подробно SOAP рассматривается в главе 12, "Web-службы и технологии промежуточного уровня".
На рис. 9.1 показана взаимосвязь между технологиями XML, XSL, XSLT и ХРАТН в приложении на основе XML. (Более подробно такие приложения рассматриваются далее, в разделе о классах XML на платформе .NET.) Обратите внимание, что XML- документ является источником данных, т.е. содержит отображаемые в приложении данные. ХРАТН-запрос элементов People/Person используется для сбора сведений обо всех элементах Person из XML-документа. XSL-стиль окружает данные элементами с указанием шрифта с помощью XSL-страницы стиля. После анализа и обработки полученный результат возвращается в формате HTML.
XML-документ
<?xml version='1.0' ?>
<?xml:stylesheet type="text/xsl" href="simple.xsl" 7»
<People>
<Person>
John Doe
</Person>
<Person>
Jane Doe
</Person>
</People>
XML-данные + XSL-стиль
Страница XSLT- стиля
<?xml version="1.0"?>
<HTML xmlns:xsl="http://www.w3.org/TR/WD-xsl->
<xsl:for-each select="People/Person">
<font face=-Arial->
<xsl:value-o£ select="."/>
</font>
</xsl:for-each>
</HTML>
Получается форматированная HTML-страница
Окончательный результат
HTML-код:
<font face=”Arial">
John Doe Jane Doe
</font>
Вид в окне Web-броузера
John Doe Jane Doe
РИС. 9.1. Иерархия технологий XML, XSL, XSLTu XPATH
ВНИМАНИЕ! ;
j Учтите, что XML имеет очень строгиепричем все имена нужно приводить с
учетом регистра символов.
Для демонстрации этой взаимосвязи вставьте код из листингов 9.2 и 9.3 в файлы икоторые располагаются в одном каталоге. Откройте
файлв Web-броузере Internet Explorer версии 6.0 или выше. Результат
выполнения этих действий будет иметь вид, представленный на рис.
ЛИСТИНГ 9.2. Файлxml
?>
?>
<People>
<Person>
John Doe
</Person>
<Person>
Jane Doe
</Person>
</People>
Обратите внимание, что страница стиля связана с XML-документом. С помощью дляилиVisual Basic можно динамически изме
нять результаты с представлением их в нескольких разных форматах (например,
ЛИСТИНГ 9.3. Файлxsl
<?xml version="1.0"?>
<HTML
<xsl:for-each select="People/Person">
<font
<xsl:value-of select="."/>
</font>
</xsl:for-each>
</HTML>
Строка— это ХРАТН-запрос, который представляет
собой эквивалент SQL-команды SELECT People FROM Person. Для циклического обхода всех элементов применяется командаor-each из спецификации XSL.
В отличие от XML, в котором нет программных директив, в XSL предусмотрен целый набор директив, который может быть расширен благодаря сценариям для предоставления дополнительной функциональности.
XML и доступ к данным
Обладая этими базовыми знаниями об XML и зная способы их использования, можно применить их в реальных приложениях для работы с данными. XML- анализатор способен считать данные в формате XML, и они будут иметь одинаковый смысл в любом приложении. Эта гибкость сокращает необходимый для работы с данными код, улучшает сопровождение и поддержку программ.
313
Когда создатели платформы .NET планировали следующую версию интегрированной среды разработки, такая гибкость была одним из основных требований. Теперь практически все элементы платформы .NET используют XML для обмена информацией. Среда CLR использует XML-файлы для указания параметров приложения. Объекты DataSet возвращаются как XML-документы, которые могут записываться в виде строки или документа. XML Web-службы удовлетворяют открытому стандарту SOAP, который в конечном итоге означает, что код объекта для платформы Windows может быть расширен для использования на любой другой платформе, которая поддерживает HTTP- или SO АР-запросы.
XML не предлагает никаких новых революционных способов изменения способов доступа к данным, а лишь иначе использует известные способы сбора и сохранения данных. Более подробная информация о применении компонентов XML с моделью ADO.NET предлагается в главе 10, “ADO.NET и XML”.
Классы XML на платформе .NET
На платформе .NET язык XML применяется для повышения производительности, совместимости с открытыми стандартами и интеграции с ADO.NET. Для этого предусмотрены специализированные классы и пространства имен. Для перечисления всех имеющихся пространств имен, классов, методов, свойств, перечислений, интерфейсов и делегатов потребовалось бы привести огромную таблицу, что в целом было бы крайне неэффективным способом расходования пространства и времени. Вместо этого здесь и далее описывается несколько основных классов платформы .NET Framework, предназначенных для работы с XML. На основе этих классов далее приводится несколько примеров эффективного использования XML с иллюстрацией причин, по которым разработчики платформы .NET Framework из компании Microsoft создали их именно такими.
В этом разделе приводятся примеры выполнения задач, связанных с созданием приложений для XML, XSL, XLST и ХРАТН. (В замечании из предыдущего раздела приводятся ссылки на некоторые ресурсы с более подробным описанием этих технологий.) Как уже отмечалось, основная цель XML заключается в структурировании и описании данных. Для этого на платформе .NET Framework предусмотрены классы пространства имен System.xml. В приведенных далее примерах используются некоторые другие новые компоненты Visual Basic .NET, которые подробно комментируются.
Применение модели Document Object Model
С появлением модели Document Object Model (DOM) значительно упростилось создание динамически генерируемых Web-узлов, благодаря приданию дополнительных функциональных возможностей статическим элементами в документах HTML/XML. Простая строка кода document. location сценария JavaScript выполняет обход всех элементов Web-страницы, начиная с самого верха объекта document и заканчивая поиском свойства location. Этот способ программируемого доступа к объектам Web-страницы был в определенной степени революционным. Затем появился XML со строгими правилами и динамической объектной моделью, на основе которой автор документа определял корневой или родительский элемент.
В примерах оставшейся части главы с помощью утилиты SQLXML создается XML- документ с именем simple2 . xml. Ручное кодирование больших HTML-страниц — это
довольно скучное и утомительное занятие, при котором нужно тщательно следить за форматом всех данных. Однако XML обладает несомненным достоинством: нет необходимости беспокоиться о том, что в документ попадет какая-то дополнительная ненужная информация, как это обычно бывает при создании Web-страниц с помощью некоторых HTML-редакторов.
В листинге 9.4 приведен текст простого XML-документа simple2 .xml. Расположение символов конца строки на самом деле не имеет никакого значения, потому что аналогично другим языкам программирования XML использует специализированные символы для обозначения строк. Например, в С+, c#, Java и JavaScript для разделения строк используется точка с запятой, а в XML единой строкой считаются все символы между открывающим < и закрывающим /> дескрипторами.
ЛИСТИНГ 9.4. Файл simple2 . xsl
<?xml version="l. 0" encoding="utf-8"?> ccustomer>
<tblCustomer ID="1" FirstName="Carole" LastName="Vermeren"
Address="6227 East Crossing Drive" City="Ocean Glen" State="NH"
PostalCode="98609" Phone="6034485994" />
<tblCustomer ID="2" FirstName="Cathy" LastName="Johnson"
Address="1629 West River Street" City="Big Center" State="NC"
PostalCode="18602" Phone="9193669205" />
ctblCustomer ID="3" FirstName="Eric" LastName="Haglund"
Address="9193 West Beach Street" City="Brown Heights" State="OK"
PostalCode="83481” Phone="4059310689" />
ctblCustomer id="4" FirstName="Julie" LastName="Ryan"
Address="9161 Fort Beach Way" City="South Point" State="KY"
PostalCode="26973" Phone="5025245220" />
ctblCustomer ID="5" FirstName="Richard" LastName="Halpin"
Address="9790 Happy River Street" City="North Lake" State="MN"
PostalCode="62875" Phone="6124066311" />
<tblCustomer ID="6" FirstName="Kathleen" LastName="Johnson"
Address="9385 West Heights Street" City="Brown Towne" State="MI"
PostalCode="59609" Phone="3138032214" />
<tblCustomer ID="7" FirstName="Sorel" LastName="Polito"
Address="2104 Brown Brook Drive" City="Blue Valley" State="MT"
PostalCode="54401" Phone="4067260212" />
<tblCustomer ID="8" FirstName="Sorel" LastName="Terman"
Address="1920 West Point Street" City-"Blue Bluffs" State="WI*
PostalCode="08965" Phone="6086246867" />
ctblCustomer ID="9" FirstName="Randy" LastName="Hobaica"
Address="4 619 North Plains Drive" City="Brown Ridge" State="CT"
PostalCode="09793" Phone="2039421728" />
<tblCustomer ID="10" FirstName="Matthew" LastName="Haglund"
Address="8725 Sunset Crossing Avenue" City="New Brook" State="AR"
PostalCode="79013" Phone="5014589191" />
</customer>
Информация в листинге 9.4 взята из базы данных Novelty, описанной в главах 1-3,
с помощью приведенного ниже запроса.
SELECT ТОР 10 * FROM tblCustomer FOR XML AUTO
XML и .NET Г
—। 315
Затем в него вручную добавлен элемент <customer>. Далее, в разделе об утилите SQLXML, показано, как для любого XML-документа автоматически создается корневой элемент.
В следующем примере рассматривается класс XMLDocument, с помощью которого осуществляется доступ к XML-данным и документам на платформе .NET Framework. В простейшем случае XML-данные из документа или строки в оперативной памяти загружаются с помощью методаLoad классаXMLDocument.
Применение технологии ХРАТН
Что происходит после загрузки XML-документа? В пространстве имен System.xml предусмотрены классы xmlNode и xmlNodeList. С помощью этих классов и технологии ХРАТН происходит считывание XML-документа и извлечение интересующих нас данных. В листинге 9.5 показано простое приложение Visual Basic .NET, которое загружает XML-документ из файла simple? . xsl и выводит все имена (атрибут FirstName) в текстовом поле.
ЛИСТИНГ 9.5. Пример ИСПОЛЬЗОВаНИЯ KJiacCOB XmlDocument И XmlNode
Imports System.Xml
Imports System.Xml.XPath
Imports System.10
Public Class Formi
Inherits System.Windows.Forms.Form
Здесь опущен код, сгенерированный Windows Form Designer.
Private Sub Forml_Load(ByVai sender As System.Object, _ ByVai e As System.EventArgs) Handles MyBase.Load
Dim xDoc As New XmlDocument()
xDoc.Load("simple?.xml")
' Обратите внимание, что для получения значения атрибута используется синтаксис ХРАТН.
Dim xNodeList As XmlNodeList =
xDoc.SelectNodes("descendant::tblCustomer/@FirstName")
Dim xNode As XmlNode
Dim i As Integer = 0
For Each xNode In xNodeList
IstResults.Items.Insert(i, xNode.InnerText)
i = i + 1
Next
End Sub
End Class
Для выполнения этого кода создайте новый проект, форму, а в форме — поле со списком IstResults. Поместите код подпрограммы Forml_Load из листинга 9.5 в код формы. После компоновки и запуска приложения в поле со списком будут показаны
результаты, показанные на рис. 9.2 (при условии, что в каталоге выполняемого файла находится файл simple2 . xml).
РИС. 9.2. Результаты выполнения кода из листинга 9.5
Как видите, загрузка XML-документа и циклический обход его данных не представляют собой большой проблемы. А как можно изменить данные в XML-документе? Ответ на это вопрос основан на комбинации использованных ранее классов.
НА ЗАМЕТКУ ;
Далее код подпрограммы Forml_Load из листинга 9.5 переносится в код подпро- : граммы ShowToplO (листингЭ.6).
В листинге 9.6 приведен измененный код данного примера, в котором можно изменять и сохранять значение каждого узла в XML-документе. В этом достаточно простом примере используются чрезвычайно эффективные и надежные средства изменения XML-документа. Помимо упомянутых выше изменений, в данное приложение также включены кнопки btnShowToplO и btnChangeAndSave для вызова отдельных подпрограмм отображения и изменения данных. После создания новых кнопок, компоновки проекта и запуска приложения щелкните на кнопке ShowToplO. После выполнения этих действий в поле со списком будут представлены результаты, показанные на рис. 9.2. (Учтите: это возможно лишь при условии, что в каталоге выполняемого файла находится файл simple2 . xml.) Для редактирования одного из значений щелкните дважды на том элементе списка, который нужно изменить. Затем в диалоговом окне ChangeAndSave введите новое имя и щелкните на кнопке ОК. После этого в поле со списком будет отображено обновленное состояние XML-документа с измененными значениями списка.
ЛИСТИНГ 9.6. КОД Приложения XMLDomSample
Imports System.Xml
Imports System.Xml.XPath
Imports System.10
Public Class Forml
317
Inherits System.Windows.Forms.Form
Здесь опущен код, сгенерированный Windows Form Designer.
Private Sub Forml_Load(ByVai sender As System.Object, ByVai e As System.EventArgs) Handles MyBase.Load
End Sub
Private Sub ShowToplO()
Dim xDoc As New XmlDocument () xDoc.Load("simple2.xml")
' Обратите внимание, что для получения значения атрибута
используется синтаксис XPATH.
Dim xNodeList As=
xDoc.SelectNodes("descendant::tblCustomer/@FirstName")
Dim xNode As XmlNode
Dim i As Integer = 0
For Each xNode In xNodeList
IstResults.Items.Insert(i, xNode.InnerText) i = i + 1
Next
End Sub
Public Sub ChangeNameandSave(ByVai NameToChange As String, ByVal ChangeTo As String)
Dim xDoc As New XmlDocument()
xDoc.Load("simple2.xml")
Dim xNodeList As XmlNodeList = _
xDoc.SelectNodes("descendant::tblCustomer/@FirstName")
Dim xNode As XmlNode
For Each xNode In xNodeList
If xNode.InnerText = NameToChange Then xNode.Value = ChangeTo
End If
Next
xDoc.Save("simple2.xml")
MsgBox("Name change saved I " / 0)
IstResults.Items.Clear()
ShowToplO()
End Sub
Private Sub btnShowToplO_Click(ByVai sender As System.Object, _ ByVal e As System.EventArgs) Handles btnShowToplO.Click ShowToplOO
End Sub
Private Sub lstResults_DoubleClick(ByVai sender As Object, ByVal e As System.EventArgs) Handles IstResults.DoubleClick
Dim oldName As String = IstRe-
sults. GetltemText (IstResults.Items.Item(IstResults.Selectedlndex))
Dim newName As String = _
InputBox("Please enter a new name", "ChangeAndSave")
ChangeNameandSave(oldName, newName)
End Sub
End Class
НА ЗАМЕТКУ
Какуже сообщалось ранее, коды всех примеров можно найти на Web-странице Издательского дома "Вильяме" по адресу: http://www.williamspublishing.com.
Утилита SQLXML
Утилита SQLXML версии 3.0 представляет собой еще один способ сбора и генерации XML-документов. Она не может заменить средства ADO.NET, а является лишь расширением функций SQL Server с помощью интерфейса броузера для создания отчетов, отслеживания выполняемых действий и т.п.
Для использования утилиты SQLXML необходимо инсталлировать Web-сервер Internet Information Server (IIS) и СУБД SQL Server 2000, в состав которой и входит эта утилита. В данном примере используется компьютер с операционной системой Windows XP Professional, IIS 5.1 и утилита SQLXML версии 3.0.
Утилита SQLXML расширяет функции СУБД SQL Server 2000 возможностями создания запросов к базе данных на основе HTTP-запросов. Хотя она обладает многими преимуществами, некоторые опасения у разработчиков вызывают вопросы обеспечения безопасности. Большинство этих опасений можно устранить за счет использования системы аутентификации Windows для виртуальных каталогов, в которых выполняются запросы, и для базы данных.
НА ЗАМЕТКУ :I
Утилиту SQLXML версии 3.0 можно бесплатно скопировать с Web-узла компании | Microsoft ПО адресу: http://msdn.microsoft.com/download?/default.asp?url=/ . downloads/sample.asp?url=/msdn-flies/027/001/824/msdncompositedoc.xml. '
Инсталляция и конфигурирование утилиты SQLXML
После копирования файла с утилитой SQLXML с Web-узла компании Microsoft щелкните дважды кнопкой мыши на нем. На экране появится программа-инсталлятор с несколькими стандартными вопросами, например: согласны ли вы с предлагаемой лицензией и хотите ли вы выполнить обычную или специализированную инсталляцию? Единственное отличие между типами инсталляции заключается в том, что в специализированной инсталляции можно указать другой каталог размещения файлов утилиты.
Для конфигурирования утилиты SQLXML выполните следующее. Выберите команду Start^Microsoft SQL Server^Configure SQL XML Support in IIS (Пуск^ Microsoft SQL Зегуег^Конфигурировать поддержку SQL XML в IIS), на экране появится диалоговое окно IIS Virtual Directory Management for SQL Server (Виртуальный каталог управления IIS для SQL Server) программы Microsoft Management Console (рис. 9.3).
319319РИС. 9.З. Диалоговое окно IIS Virtual Directory Management for SQL Server программы Microsoft Management ConsoleРИС. 9.З. Диалоговое окно IIS Virtual Directory Management for SQL Server программы Microsoft Management Console
319
319
РИС. 9.З. Диалоговое окно IIS Virtual Directory Management for SQL Server программы Microsoft Management Console
РИС. 9.З. Диалоговое окно IIS Virtual Directory Management for SQL Server программы Microsoft Management Console
Обратите внимание на то, что панель в правой части окна содержит только один столбец Computer. Щелкните дважды левой кнопкой мыши на имени компьютера в этом столбце для раскрытия списка всех Web-узлов данного компьютера, как показано на рис. 9.4. Обратите внимание: имя текущего столбца в правой части окна изменилось и он стал называться Web Site Name (Имя Web-узла).
Щелкните дважды на надписи Default Web Site (Используемый по умолчанию Web- узел), которая показана на рис. 9.5, для отображения списка конфигурированных виртуальных каталогов в правой части окна.
7« MS Virtual Ditectoi у Management for SQL Server
1Й File Action View Window Help■■ IB ПН] * Q G^'S1Й File Action View Window Help■■ IB ПН] * Q G^'S^ IIS Virtual Directory Managem у JlbUiffiit^ IIS Virtual Directory Managem у JlbUiffiitWeb Site NameWeb Site NameQ Default Web SiteQ Default Web Site^DefaultWeb Site^DefaultWeb Site||РИС. 9.4. Список Web-узловРИС. 9.4. Список Web-узлов
1Й File Action View Window Help
■■ IB ПН] * Q G^'S
1Й File Action View Window Help
■■ IB ПН] * Q G^'S
^ IIS Virtual Directory Managem у JlbUiffiit
^ IIS Virtual Directory Managem у JlbUiffiit
Web Site Name
Web Site Name
Q Default Web Site
Q Default Web Site
^DefaultWeb Site
^DefaultWeb Site
|
|
РИС. 9.4. Список Web-узлов
РИС. 9.4. Список Web-узлов
РИС. 9.5. Используемый по умолчанию Web-узел
Далее создайте новый каталог с:\inetpub\wwwroot\novelty. Для этого выберите команду Starts Run, введите команду cmd, щелкните на кнопке ОК, введите в режиме командной строки команду mkdir с: \inetpub\wwwroot\novelty и нажмите клавишу <Enter>. Аналогично внутри этого каталога создайте подкаталог Template s.
Для конфигурирования нового виртуального каталога щелкните правой кнопкой мыши на узле Default Web Site в левой части окна и выберите в контекстном меню команду New^ Virtual Directory (Создать1^ Виртуальный каталог), как показано на рис. 9.6.
Рис. 9.6. Выбор команды New ^VirtualDirectory
После выбора виртуального каталога на экране появится новое диалоговое окно New Virtual Directory Properties (Свойства нового виртуального каталога) с шестью
321
вкладками. В первой вкладке, General, предлагается ввести имя виртуального каталога, а также путь к локальному каталогу, где располагаются файлы. Введите в текстовом поле Virtual Directory Name (Имя виртуального каталога) имя Novelty, а в текстовом поле Local Path (Локальный путь) укажите путь к локальному каталогу c: \inetpub\ wwwroot\novelty, как показано на рис. 9.7.
РИС. 9.7. Указание имени виртуального каталога и пути к нему
Далее щелкните на вкладке Security (Безопасность) в верхней части диалогового окна и выберите переключатель Use Windows Integrated Authentication (Использовать интегрированную систему аутентификации Windows). На этом этапе предполагается, что при установке SQL Server был выбран смешанный режим аутентификации MixedMode Authentication или режим аутентификации Windows Authentication. Выбор каждого из этих режимов определяется предпочтительным уровнем безопасности. Если безопасность не имеет существенного значения, например СУБД не связана с внешней сетью, то в таком случае подойдет первый режим. Он позволяет кэшировать параметры подключения аналогично строке подключения. Третий режим, HTTP-Based Basic Authentication (Аутентификация на основе HTTP), базируется на учетной записи SQL Server. На рис. 9.8 показан выбор режима аутентификации Windows Authentication.
Выберите вкладку Data Source (Источник данных) и укажите используемый экземпляр СУБД SQL Server и базу данных. В текстовом поле SQL Server оставьте предла- гаемоепоумолчаниюимя (local).
РИС. 9.8. Параметры системы обеспечения безопасности
НА ЗАМЕТКУ
Имя (local) является "дружественным" именем сервера, который запущен на локальном компьютере. Если SQL Server сконфигурирован для репликации данных в качестве подписчика или издателя данных, то вместо дружественного имени нужно указать фактическое имя компьютера.
Далее в поле со списком Database (База данных) выберите базу данных Novelty, как показано на рис. 9.9. Учтите, что в нем перечислены те базы данных, которые соответствуют параметрам подключения и режиму безопасности.
Выберите вкладку Settings (Параметры) и установите флажки параметров Allow URL queries (Разрешить запросы в URL-указателях), Allow template queries (Разрешить запросы из шаблонов) и Allow XPath (Разрешить Xpath-запросы), как показано на рис. 9.10.
Наконец, выберите вкладку Virtual Names (Виртуальные имена). Щелкните на кнопке New в области окна Defined virtual names (Заданные виртуальные имена). В текстовом поле Virtual Name (Виртуальное имя) диалогового окна Virtual Name Configuration (Конфигурация виртуального имени) введите имя templates. В поле со списком Туре (Тип) выберите тип Template (Шаблон), а в текстовом поле введите с:\inetpub\wwwroot\novelty\templates, как показано на рис. 9.11. Для сохранения указанных параметров щелкните на кнопке Save.
РИС. 9.9. Параметры источника данныхРИС. 9.9. Параметры источника данных323323РИС. 9.10. Вкладка SettingsРИС. 9.10. Вкладка SettingsРИС. 9.11. Диалоговое окно Virtual Name ConfigurationРИС. 9.11. Диалоговое окно Virtual Name Configuration
РИС. 9.9. Параметры источника данных
РИС. 9.9. Параметры источника данных
323
323
РИС. 9.10. Вкладка Settings
РИС. 9.10. Вкладка Settings
РИС. 9.11. Диалоговое окно Virtual Name Configuration
РИС. 9.11. Диалоговое окно Virtual Name Configuration
После этого диалоговое окно New Virtual Directory Properties будет иметь вид, показанный на рис. 9.12. Щелкните на кнопке ОК для его закрытия.
Таким образом, успешно сконфигурирован виртуальный каталог с помощью IIS. Теперь можно корректно выполнять SQL-запросы к базе данных SQL Server. Если дважды щелкнуть на узле Default Web Site в левой части диалогового окна IIS Virtual Directory Management for SQL Server программы Microsoft Management Console, то его правая часть будет иметь такой вид, как на рис. 9.13. Закройте диалоговое окно IIS Virtual Directory Management for SQL Server.
Рис. 9.12. Диалоговое окно New Virtual Directory Properties после конфигурирования виртуалъного каталога
Рис. 9.13. Диалоговое окно IIS Virtual Directory Management for SQL Server программы Microsoft Management Console после конфигурирования параметров Web-узла Novelty
—
325
Результаты конфигурирования
Рассмотрим теперь, что же получилось в результате конфигурирования параметров
Novelty. ОткройтеInternet Explorer версии 6.0 или выше. Полученный результат будет выглядеть, как показано в листинге 9.4. Файлxml был
создан именно так. Введите приведенный ниже URL-указатель для просмотра структуры файла.
http://localhost/Novelty?sql=select top 10 * <9 fromFOR XML
Обратите внимание, что параметр root в конце данного URL-указателя содержит корневой элемент XML-документа. Без него довольно трудно отображать результаты HTTP-запросов в окне Web-броузера.
применениеXSLT и
для создания отчета
Попробуем теперь применить описанные ранее сведения в практической ситуации, например для создания списка адресов клиентов. Как будет показано далее, изменение HTML-содержимого с помощью XSL-файла не вызывает больших трудностей. Поэтому такой способ часто применяется для форматирования страниц, которые предполагается распечатать, или вместе сдля повышения
функциональности и надежности.
SQL Server предоставляет данные в формате XML, а для сохранения запроса используется шаблон в файле noveltytemplate . xml. Единственным назначением этого файла является сбор данных и присвоение страницы стиля.
<?xml version' ?>
<root
sql:xsl='noveltyxsl.xsl'>
<sql:query>
SELECT FirstName,Address, City,
State FROM tblCustomer FOR XML AUTO
</sql:query>
</root>
В первой строке задается базовый XML-документ и способ связывания со страницей стиля, который аналогичен применению элементаstylesheet в листинге 9.2. Следующий элементquery является контейнером команды SQL или вы
полняемого запроса. Предложение FOR XML AUTO сообщает SQL Server о возвращении результатов в формате XML. В шаблоне предполагается, что корневой элемент называется root, поэтому его не нужно указывать в шаблоне запроса.
НА ЗАМЕТКУ :
Для динамического присвоения страницы стиля с помощью SQLXML нужно в URL-указатель параметр запросаи указать используемый файл в формате
1 http://имя_компьютера/виртуальный_каталог_шаблонов/имя_шаблона.хш1?х файла
В листинге 9.7 показана страница стиля XSL, которая используется для создания итогового XML-документа. Для указания атрибута в ней применяется технология ХРАТН. С помощью синтаксиса CSS и HTML создается формат представления извлеченной информации в окне Web-броузера.
ЛИСТИНГ 9.7. Файл noveltyxsl.xsl
<?xml version='1.0 ' encoding='UTF-8'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="l.0">
<xsl:template match = '*'>
<xsl:apply-templates />
</xsl:template>
<!- Имя запрашиваемой таблицы извлекается из дочерних элементов. ->
<xsl:template match = 'tblCustomer1 >
<TR>
<!- Обратите внимание на использование ХРАТН для сбора данных из полей. ->
<TD><xsl:value-of select = '@FirstName1 /></TD>
<TD><xsl:value-of select = '@LastName' /></TD>
<TDXxsl:value-of select = '@Address' /></TD>
<TD><xsl:value-of select = *ecity1 /></TD>
CTDxxsl: value-of select = 1 @State1 /></TD>
</TR>
</xsl:template>
<xsl: template match = ' / ’ >
<HTML>
<HEAD>
<STYLE>th { background-color: #000080; color: #ffffff; }<P td (font-family: Arial}</STYLE>
</HEAD>
<BODY>
<TABLE border=’1' style=*width:600;*>
<TR><TH colspan=1 9' >Customers</THX/TR>
<TR>
<TH>First name</TH>
<TH>Last name</TH>
<TH>Address</TH>
<TH>City</TH>
<TH>State</TH>
</TR>
<xsl:apply-templates select = 'root1 />
</TABLE>
</BODY>
</HTML>
</xsl:template>
</xsl:stylesheet>
Разместите оба файла (noveltytemplate. xml и noveltyxsl.xsl) в подчиненном каталоге templates виртуального Web-каталога Novelty, который описывается в предыдущем разделе. После этого откройте Web-броузер Internet Explorer версии 6.0 или выше и введите в нем следующий URL-указатель:
http://localhost/Novelty/templates/noveltytemplate.xml?contenttype=text/html
327
Параметр contenttype используется для указания формата представления конечного результата, т.е. в виде HTML-страницы. После выполнения описанных действий данные в окне будут иметь такой вид, как на рис.
-
FileEditViewFavoritesHelp
Favorites
■CnstotaeriFirstLast name.Address.City10Й8CarolejVermerenEastDrive[Ocean GlenNH(CathyJohnsonWest RiverCenter;NC ]Eric(HaglundWest Beach StreetHeights(Julie.(RyanFortPointRichardHalpin(9790 Happy River Street(North LakeMNKathleen(Johnson(9385 West Heights Street(BrownTowneMl(SorelPoiitoBrown Brook Drive(Blue ValleyMT[SorelTermanStreet(Blue BluffsW1(RandyHobaicaNorth Plains DriveRidge...(Matthew(HagiundSunset Crossing AvenueBrook(CathyVermeren(4805 New Center[North|vfjBradTownsend5477 Dead Man Heights Street[Ocean Glen(OH■CnstotaeriFirstLast name.Address.City10Й8CarolejVermerenEastDrive[Ocean GlenNH(CathyJohnsonWest RiverCenter;NC ]Eric(HaglundWest Beach StreetHeights(Julie.(RyanFortPointRichardHalpin(9790 Happy River Street(North LakeMNKathleen(Johnson(9385 West Heights Street(BrownTowneMl(SorelPoiitoBrown Brook Drive(Blue ValleyMT[SorelTermanStreet(Blue BluffsW1(RandyHobaicaNorth Plains DriveRidge...(Matthew(HagiundSunset Crossing AvenueBrook(CathyVermeren(4805 New Center[North|vfjBradTownsend5477 Dead Man Heights Street[Ocean Glen(OH^ J Local intranet^ J Local intranet^ Done^ DoneGlenGlen(Eves(EvesLake StreetLake StreethtUrMocaihost/rcv^vThwplatMjft&ydty^frlatexmfrccnterthtUrMocaihost/rcv^vThwplatMjft&ydty^frlatexmfrccntert....
■Cnstotaeri
First
Last name
.Address
.City
10Й8
Carole
jVermeren
EastDrive
[Ocean Glen
NH
(Cathy
Johnson
West River
Center
;NC ]
Eric
(Haglund
West Beach Street
Heights
(Julie.
(Ryan
Fort
Point
Richard
Halpin
(9790 Happy River Street
(North Lake
MN
Kathleen
(Johnson
(9385 West Heights Street
(BrownTowne
Ml
(Sorel
Poiito
Brown Brook Drive
(Blue Valley
MT
[Sorel
Terman
Street
(Blue Bluffs
W1
(Randy
Hobaica
North Plains Drive
Ridge
...
(Matthew
(Hagiund
Sunset Crossing Avenue
Brook
(Cathy
Vermeren
(4805 New Center
[North
|vf
jBrad
Townsend
5477 Dead Man Heights Street
[Ocean Glen
(OH
■Cnstotaeri
First
Last name
.Address
.City
10Й8
Carole
jVermeren
EastDrive
[Ocean Glen
NH
(Cathy
Johnson
West River
Center
;NC ]
Eric
(Haglund
West Beach Street
Heights
(Julie.
(Ryan
Fort
Point
Richard
Halpin
(9790 Happy River Street
(North Lake
MN
Kathleen
(Johnson
(9385 West Heights Street
(BrownTowne
Ml
(Sorel
Poiito
Brown Brook Drive
(Blue Valley
MT
[Sorel
Terman
Street
(Blue Bluffs
W1
(Randy
Hobaica
North Plains Drive
Ridge
...
(Matthew
(Hagiund
Sunset Crossing Avenue
Brook
(Cathy
Vermeren
(4805 New Center
[North
|vf
jBrad
Townsend
5477 Dead Man Heights Street
[Ocean Glen
(OH
^ J Local intranet
^ J Local intranet
^ Done
^ Done
Glen
Glen
(Eves
(Eves
Lake Street
Lake Street
htUrMocaihost/rcv^vThwplatMjft&ydty^frlatexmfrccntert
htUrMocaihost/rcv^vThwplatMjft&ydty^frlatexmfrccntert
..
..
РИС. 9.14.представления данных с помощью
и технологии XSL
Резюме
В этой главе представлены базовые сведения о языке XML и его назначении. Здесь демонстрируются способы применения XML на платформеFramework и
взаимодействия XML с Visual BasicВ конце главы показан способ конфигури
рования Web-сервера IIS и СУБД SQL Server 2000 для представления информации в виде XML-документов, на основе которых можно легко создавать HTML-страницы. Большая часть приведенной здесь информации играет роль введения в главу 10, и
Вопросы и ответы
Что означает "создание схемы" в контексте
В контексте баз данных "создание схемы" означает создание документа с определениями объектов и сущностей. В контексте XML это понятие может быть расширено и охватывать другие сведения, например определение поля без неопределенных значений (NOT NULL) в таблице базы данных.
Что произойдет, если для названия элемента в XML-документе использовать уже зарезервированное имя HTML-элемента, например title?
Для устранения такой двусмысленности и предназначены пространства имен. Можно создать XML-документ с применением XSLT и использовать в нем уже зарезервированное имя HTML-элемента, например title, для одного из полей таблицы. Именно для этого придется использовать собственное пространство имен.
С помощью строки кода <xmlns:b=http://myMachine. com> можно теперь использовать префикс b: для любого XML-элемента, даже если его имя совпадает с каким-то уже зарезервированным именем HTML-элемента.
В таком случае строки <b : title> и <title> будут относиться к совершенно разным сущностям. Единственным строгим условием является предварительное объявление используемого пространства имен, например в самом начале документа.
В ЭТОЙ ГЛАВЕ...
» Основные принципы чтения и записи XML-данных
# Создание объекта XmlReader
с помощью объекта Command
Объект XmlDataDocument
♦ Резюме
Вопросы и ответы
Оставляя в стороне маркетинговую шумиху, следует отметить, что XML действительно способствует выполнению множества задач, связанных с программированием бизнес-решений. Это особенно верно в тех случаях, когда огромное значение приобретает интеграция разных систем и платформ в рамках одной или нескольких компаний. Поэтому при создании интегрированной среды разработки Visual Studio .NET одной из основных целей была расширенная поддержка XML.
Язык XML является форматом устойчивого хранения объекта DataSet, т.е. при сохранении на жестком диске объекта DataSet используется универсальный формат XML, а не двоичный или какой-то другой специализированный формат. Аналогично, при обмене объектами DataSet между разными процессами или компьютерами данные передаются в потоке формата XML.
В прежних главах показано, что объект DataSet не зависит от источника данных, которые он содержит. Для него данные — это всего лишь данные, независимо от их происхождения. То же самое верно, когда источник данных имеет формат XML. Объект DataSet обладает средствами чтения и записи данных в формате XML и/или информации о схеме данных. В модели ADO.NET эти средства обладают гораздо более высокими функциональными возможностями по сравнению с моделью ADO. Более того, благодаря объекту XmlDataDocument приложение может просматривать данные объекта DataSet и манипулировать ими с помощью реляционных инструментов и/или XML-инструментов, в зависимости от ситуации.
Полное описание XML и связанных с ним технологий и инструментов выходит за рамки данной книги. В главе 9, “XML и .NET”, представлены основные сведения о расширенной поддержке XML на платформе .NET Framework, а в этой главе описываются основы интеграции ADO.NET и платформы .NET Framework. Более подробную информацию об этом можно найти в справочных материалах Visual Studio .NET и платформы .NET Framework, а также на Web-узле MDSN по адресу: http://msdn.microsoft.com.
Основные принципы чтения и записи XML-данных
В главах 5, "ADO.NET: объект DataSet", и 6, "ADO.NET: объект DataAdapter”, демонстрируются программируемые и прямые способы загрузки данных в объект DataSet из базы данных. Еще один метод загрузки данных основан на чтении XML- данных. Как и следовало ожидать, в объект DataSet данные можно записывать в XML-формате. Более того, объект DataSet обладает средствами чтения и записи XML-схем либо вместе с XML-данными, либо раздельно.
Чтение XML-данных
BADO.NET предусмотрены богатые и разнообразные инструменты чтения и записи XML-данных и XML-схем. Далее рассматриваются основные способы использования предназначенных для этого методов и свойств.
Как и в предыдущих главах, для демонстрации взаимодействия модели ADO.NET и XML здесь приводится практический пример их использования на основе простой формы.
ADO.NET и XML 351
Запустите интегрированную среду разработки Visual Studio .NET и создайте новый проект Visual Basic Windows Application. Для этого в диалоговом окне New Project (Новый проект) выберите тип проекта Visual Basic Project в области Project Types (Типы проектов), а затем шаблон Windows Application (Приложение Windows) в области Templates (Шаблоны).
Назовите проект ADO-XML.
Укажите путь к файлам проекта.
Увеличьте размер формы Forml.
В окне свойств Properties укажите значение frmXML для свойства (Name) и зна- чениеADO.NET and XML для свойства Text формы Forml.
В верхнем левом углу формы создайте кнопку, перетаскивая ее из панели элементов управления.
В окне свойств Properties укажите значение btnReadXML для свойства (Name) и значение Read XML для свойстваText этой кнопки.
В правой части формы создайте сетку DataGrid, перетаскивая ее из панели элементов управления.
В окне свойств Properties укажите значение grdData для свойства (Name) сетки.
УвеличьтеразмерсеткиgrdData,чтобыоназанимала до80%всейплощади
формы.
В верхней части файла введите следующий код:
Imports System
Imports System.Data
Imports System.Data.SqlClient
Затем в определении класса формы frmXML введите код из листинга 10.1.
Листинг 10.1. Код чтения содержимого XML-файла в объект DataSet
Private Sub btnReadXML_Click(ByVai sender As System.Object, _ ByVai e As System.EventArgs) Handles btnReadXML.Click
Dim dsPubs As New DataSet ()
' Чтение XML-данных из файла.
dsPubs.ReadXml("..\Pubs.xml")
' Связывание объекта DataSet с сеткой данных DataGrid.
grdData.DataMember = "publishers" grdData.Datasource = dsPubs
End Sub
Эта подпрограмма считывает XML-данные из файла pubs.xml в объект DataSet. Теперь объект DataSet и его данные могут использоваться любым из способов, описанных в предыдущих главах. Кроме того, эта подпрограмма связывает объект DataSetссеткойданныхDataGrid.В листинге10.2показаносодержимоефайла
pubs . xml, а на рис. 10.1 — данные в сетке DataGrid.
РИС.файлаxml в сетке Da
Листинг10.2. Содержимоефайла
<?xml
<NewDataSet>
<publishers>
3
Moon
<city>Boston</city>
<state>MA</state>
<country>USA</country>
</publishers>
<publlshers>
<pub_id>0877</pub_id>
<pub_name>Binnet
<city>Washington</city>
<state>DC</state>
<country>USA</country>
</publishers>
<publishers>
<pub_id>1389</pub_id>
<pub_name>Algodata Infosystems</pub_name>
<city>Berkeley</city>
<state>CA</state>
<country>USA</country>
</publishers>
<publishers>
<pub_id>1622</pub_id>
Lakes
<city>Chicago</city>
<state>IL</state>
<country>USA</country>
</publishers>
<publishers>
333
<pub_id>1756</pub_id>
<pub_name>Ramona Publishers</pub_name>
<city>Dallas</city>
<state>TX</state>
<country>USA</country>
</publishers>
<publishers>
<pub_id>9952</pub_id>
<pub_name>Scootney Books</pub_name>
<city>New York</city>
<state>NY</state>
<country>USA</country>
</publishers>
<publishers>
<pub_id>9999</pub_id>
<pub_name>Lucerne Publishing</pub_name>
<city>Paris</city>
<country>France</country>
</publishers>
</NewDataSet>
НА ЗАМЕТКУ '
—
После вызова метода ReadXML для загрузки данных в объект DataSet для свойства 1 RowState всех новых строк задается значение Added. Этот подход отличается от I принятого по умолчанию поведения, когда для загрузки данных в объект DataSet из : базы данных используется объект DataAdapter и для свойства RowState всех новых | строк задается значение Unchanged. Такой подход позволяет загружать данные из ! XML-источника и вставлять их в таблицу базы данных. Если вы не хотите этого де- I лать, то можно с помощью метода AcceptChanges переустановить значение Un- ; j changed ДЛЯ свойства RowState всех новых строк. Для изменения принятого по |
умолчанию поведения при загрузке данных в объект DataSet из базы данных можно I указать значение False для свойства AcceptChangesOnFill, что приведет к авто- j i матической установке значения Added для свойства RowState всех новых строк.
В данном примере демонстрируется простейший способ чтения XML-данных в объект DataSet, т.е. чтение из файла. Помимо этого способа, существует много других вариантов чтения XML-данных с помощью перегруженных версий метода ReadXML, например с помощью объектов Stream, TextReader или XmlReader. Соответствующие им перегруженные версии метода ReadXML содержат второй параметр со значением XmlReadMode. Этот параметр используется для указания способа интерпретации содержимого XML-источника и обработки схемы данных. В табл. 10.1 приведено краткое описание членов перечисленияXmlReadMode.
Таблица 10.1. Перечисление XmlReadMode
Имя члена перечисления
Описание
ReadSchema
Считывает любую встроенную схему и загружает ее вместе с данными в объ- ектDataSet. Таблицы, определенныевсхеме, вставляютсявобъектData- Set, но если схема определяет таблицу, которая уже есть в этом наборе данных, то генерируется исключительная ситуация
Окончание табл. 10.1
Имя члена перечисления
Описание
IgnoreSchema
Игнорирует любую встроенную схему и загружает данные в объект DataSet с помощью существующего определения схемы. Любые данные, которые не соответствуют схеме объекта DataSet, игнорируются и не загружаются.
Аналогично, если схема не определена, то данные не загружаются
InferSchema
Игнорирует любую встроенную схему и выводит схему на основе структуры данных, а затем загружает данные в объект DataSet. Дополнительные таблицы и поля, определенные в результате такого анализа структуры данных, добавляются в уже существующую схему в объекте DataSet. В случае конфликта определений генерируется исключительная ситуация
Fragment
Считывает все существующие XML-фрагменты и загружает данные в объект DataSet. Любые данные, которые не соответствуют схеме в объекте DataSet, игнорируются и не загружаются
DiffGram
Считывает данные в формате DiffGram и загружает данные в объект DataSet. Новые записи сливаются с уже существующими записями с такими же значениями уникального идентификатора, в противном случае в объекте DataSet создаются новые записи. В случае несоответствия схем генерируется исключительная ситуация. (Формат DiffGram более подробно описывается далее в главе. — Прим. ред.)
Auto
Этот режим используется по умолчанию. При этом выполняется одна из следующих операций: если XML-данные имеют формат DiffGram, то выбирается член DiffGramперечисления XmlReadMode; если схема определена в объекте DataSet или встроена в XML-документ, то выбирается член Readschema перечисления XmlReadMode; в остальных случаях выбирается член InferSchemaперечисления XmlReadMode
Для чтения только схемы данных (и игнорирования данных) предусмотрена перегруженная версия метода ReadXmlSchema, которая может применяться для считыва- ниясхемыобъектовDataTableобъектаDataSetтак, какпоказанониже.
MyDataSet.ReadXmlSchema("MySchemaFile.xml")
Те же четыре источника данных (файл, объекты Stream, TextReader и XmlReader) могут использоваться вместе с методом ReadXmlSchema. Объект DataSet имеет аналогичный набор методов для записи XML-данных.
Запись XML-данных
После загрузки в объект DataSet (независимо от способа и места загрузки) данные и/или схемы данных можно записать в XML-формате (с XML-схемой или без нее). Для демонстрации способов записи данных в XML-формате выполните перечисленные ниже действия.
Создайте в форме новую кнопку сразу под кнопкой Read XML, перетаскивая ее из панели инструментов.
335
В окне свойств Properties укажите значение btnWriteXMLдля свойства (Name) и значение Write XML для свойства Text этой кнопки.
Затем в определении класса формы frmXML введите код из листинга 10.3.
Листинг 10.3. Код сохранения содержимого объекта DataSet в виде XML-файла
Private Sub btnWriteXML_Click(ByVai sender As System.Object, _
By Vai e As System.EventArgs) Handles btnWriteXML.Click
Dim dsSales As New DataSet()
Dim cn As New SqlConnection
("data source=localhost;initial catalog=pubs;user id=sa")
Dim daAuthors As New SqlDataAdapter("select * from sales ", cn)
Dim daPublishers As New SqlDataAdapter("select * from stores ", cn)
Загрузка реляционных данных из базы данных.
daAuthors.Fill(dsSales, "Sales")
daPublishers.Fill(dsSales, "Stores")
' Запись XML-данных в файл.
dsSales.WriteXml("..\StoreSales.xml")
' Запись схемы в XSD-файл.
dsSales.WriteXmlSchema("..\StoreSales. xsd")
End Sub
В этой подпрограмме создаются два объекта— адаптера данных (daAuthors и daPublishers), которые затем используются для вставки данных в объект dsPubs из двух таблиц базы данных pubs СУБД SQL Server. В листинге 10.4 приведено содержимое файла StoreSales.xml, который создается в результате выполнения этой подпрограммы. Обратите внимание на то, что данный XML-документ содержит записи о продажах, а затем записи о магазинах. Этот подход имеет смысл, так как между ними не задано никакого отношения. Если бы таблицы Sales и Stores были связаны, то эти записи можно было вложить друг в друга. Пример такого вложения приводится далее, в бизнес-ситуации 10.1.
ЛИСТИНГ 10.4. Содержимое файла StoreSales . xml
<?xml version="1.0" standalone="yes"?>
<NewDataSet>
<Sales>
<stor_id>6380</stor_id>
<ord_num> 6 8 71 < / о r d_num>
<ord_date>l994-09-14T00:00:00.0000000+02:00</ord_date>
<qty>5</qty>
<payterms>Net 60</payterms>
<title_id>BU1032</title_id>
</Sales>
<Sales>
<stor_id>6380</stor_id>
<оrd_num>72 2a</оrd_num>
<ord_date>l994-09-13T00:00:00.0000000+02:00</ord_date>
<qty>3</qty>
<payterms>Net 60</payterms>
336
<title_id>PS209K/title_ld>
</Sales>
<Sales>
<stor_id>7066</stor_id>
< о r d_num> A2 9 7 6 < /ord_num>
<ord_date>1993-05-24T00:00:00 .0000000+02:00</ord_date>
<qty>50</qty>
<payterms>Net 30</payterms>
<title_id>PC8888</title_id>
</Sales>
<Sales>
<stor_id>7066</stor_id>
<ord_num>QA7442.3</ord_num>
<ord_date>1994-09-13T00:00:00.0000000+02:00</ord_date>
<qty>75</qty>
<payterms>ON invoice</payterms>
<title_id>PS209K/title_id>
</Sales>
<Sales>
<stor_id>7067</stor_id>
<ord_num>D4482</ord_num>
<ord_date>1994-09-14T00:00:00.0000000+02:00</ord_date>
<qty>10</qty>
<payterms>Net 60</payterms>
<title_id>PS2091</title_id>
</Sales>
<Sales>
<stor_id>7067</stor_id>
<ord_num>P2121</оrd_num>
<ord_date>1992-06-15T00:00:00.0000000+02:00</ord_date>
<qty>40</qty>
<payterms>Net 30</payterms>
<title_id>TC3218</title_id>
</Sales>
<Sales>
<stor_id>7067</stor_id>
<ord_num>P2121</ord_num>
<ord_date>1992-06-15T00:00:00.0000000+02:00</ord_date>
<qty>20</qty>
<payterms>Net 30</payterms>
<title_id>TC4203</title_id>
</Sales>
<Sales>
<stor_id>7067</stor_id>
<ord_num>P2121</ord_num>
<ord_date>1992-06-15T00:00:00.0000000+02:00</ord_date>
<qty>20</qty>
<payterms>Net 30</payterms>
<title_id>TC7777</title_id>
</Sales>
<Sales>
<stor_id>713K/stor_id>
<оrd_num>N914008</оrd_num>
<ord_date>1994-09-14T00:00:00.0000000+02:00</ord_date>
<qty>20</qty>
<payterms>Net 30</payterms>
<title_id>PS2091</title_id>
</Sales>
<Sales>
<stor _id>7131</stor_id>
337
оrd_num>N914 014 < /ord_num>
<ord_date>1994-09-14T00:00:00.0000000+02:00</ord_date>
<qty>25</qty>
<payterms>Net 30</payterms>
<title_id>MC3021</title_id>
</Sales>
<Sales>
<stor_id>713K/stor_id>
<ord_num>P3087a</ord_num>
<ord_date>1993-05-29T00:00:00.0000000+02:00</ord_date>
<qty>20</qty>
<payterms>Net 60</payterms>
<title_id>PS1372</title_id>
</Sales>
<Sales>
<stor_id>7131</stor_id>
<ord_num>P3087a</ord_num>
<ord_date>1993-05-29T00:00:00.0000000+02:00</ord_date>
<qty>25</qty>
<payterms>Net 60</payterms>
<title_id>PS2106</title_id>
</Sales>
<Sales>
<stor_id>713K/stor_id>
o r d_num>P3087a</o r d_num>
<ord_date>1993-05-29T00:00:00.0000000+02:00</ord_date>
<qty>15</qty>
<payterms>Net 60</payterms>
<title_id>PS3333</title_id>
</Sales>
<Sales>
<stor_id>7131</stor_id>
<ord_num>P3087a</ord_num>
<ord_date>l993-05-29T00:00:00.0000000+02:00</ord_date>
<qty>25</qty>
<payterms>Net 60</payterms>
<title_id>PS7777</title_id>
</Sales>
<Sales>
<stor_id>7896</stor_id>
о r d_num> QQ2 2 9 9 < / о r d_num>
<ord_date>1993-10-28T00:00:00.0000000+02:00</ord_date>
<qty>15</qty>
<payterms>Net 60</payterms>
<title_id>BU7832</title_id>
</Sales>
<Sales>
<stor_id>7896</stor_id>
<ord_num>TQ456</ord_num>
<ord_date>1993-12-12T00:00:00.0000000+02:00</ord_date>
<qty>10</qty>
<payterms>Net 60</payterms>
<title_id>MC2222</title_id>
</Sales>
<Sales>
<stor_id>7896</stor_id>
<ord_num>X999</ord_num>
<ord_date>1993-02-21T00:00:00.0000000+02:00</ord_date>
<qty>35</qty>
<payterms>ON invoice</payterms>
338
<title_id>BU2075</title_id>
</Sales>
<Sales>
<stor_id>8042</stor_id>
<оrd_num>423LL922</o rd_num>
<ord_date>1994-09-14T00:00:00.0000000+02:00</ord_date>
<qty>15</qty>
<payterms>ON invoice</payterms>
<title_id>MC3021</title_id>
</Sales>
<Sales>
<stor_id>8042</stor_id>
<ord_num>423LL930</ord_num>
<ord_date>1994-09-14T00:00:00.0000000+02:00</ord_date>
<qty>10</qty>
<payterms>ON invoice</payterms>
<title_id>BU1032</title_id>
</Sales>
<Sales>
<stor_id>8042</stor_id>
<оrd_num>P723</оrd_num>
<ord_date>1993-03-llT00:00:00.0000000+02:00</ord_date>
<qty>25</qty>
<payterms>Net 30</payterms>
<title_id>BUllll</title_id>
</Sales>
<Sales>
<stor_id>8042</stor_id>
<ord_num>QA879.l</ord_num>
<ord_date>1993-05-22T00:00:00.0000000+02:00</ord_date>
<qty>30</qty>
<payterms>Net 30</payterms>
<title_id>PC1035</title_id>
</Sales>
<Stores>
<stor_id>6380</stor_id>
<stor_name>Eric the Read Books</stor_name>
<stor_address>788 Catamaugus Ave.</stor_address>
<city>Seattle</city>
<state>WA</state>
<zip>98056</zip>
</Stores>
<Stores>
<stor_id>7066</stor_id>
<stor_name>Barnum's</stor_name>
<stor_address>567 Pasadena Ave.</stor_address>
<city>Tustin</city>
<state>CA</state>
<zip>92789</zip>
</Stores>
<Stores>
<stor_id>7067</stor_id>
<stor_name>News & Brews</stor_name>
<stor_address>577 First St.</stor_address>
<city>Los Gatos</city>
<state>CA</state>
<zip>96745</zip>
</Stores>
<Stores>
<stor _id>7131</stor id>
339
<stor_name>Doc-U-Mat: Quality Laundry and Books</stor_name>
<stor_address>24-A Avogadro Way</stor_address>
<city>Remulade</city>
<state>WA</state>
<zip>98014</zip>
</Stores>
<Stores>
<stor_id>7896</stor_id>
<stor_name>Fricative Bookshop</stor_name>
<stor_address>89 Madison St.</stor_address>
<city>Fremont</city>
<state>CA</state>
<zip>90019</zip>
</Stores>
<Stores>
<stor_id>8042c/stor_id>
<stor_name>Bookbeat</stor_name>
<stor_address>679 Carson St.</stor_address>
<city>Portland</city>
<state>OR</state>
<zip>89076</zip>
</Stores>
</NewDataSet>
В приведенном примере демонстрируется простейший способ записи данных из объекта DataSet в XML-файл. Помимо этого способа существуют другие варианты записи данных и схемы с помощью перегруженных версий метода WriteXML. Соответствующие им перегруженные версии метода ReadXML содержат второй параметр со значением XmlWriteMode. В табл. 10.2 приведено краткое описание членов перечисления XmlWriteMode.
Таблица 10.2. Перечисление XmlWriteMode
Имя члена перечисления
Dif f Gram
WriteSchema
IgnoreSchema
Описание
Записывает данные из объекта DataSet в виде объекта DiffGram с текущими исходными значениями всех записей
Записывает данные из объекта DataSet в XML-формате вместе с их схемой. Если объект DataSet содержит только схему, то записывается только схема. Если в объекте DataSet не определена схема, то ничего не записывается
Этот режим используется по умолчанию для записи содержимого объекта DataSet в XML-формате без схемы
НА ЗАМЕТКУ
Объект DataSet имеет методGetXml, который возвращает XML-строку с данными из | объекта DataSet. Точно такой же результат можно получить, используя метод j WriteXml со вторым параметром XmlWriteMode, который имеет значение Ignore- Schema. Извлечение данных в виде одной строки иногда позволяет более гибко про; граммировать доступ к данным, но требует больших усилий, особенно если вам нуж- i но всего лишь записать данные в файл.
Для записи схемы данных из объекта DataSet в отдельном XSD-файле (вместо встраивания ее в данные) предусмотрен метод WriteXmlSchema, который используется так, как показано ниже.
dsSales.WriteXmlSchema
В листинге 10.5 приведено содержимое файла StoreSales .xsd, полученного в результате выполнения этого метода.
Листинг 10.5. Содержимое файла StoreSales. xsd, который является схемой из объекта ds Sales
<?xml version="l.0" standalone="yes"?>
<xs:schema id="NewDataSet" xmlns=""
xmlns:xs="http: / /ww.w3 .org/2001/XMLSchema" xmlns:msdata="urn:schemas- microsoft-com:xml-msdata">
<xs:element name="NewDataSet" msdata:IsDataSet="true">
<xs:complexType>
<xs:choice maxOccurs="unbounded">
<xs:element name="Sales">
<xs:complexType>
<xs:sequence>
<xs:element name="stor_id" type="xs:string" min0ccurs="0" />
<xs:element name="ord_num" type="xs:string" minOccurs="0" />
<xs:element name="ord_date" type="xs:dateTime" min0ccurs=”0" />
<xs:element name="qty" type="xs:short" minOccurs="0" />
<xs:element name="payterms" type="xs:string" minOccurs="0" />
<xs:element name="title_id" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Stores">
<xs:complexType>
<xs:sequence>
<xs:element name="stor_id" type="xs:string" min0ccurs=”0" />
<xs:element name="stor_name" type="xs:string" minOccurs="0" />
<xs:element name="stor_address" type="xs:string" minOccurs="0" />
<xs:element name="city" type="xs:string" minOccurs="0" />
<xs:element name="state" type="xs:string" minOccurs="0" />
<xs:element name="zip" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>
Формат DiffCram
Рассмотрим теперь формат DiffGram, который используется членами перечислений XmlReadMode и XmlWriteMode. Это XML-формат, содержащий не только текущие значения элементов-данных, но и их исходные значения, которые были изменены или удалены (вслед за последним вызовом метода AcceptChanges). Итак, DiffGram представляет собой формат сериализации, который используется для пере-
ADO.NET и XML 341
дачи данных другому процессу или компьютеру. Поскольку эти данные фактически имеют XML-формат, они могут легко передаваться другим платформам, например UNIX или Linux.
Каждый файл формата DifTGram имеет три раздела. Первый раздел содержит текущие значения всех записей объекта DataSet, независимо от того, изменялись они или нет. Любой измененный элемент (запись) обозначается как diffgr:hasChanges= "modified", а любой добавленный элемент (запись) — как diffgr:hasChanges= "inserted". Второй раздел содержит исходные значения всех измененных и удаленных записей. Его элементы связаны с соответствующими элементами первого раздела, которые обозначаются как diffgr :id="xxx", где ххх — идентификатор записи. Третий раздел содержит информацию об ошибках работы с записями. Его элементы связаны аналогичным образом с соответствующими элементами первого раздела, которые обозначаются какdiffgrdd="xxx".
Для генерации XML-файла в формате DiffGram нужно внести приведенные ниже изменения (они обозначены полужирным начертанием) в подпрограмму btnWriteXML_Click.H3 листинга 10.3.
Private Sub btnWriteXML_Click(ByVai sender As System.Object, _
ByVai e As System.EventArgs) Handles btnWriteXML.Click
Dim dsSales As New DataSet()
Dim cn As New SqlConnection
("data source=localhost;initial catalog=pubs;user id=sa") Dim daAuthors As New SqlDataAdapter("select * from sales ", cn)
Dim daPublishers As New SqlDataAdapter("select * from stores ", cn)
' Загрузка реляционных данных из базы данных.
daAuthors.Fill(dsSales, "Sales")
daPublishers.Fill(dsSales, "Stores")
' Запись XML-данных в файл.
dsSales.WriteXml("..\StoreSales.xml")
' Запись схемы в XSD-файл.
dsSales.WriteXmlSchema("..\StoreSales.xsd")
' Пример изменения, удаления и вставки новой записи.
dsSales.Tables("Stores").Rows(O)("stor_id") = 9991 Изменение
dsSales.Tables("Stores").Rows(l).Delete()' Удаление
Dim rr As DataRow =
rr("stor_name") = "New Store"
dsSales.Tables("Stores").Rows.Add(rr)' Вставка
' Сохранение только измененных записей в формате DiffGram.
Dim ChangedDataSet = dsSales.GetChanges()
ChangedDataSet.WriteXml("..\Changes.xml", XmlWriteMode.DiffGram)
' Сохранение всех записей в формате DiffGram.
dsSales.WriteXml("..\DiffGram.xml", XmlWriteMode.DiffGram)
End Sub
В листинге 10.6 приведено содержимое файла Diff Gram, xml, полученного в результате щелчка на кнопке Write XML формы проекта ADO-XML. Внесенные в таблицу Stores изменения выделены полужирным начертанием. Удаленная запись отсутствует
в разделе текущих данных, но присутствует в разделе исходного состояния вместе с исходным значением измененной записи. Кроме того, раздел с текущими данными содержит новую запись с отметкой "inserted" (вставлена).
Листинг 10.6. XML-файл Diff Gram.xml в формате DiffCram с одной вставленной записью, одной удаленной записью и одной измененной записью
<?xml version="l.0" standalone="yes"?>
<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-vl">
<NewDataSet>
<Sales diffgr:id="Salesl" msdata:rowOrder="0">
<stor_id>6380</stor_id>
<оrd_num>6871</оrd_num>
<ord_date>1994-09-14T00:00:00.0000000+02:00</ord_date>
<qty>5</qty>
<payterms>Net 60</payterms>
<title_id>BU1032</title_id>
</Sales>
<Sales diffgr:id="Sales2" msdata:rowOrder="l">
<stor_id>6380</stor_id>
<оrd_num>7 22 a</оrd_num>
<ord_date>1994-09-13T00:00:00.0000000+02:00</ord_date>
<qty>3</qty>
<payterms>Net 60</payterms>
<title_id>PS2091</title_id>
</Sales>
<Sales diffgr:id="Sales3" msdata:rowOrder="2">
<stor_id>7066</stor_id>
<ord_num>A2 97 6</ord_num>
<ord_date>1993-05-24T00:00:00.0000000+02:00</ord_date>
<qty>50</qty>
<payterms>Net 30</payterms>
<title_id>PC8888</title_id>
</Sales>
<Sales diffgr:id="Sales4" msdata:rowOrder="3">
<stor_id>7066</stor_id>
<ord_num>QA7442.3</ord_num> "
<ord_date>1994-09-13T00:00:00.0000000+02:00</ord_date>
<qty>75</qty>
<payterms>ON invoice</payterms>
<title_id>PS2091</title_id>
</Sales>
<Sales diffgr:id="Sales5" msdata:rowOrder="4">
<stor_id>7067</stor_id>
<ord_num>D4482</ord_num>
<ord_date>1994-09-14T00:00:00.0000000+02:00</ord_date>
<qty>10</qty>
<payterms>Net 60</payterms>
<title_id>PS2091</title_id>
</Sales>
<Sales diffgr:id="Sales6" msdata:rowOrder="5">
<stor_id>7067</stor_id>
<ord_num>P2121</ord_num>
<ord_date>1992-06-15T00:00:00.0000000+02:00</ord_date>
<qty>40</qty>
<payterms>Net 30</payterms>
<title_id>TC3218</title_id>
</Sales>
343
<Sales diffgr:id="Sales7" msdata:rowOrder="6">
<stor_id>7067</stor_id>
<ord_num>P212K/ord_num>
<ord_date>1992-06-15T00:00:00.0000000+02:00</ord_date>
<qty>20</qty>
<payterms>Net 30</payterms>
<title_id>TC4203</title_id>
</Sales>
<Sales diffgr:id="Sales8" msdata:rowOrder="7">
<stor_id>7067</stor_id>
or d_num> P212K/o r d_num>
<ord_date>1992-06-15T00:00:00.0000000+02:00</ord_date>
<qty>20</qty>
<payterms>Net 30</payterms>
<title_id>TC7777</title_id>
</Sales>
<Sales diffgr:id="Sales9" msdata:rowOrder="8">
<stor_id>7131</stor_id>
<оrd_num>N914 0 0 8 </ord_num>
<ord_date>1994-09-14T00:00:00.0000000+02:00</ord_date>
<qty>20</qty>
<payterms>Net 30</payterms>
<title_id>PS209K/title_id>
</Sales>
<Sales diffgr:id="SaleslO" msdata:rowOrder="9">
<stor_id>7131</stor_id>
о rd_num>N914014с/о rd_num>
<ord_date>1994-09-14T00:00:00.0000000+02.:00</ord_date>
<qty>25</qty>
<payterms>Net 30</payterms>
<title_id>MC3021</title_id>
</Sales>
<Sales diffgr: id="Salesll1 msdata: row0rder="10">
<stor_id>7131</stor_id>
<ord_num>P3087a</ord_num>
<ord_date>1993-05-29T00:00:00.0000000+02:00</ord_date>
<qty>20</qty>
<payterms>Net 60</payterms>
<title_id>PS1372</title_id>
</Sales>
<Sales diffgr:id="Sales12" msdata:rowOrder="ll">
<stor_id>713K/stor_id>
о r d_num> P 3 0 8 7 a< / or d_num>
<ord_date>1993-05-29T00:00:00.0000000+02:00</ord_date>
<qty>25</qty>
<payterms>Net 60</payterms>
<title_id>PS2106</title_id>
</Sales>
<Sales diffgr:id="Salesl3" msdata:rowOrder="12">
<stor_id>7131</stor_id>
<ord_num>P3087a</ord_num>
<ord_date>1993-05-29T00:00:00.0000000+02:00</ord_date>
<qty>15</qty>
<payterms>Net 60</payterms>
<title_id>PS3333</title_id>
</Sales>
<Sales diffgr:id="Salesl4" msdata:rowOrder="13">
<stor_id>7131</stor_id>
<ord_num>P3087a</ord_num>
cord—date>1993-05-29T00:00:00.0000000+02:00</ord_date>
<qty>25</qty>
<payterms>Net 60</payterms>
<title_id>PS7777</title_id>
</Sales>
<Sales diffgr:id="Salesl5" msdata:rowOrder="14">
<stor_id>7896</stor_id>
<ord_num>QQ2299</ord_num>
<ord_date>1993-10-28TOO:00:00.0000000+02:00</ord_date>
<qty>15</qty>
<payterms>Net 60</payterms>
<title_id>BU7832</title_id>
</Sales>
<Sales diffgr:id="Salesl6" msdata:rowOrder="15">
<stor_id>7896</stor_id>
<ord_num>TQ456</ord_num>
<ord_date>1993-12-12T00:00:00.0000000+02:00</ord_date>
<qty>10</qty>
<payterms>Net 60</payterms>
<title_id>MC2222</title_id>
</Sales>
<Sales diffgr:id="Sales17" msdata:rowOrder="16">
<stor_id>7896</stor_id>
cord_num>X9 9 9 </оrd_num>
<ord_date>1993-02-21T00:00:00.0000000+02:00</ord_date>
<qty>35</qty>
<payterms>ON invoice</payterms>
<title_id>BU2075</title_id>
</Sales>
<Sales diffgr:id="Salesl8" msdata:rowOrder="17">
<stor_id>8042</stor_id>
co rd_num> 4 2 3 LL 92 2 с/оrd_num>
cord_date>1994-09-14T00:00:00.0000000+02:00c/ord_date>
<qty>15</qty>
<payterms>ON invoice</payterms>
<title_id>MC3021</title_id>
</Sales>
<Sales diffgr:id="Salesl9" msdata:rowOrder="18">
cstor_id>8042c/stor_id>
<ord_num>423LL930</ord_num>
<ord_date>1994-09-14T00:00:00.0000000+02:00</ord_date>
<qty>10</qty>
<payterms>ON invoice</payterms>
<title_id>BU1032</title_id>
</Sales>
<Sales diffgr:id="Sales20" msdata:rowOrder="19">
<stor_id>8042</stor_id>
<ord_num>P723</ord_num>
<ord_date>1993-03-HT00:00:00.0000000+02:00</ord_date>
<qty>25</qty>
<payterms>Net 30</payterms>
<title_id>BUllll</title_id>
</Sales>
<Sales diffgr:id="Sales21" msdata:rowOrder="20">
<stor_id>8042</stor_id>
<ord_num>QA879.lc/ord_num>
<ord_date>1993-05-22T00:00:00.0000000+02:00</ord_date>
<qty>30</qty>
<payterms>Net 30</payterms>
<title_id>PC1035</title_id>
</Sales>
<Stores diffgr:id="Storesl" msdata:rowOrder="0"<₽ diffgr:hasChanges="modified">
345
<stor_id>999</stor_id>
<stor_name>Eric the Read Books</stor_name>
<stor_address>788 Catamaugus Ave.</stor_address>
<city>Seattle</city>
<state>WA</state>
<zip>98056</zip>
</Stores>
<Stores diffgr:id="Stores3" msdata:row0rder="2">
<stor_id>7067</stor_id>
<stor_name>News & Brews</stor_name>
<stor_address>577 First St.</stor_address>
<city>Los Gatos</city>
<state>CA</state>
<zip>96745</zip>
</Stores>
<Stores diffgr:id="Stores4" msdata:row0rder="3">
<stor_id>7131</stor_id>
<stor_name>Doc~U-Mat: Quality Laundry and Books</stor_name>
<stor_address>24-A Avogadro Way</stor_address>
<city>Remulade</city>
<state>WA</state>
<zip>98014</zip>
</Stores>
<Stores diffgr:id="Stores5" msdata:row0rder="4">
<stor_id>7896</stor_id>
<stor_name>Fricative Bookshop</stor_name>
<stor_address>89 Madison St.</stor_address>
<city>Fremont</city>
<state>CA</state>
<zip>90019</zip>
</Stores>
<Stores diffgr:id="Stores6" msdata:rowOrder="5">
<stor_id>8042</stor_id>
<stor_name>Bookbeat</stor_name>
<stor_address>679 Carson St.</stor_address>
<city>Portland</city>
<state>OR</state>
<zip>8907 6</zip>
</Stores>
<Stores diffgr:id="Stores7" msdata:rowOrder="6"<P
diffgr:hasChanges="inserted">
<stor_name>New Store</stor_name>
</Stores>
</NewDataSet>
<diffgr:before>
<Stores diffgr:id="Storesl" msdata:rowOrder="0">
<stor_id>6380</stor_id>
<stor_name>Eric the Read Books</stor_name>
<stor_address>788 Catamaugus Ave.</s tor_addres s>
<city>Seattle</city>
<state>WA</state>
<zip>98056</zip>
</Stores>
<Stores diffgr:id="Stores2" msdata:rowOrder="l">
<stor_id>7066</stor_id>
<stor_name>Barnum's</stor_name>
<stor_address>567 Pasadena Ave.</stor_address>
<city>Tustin</city>
<state>CA</state>
<zip>92789</zip>
</Stores>
</diffgr:before>
</diffgr:diffgram>
НА ЗАМЕТКУ —;;
Для сохранения только измененных записей в форматеDiffGram следует использо-
1 вать метод GetChanges:'
i Dim ChangedDataSet =
ChangedDataSet.WriteXml("..\Changes.xml", XmlWriteMode.DiffGram)
i В листинге 10.7 приведено содержимое файла DiffGram. xml, полученного в pe-
5 зультате выполнения этого метода.
Листинг 10.7. XML-файл Changes. xml в формате DiffGram только
с измененными записями
<?xml version="l.0" standalone="yes"?>
<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr=”urn:schemas-microsoft-com:xml-diffgram- vl">
<NewDataSet>
<Stores diffgr:id="Storesl" msdata:rowOrder="0"
diffgr:hasChanges="modified">
<stor_id>999</stor_fd>
<stor_name>Eric the Read Books</stor_name>
<stor_address>788 Catamaugus Ave.</stor_address>
<city>Seattle</city>
<state>WA</state>
<zip>98056</zip>
</Stores>
<Stores diffgr:id="Stores3" msdata:rowOrder="2"
diffgr:hasChanges="inserted">
<stor_name>New Store</stor_name>
</Stores>
</NewDataSet>
<diffgr:before>
<Stores diffgr:id="Storesl" msdata:row0rder="0">
<stor_id>6380</stor_id>
<stor_name>Eric the Read Books</stor_name>
<stor_address>788 Catamaugus Ave.</stor_address>
<city>Seattle</city>
<state>WA</state>
<zip>98056</zip>
</Stores>
<Stores diffgr:id="Stores2" msdata:rowOrder="l">
<stor_id>7066</stor_id>
<stor_name>Barnum's</stor_name>
<stor_address>567 Pasadena Ave.</stor_address>
<city>Tustin</city>
<state>CA</state>
<zip>92789</zip>
</Stores>
</diffgr:before>
</diffgr:diffgram>
347
Бизнес-ситуация 10.1: подготовка XML-файлов для бизнес-партнеров
Компания Jones Novelty обменивается информацией с помощью электронных средств передачи данных со многими своими поставщиками и партнерами. Развитие этой тенденции, вероятно, приведет к тому, что компании придется спустя какое-то время применить качественно новое решение, например систему Microsoft BizTalk Server. В настоящее время Брэд Джонс стремится удовлетворить текущие потребности и сохранить конкурентоспособность, применяя XML для передачи транзакций. Для этого будут использованы уже описанные возможности XML и ряд других, которые описываются далее. Следует отметить. что многие требования можно удовлетворить даже без использования "мощных и тяжеловесных" платформ, инструментов и технологий, например XSLT.
Сначала следует организовать отправку XML-файла с перечнем товаров на складах. Для этого должны быть посланы сведения из всех полей таблицы tbllnventory, за исключением поля WholesalePrice. Хотя это можно организовать с помощью простого запроса, который включает все поля, кроме WholesalePrice, в данном примере используется способ на основе XML-свойств. Для создаваемого XML-файл существует еще одно требование: в него необходимо включить XS D-схему с описанием всех полей как элементов, за исключением поля ID, которое передается с помощью атрибута.
Для создания этого приложения выполните перечисленные ниже действия.
Запустите интегрированную среду разработки Visual Studio .NET и создайте новый проект Visual Basic Windows Application. Для этого в диалоговом окне New Project (Новый проект) выберите тип проекта Visual Basic Project в области Project Types (Типы проектов), а затем шаблон Windows Application (Приложение Windows) в области Templates (Шаблоны).
Назовите проект BusinessCaselO.
Укажите путь к файлам проекта.
Увеличьте размер формы Forml.
В окне свойств Properties укажите значение frmPrepareXMLдля свойства (Name) и значение Prepare XML для свойства Text формы Forml.
В верхнем левом углу формы создайте кнопку, перетаскивая ее из панели элементов управления.
В окне свойств Properties укажите значение btnlnventory для свойства (Name) и значение Create Inventory XML для свойства Text этой кнопки.
В верхней части файла введите следующий код:
Imports System
Imports System.Data
Imports System.Data.SqlClient
Затем в определении класса формы frmPrepareXML введите приведенный ниже код.
Dim cn As New SqlConnection _
("data source=localhost;initial catalog=Novelty;user id=sa")
Private Subsender As
eAsHandles
DimAsNew
DimAsNew SqlDataAdapter
("select *from
dalnventory.Fill(dslnventory, "tbllnventory")
' Сохранение поля ID как XML-атрибута, а не =
MappingType.Attribute
' Сокрытие поляв сохраненном
dslnventory.Tables("tbllnventory") .Columns _
=
' Сохранение данных в XML-файле, включая встроенную схему. dslnventory.WriteXml("..\Inventory.xml", XmlWriteMode.WriteSchema)
End Sub
После вставки данных в объект DataSet для формирования XML-файла используются следующие два выражение. Первое выражение указывает на то, что поле ID следует сохранить как XML-атрибут:
=
MappingType.Attribute
Второе выражение указывает на сокрытие поля WholesalePrice в сохраненном XML-файле:
dslnventory.Tables("tbllnventory").Columns _ =
Наконец, во время сохранения данных используется второй параметр метода который указывает на необходимость включениявместе с дан
ными. Полученный в результате XML-файл показан в листинге 10.8.
Листинг 10.8. Пример сохранения таблицыв виде XML-файла
<?xml version="l.0" standalone="yes"?>
<NewDataSet>
<xs:schema id="NewDataSet" xmlns=""
xmlns:xs="http://www.w3.org/SOOl/XMLSchema"^
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xs:element name="NewDataSet" msdata:IsDataSet="true">
<xs:complexType>
<xs:choice maxOccurs="unbounded">
<xs:element name="tbllnventory">
<xs:complexType>
<xs:sequence>
<xs:element name="ProductName" type="xs:string" <9 />
<xs:element name="RetailPrice" type="xs:decimal" <f />
<xs:element name="Description" type="xs:string" <9 />
</xs:sequence>
/>
349
<xs:attribute name="WholesalePrice" <9 type="xs:decimal" use="prohibited" /> c/xs:complexType> c/xs:element> c/xs:choice>
c/xs:complexType>
</xs:element>
c/xs:schema>
ctbllnventory ID="1">
cProductName>Rubber Chickenc/ProductName>
<RetailPrice>2.99c/RetailPrice>
<Description>The quintessential rubber chicken. c/Description> c/tbllnventory>
ctbllnventory ID="2">
cProductName>Joy Buzzer</ProductName>
cRetailPrice>9.99c/RetailPrice>
cDescription>They will get a real shock out of this.</Description> c/tbllnventory>
ctbllnventory ID="3">
cProductName>Seltzer Bottle</ProductName>
<RetailPrice>15.24</RetailPrice>
<Description>Seltzer sold separately.c/Description>
c/tblInventory>
ctbllnventory ID="4">
cProductName>Ant Farmc/ProductName>
CRetailPrice>14.99c/RetailPrice>
cDescription>Watch ants where they live and breed.</Description> c/tblInventory>
ctbllnventory ID="5">
cProductName>Wind-Up Robot</ProductName>
<RetailPrice>29.99</RetailPrice>
<Description>Giant robot: attack toybox!</Description>
c/tblInventory>
ctbllnventory ID="6">
cProductName>Rubber Eyeballsc/ProductName>
cRetailPrice>0.99</RetailPrice>
cDescription>Peek-a-boo!c/Description>
c/tbllnventory>
ctbllnventory ID="7">
<ProductName>Doggy Messc/ProductName>
cRetailPrice>l.99c/RetailPrice>
cDescription>Yechhh!c/Description>
c/tbllnventory>
ctbllnventory ID="8">
cProductName>Mini-CameraC/ProductName>
<RetailPrice>9.99c/RetailPrice>
<Description>For future spies!c/Description>
c/tbllnventory>
ctbllnventory ID="9">
<ProductName>Glow Wormsc/ProductName>
cRetailPrice>l.99c/RetailPrice>
cDescription>Makes them easy to findc/Description> c/tbllnventory>
ctbllnventory ID="10">
<ProductName>Insect Popsc/ProductName>
cRetailPrice>0.99c/RetailPrice>
<Description>Special treatsc/Description>
</tblInventory> ctbllnventory ID="H">
<ProductName>Alien Alarm Clock</ProductName>
<RetailPrice>45.99</RetailPrice>
<Description>Do you know what time it is out there?</Description>
</tbl!nventory>
ctbllnventory ID="12">
<ProductName>Cinnamon Toothpicks</ProductName>
<RetailPrice>l.99</RetailPrice>
<Description>Really wakes up your mouth</Description>
</tbl!nventory>
</NewDataSet>_
Для составления ведомости на выдачу заработной платы сотрудникам компании нужно создать код сохранения информации о сотрудниках в формате XML для каждого отдела. Для этого разработчик базы данных должен включить в форму f rmPrepareXML вторую кнопку btnEmployees и вставить в код класса формы frmPrepareXML код из листинга 10.9.
Листинг 10.9. Код сохранения данных из таблицtblEmployee
И tblDepartment В XML-файле
Private Sub btnEmployees_Click(ByVai sender As System.Object, _ ByVai e As System.EventArgs) Handles btnEmployees .Click
Dim dsEmployees As New DataSet ( )
Dim daEmployees As New SqlDataAdapter
("select * from tbiEmpioyee", cn)
Dim daDepartments As New SqlDataAdapter
("select * from tblDepartment", cn)
daDepartments . Fill (dsEmployees, "tblDepartment")
daEmployees . Fill (dsEmployees, "tblEmployee")
' Определение отношения между таблицами.
dsEmployees.Relations.Add( "DepartmentEmployees", _ dsEmployees. Tables ( "tblDepartment") .Columns("ID"), _ dsEmployees .Tables( "tblEmployee") .Columns("DepartmentID") )
' Сохранение данных в XML-файле.
dsEmployees.WriteXml(" . .\Employees.xml")
EndSub_
В этом коде для сохранения данных из таблиц tblDepartment и tbiEmpioyee в XML-файле используются предлагаемые по умолчанию параметры объекта DataSet. Полученный в результате XML-файл Employees . xml показан в листинге 10.10.
Листинг 10.10. XML-файл Employees.xml, полученный в результате Сохранения данных ИЗ ТабЛИЦ tblDepartment И tbiEmpioyee
<?xml version="l.0" standalone="yes"?>
<NewDataSet>
<tblDepartment>
<ID>K/ID>
<DepartmentName>Administration</DepartmentName>
</tblDepartment>
<tblDepartment>
<1D>2</ID>
351
<DepartmentName>Engineering</DepartmentName>
</tb!Department>
<tb!Department>
<ID>3</ID>
<DepartmentName>Sales</DepartmentName>
</tblDepartment>
<tb!Department>
<ID>4</ID>
departmentName>Marketing</DepartmentName>
</tb!Department>
<tblEmployee>
<ID>1</ID>
<FirstName>Carole</FirstName>
<LastName>Vermeren</LastName>
<DepartmentID>2</DepartmentID>
<Salary>222</Salary>
</tblEmployee>
<tblEmployee>
<ID>2</ID>
<FirstName>Cathy</FirstName>
<LastName>Johnson</LastName>
departmentID>2</DepartmentID>
<Salary>13000</Salary>
</tblEmployee>
<tblEmployee>
<ID>3</ID>
<FirstName>Eric</FirstName>
<LastName>Haglund</LastName> departmentID>4</DepartmentID>
<Salary>12000</Salary>
</tblEmployee>
<tblEmployee>
<ID>4</ID>
<FirstName>Julie</FirstName>
<LastName>Ryan</LastName> departmentID>1</Department!D>
<Salary>4000</Salary>
</tblEmployee>
<tblEmployee>
<ID>5</ID>
<FirstName>Richard</FirstName>
<LastName>Halpin</LastName>
departmentID>2c/Department!D>
<Salary>10000</Salary>
</tb!Employee>
<tblEmployee>
<ID>6</ID>
<FirstName>Kathleen</FirstName>
<LastName>Johnson</LastName>
<DepartmentID>3</DepartmentID>
<Salary>18000</Salary>
</tblEmployee>
<tblEmployee>
<ID>7</ID>
<FirstName>Sorel</FirstName>
<LastName>Polito</LastName>
<DepartmentID>4</DepartmentID>
<Salary>28000</Salary>
</tblEmployee>
352
<tblEmployee>
<io>e</iD>
<FirstName>Sorel</FirstName>
<LastName>Terman</LastName>
<DepartmentID>l</DepartmentID>
<Salary>8000</Salary>
</tblEmployee>
<tblEmployee>
<ID>9</ID>
<FirstName>Randy</FirstName>
<LastName>Hobaica</LastName>
<DepartmentID>2</DepartmentID>
<Salary>18000</Salary>
</tblEmployee>
<tblEmployee>
<ID>10</ID>
<FirstName>Matthew</FirstName>
<LastName>Haglund</LastName>
<DepartmentID>3</DepartmentID>
<Salary>30000</Salary>
</tblEmployee>
<tblEmployee>
<ID>1K/ID>
<FirstName>Cathy</FirstName>
<LastName>Vermeren</LastName>
<DepartmentID>4</DepartmentID>
<Salary>0</Salary>
</tblEmployee>
<tblEmployee>
<ID>12</ID>
<FirstName>Brad</FirstName>
<LastName>Townsend</LastName>
<DepartmentID>2</DepartmentID>
<Salary>12000</Salary>
</tblEmployee>
<tblEmployee>
<ID>13</ID>
<FirstName>Jennifer</FirstName>
<LastName>Eves</LastName>
<DepartmentID>2</DepartmentID>
<Salary>26000</Salary>
</tblEmployee>
<tblEmployee>
<ID>14</ID>
<FirstName>Steve</FirstName>
<LastName>Marshall</LastName>
<DepartmentID>3</DepartmentID>
<Salary>42000</Salary>
</tblEmployee>
<tblEmployee>
<ID>15</ID>
<FirstName>Laura</FirstName>
<LastName>Davidson</LastName>
<DepartmentID>4</DepartmentID>
<Salary>60000</Salary>
</tblEmployee>
<tblEmployee>
<ID>16</ID>
<FirstName>Angela</FirstName>
353
<LastName>Stefanac</LastName>
<DepartmentID>2</DepartmentID>
<Salary>16000</Salary>
</tblEmployee>
<tblEmployee>
<ID>17</ID>
<FirstName>Marjorie</FirstName>
<LastName>Bassett</LastName> departmentID>2</DepartmentID>
<Salary>34000</Salary>
</tblEmployee>
<tblEmployee>
<ID>18</ID>
<FirstName>Joe</FirstName>
<LastName>Chideya</LastName>
<DepartmentID>3</DepartmentID>
<Salary>54000</Salary>
</tblEmployee>
<tblEmployee>
<ID>19</ID>
<FirstName>Katie</FirstName>
<LastName>Chideya</LastName> departmentID>4</DepartmentID>
<Salary>76000</Salary>
</tblEmployee>
<tblEmployee>
<ID>20</ID>
<FirstName>Terri</FirstName>
<LastName>Allen</LastName> department ID>K/DepartmentID>
<Salary>20000</Salary>
</tblEmployee>
<tblEmployee>
<ID>2K/ID>
<FirstName>Mike</FirstName>
<LastName>Doberstein</LastName>
<DepartmentID>2</DepartmentID>
<Salary>42000</Salary>
</tblEmployee>
<tblEmployee>
<ID>22</ID>
<FirstName>Terri</FirstName>
<LastName>Woodruff</LastName>
<DepartmentID>3</DepartmentID>
<Salary>66000</Salary>
</tblEmployee>
<tblEmployee>
<ID>23</ID>
<FirstName>Cathy</FirstName>
<LastName>Rosenthal</LastName>
departmentID>4</DepartmentID>
<Salary>0</Salary>
</tblEmployee>
<tblEmployee>
<ID>24</ID>
<FirstName>Margaret</FirstName>
<LastName>Eves</LastName>
departmentID>K/DepartmentID>
<Salary>24000</3alary>
</tblEmployee>
<tblEmployee>
<ID>25</ID>
<FirstName>Mikki</FirstName>
<LastName>Lemay</LastName>
<DepartmentID>2</DepartmentID>.
<Salary>50000</Salary>
</tblEmployee>
<tblEmployee>
<ID>26</ID>
<FirstName>Randy</FirstName>
<LastName>Nelson</LastName>
<DepartmentID>3</DepartmentID>
<Salary>78000</Salary>
</tblEmployee>
<tblEmployee>
<ID>27</ID>
<FirstName>Kathleen</FirstName>
<LastName>Husbands</LastName>
<DepartmentID>4</DepartmentID>
<Salary>108000</Salary>
</tblEmployee>
<tblEmployee>
<ID>28</ID>
<FirstName>Kathleen</FirstName>
<LastName>Eberman</LastName>
<DepartmentID>1</DepartmentID>
<Salary>28000</Salary>
</tblEmployee>
<tblEmployee>
<ID>29</ID>
<FirstName>Richard</FirstName>
<LastName>Rosenthal</LastName>
<DepartmentID>2 </DepartmentID>
<Salary>58000</Salary>
</tblEmployee>
<tblEmployee>
<ID>30</ID>
<FirstName>Mike</FirstName>
<LastName>Woodruff</LastName>
<DepartmentID>3</DepartmentID>
<Salary>90000</Salary>
</tblEmployee>
</NewDataSet>
К сожалению, данный XML-файл содержит список сотрудников, который не сгруппирован по отделам. Несмотря на созданное отношение между родительской таблицей tblDepartment и дочерней таблицей tblEmployee, полученный XML-файл содержит данные отдельно для каждой таблицы. Для вложения дочерних элементов из таблицы tblEmployee в родительские элементы из таблицы tblDepartment нужно указать значение True для свойства Nested объекта-отношения Relation.
dsEmployees.Relations("DepartmentEmployees").Nested = True
После вставки этой строки кода перед строкой сохранения данных
dsInventory.WriteXml("..\Inventory.xml", XmlWriteMode.WriteSchema)
будет получен XML-файл Employees . xml, показанный в листинге 10.11.
Листинг 10.11. XML-файл Employees.xml с вложением дочерних элементов
ИЗ ТабЛИЦЫ tblEmployee В родительские элементы ИЗ ТабЛИЦЫ tblDepartment
<?xml version="l .0" standalone="yes"?>
<NewDataSet>
<tblDepartment>
<ID>K/ID>
<DepartmentName>Administration</DepartmentName>
<tblEmployee>
<ID>2035</ID>
<FirstName>Julie</FirstName>
<LastName>Ryan</LastName>
<DepartmentID>l</DepartmentID>
<Salary>4000</Salary>
</tblEmployee>
<tblEmployee>
<ID>2039</ID>
<FirstName>Sorel</FirstName>
<LastName>Terman</LastName>
<DepartmentID>l</DepartmentID>
<Salary>8000</Salary>
</tblEmployee>
<tblEmployee>
<ID>205K/ID>
<FirstName>Terri</FirstName>
<LastName>Allen</LastName>
<DepartmentID>l</DepartmentID>
<Salary>20000</Salary>
</tblEmployee>
<tblEmployee>
<ID>2055</ID>
<FirstName>Margaret</FirstName>
<LastName>Eves</LastName>
<DepartmentI D>K/DepartmentI D>
<Salary>24000</Salary>
</tblEmployee>
<tblEmployee>
<ID>2059</ID>
<FirstName>Kathleen</FirstName>
<LastName>Eberman</LastName>
<DepartmentID>l</DepartmentID>
<Salary>28000</Salary>
</tblEmployee>
</tblDepartment>
<tblDepartment>
<ID>2</ID>
<DepartmentName>Engineering</DepartmentName>
<tblEmployee>
<ID>2032</ID>
<FirstName>Carole</FirstName>
<LastName>Vermeren</LastName>
<DepartmentlD>2</DepartmentID>
<Salary>222</Salary>
</tblEmployee>
<tblEmployee>
<ID>2033</ID>
<FirstName>Cathy</FirstName>
<LastName>Johnson</LastName>
<DepartmentID>2</DepartmentID>
<Salary>13000</Salary>
</tblEmployee>
<tblEmployee>
<ID>2036</ID>
<FirstName>Richard</FirstName>
<LastName>Halpin</LastName>
<DepartmentID>2</DepartmentID>
<Salary>10000</Salary>
</tblEmployee>
<tblEmployee>
<ID>2040</ID>
<FirstName>Randy</FirstName>
<LastName>Hobaica</LastName> departmentID>2</DepartmentID>
<Salary>18000</Salary>
</tblEmployee>
<tblEmployee>
<ID>2043</ID>
<FirstName>Brad</FirstName>
<LastName>Townsend</LastName> departmentID>2</DepartmentID>
<Salary>12000</Salary>
</tblEmployee>
<tblEmployee>
<ID>2044</ID>
<FirstName>Jennifer</FirstName>
<LastName>Eves</LastName> departmentID>2</DepartmentID>
<Salary>26000</Salaty>
</tblEmployee>
<tblEmployee>
<ID>204V</ID>
<FirstName>Angela</FirstName>
<LastName>Stefanac</LastName>
<DepartmentID>2</DepartmentID>
<Salary>16000</Salary>
</tblEmployee>
<tblEmployee>
<ID>2048</ID>
<FirstName>Marjorie</FirstName>
<LastName>Bassett</LastName>
<DepartmentID>2</DepartmentID>
<Salary>34000</Salary>
</tblEmployee>
<tblEmployee>
<ID>2052</ID>
<FirstName>Mike</FirstName>
<LastName>Doberstein</LastName>
<DepartmentID>2</DepartmentID>
<Salary>42000</Salary>
</tblEmployee>
<tblEmployee>
<ID>2056</ID>
<FirstName>Mikki</FirstName>
<LastName>Lemay</LastName>
<DepartmentID>2</DepartmentID>
<Salary>50000</Salary>
</tblEmployee>
357
<tblEmployee>
<ID>2060</ID>
<FirstName>Richard</FirstName>
<LastName>Rosenthal</LastName>
<DepartmentID>2</DepartmentID>
<Salary>58000</Salary>
</tblEmployee>
</tblDepartment>
<tblDepartment>
<ID>3</ID>
<DepartmentName>Sales</DepartmentName>
<tblEmployee>
<ID>2037</ID>
<FirstName>Kathleen</FirstName>
<LastName>Johnson</LastName>
<DepartmentID>3</DepartmentID>
<Salary>18000</Salary>
</tblEmployee>
<tblEmployee>
<ID>204K/ID>
<FirstName>Matthew</FirstName>
<LastName>Haglund</LastName>
<DepartmentID>3</DepartmentID>
<Salary>30000</Salary>
</tblEmployee>
<tblEmployee>
<ID>2045</ID>
<FirstName>Steve</FirstName>
<LastName>Marshall</LastName>
<DepartmentID>3</DepartmentID>
<Salary>42000</Salary>
</tblEmployee>
<tblEmployee>
<ID>2049</ID>
<FirstName>Joe</FirstName>
<LastName>Chideya</LastName>
<DepartmentID>3</DepartmentID>
<Salary>54000</Salary>
</tblEmployee>
<tblEmployee>
<ID>2053</ID>
<FirstName>Terri</FirstName>
<LastName>Woodruff</LastName>
<DepartmentID>3</DepartmentID>
<Salary>66000</Salary>
</tblEmployee>
<tblEmployee>
<ID>2057</ID>
<FirstName>Randy</FirstName>
<LastName>Nelson</LastName>
<DepartmentID>3</DepartmentID>
<Salary>78000</Salary>
</tblEmployee>
<tblEmployee>
<ID>206K/ID>
<FirstName>Mike</FirstName>
<LastName>Woodruf f</LastName>
<DepartmentID>3</DepartmentID>
<Salary>90000</Salary>
</tblEmployee>
</tblDepartment>
<tblDepartment>
<ID>4</ID>
<DepartmentName>Marketing</DepartmentName>
<tblEmployee>
<ID>2034</ID>
<FirstName>Eric</FirstName>
<LastName>Haglund</LastName>
<DepartmentID>4</DepartmentID>
<Salary>12000</Salary>
</tblEmployee>
<tblEmployee>
<ID>2038</ID>
<FirstName>Sorel</FirstName>
<LastName>Polito</LastName>
<DepartmentID>4</DepartmentID>
<Salary>28000</Salary>
</tblEmployee>
<tblEmployee>
<ID>2042</ID>
<FirstName>Cathy</FirstName>
<LastName>Vermeren</LastName>
<DepartmentID>4</DepartmentID>
<Salary>0</Salary>
</tblEmployee>
<tblEmployee>
<ID>2046</ID>
<FirstName>Laura</FirstName>
<LastName>Davidson</LastName>
<DepartmentID>4</DepartmentID>
<Salary>60000</Salary>
</tblEmployee>
<tblEmployee>
<ID>2050</ID>
<FirstName>Katie</FirstName>
<LastName>Chideya</LastName>
<DepartmentID>4</DepartmentID>
<Salary>76000</Salary>
</tblEmployee>
<tblEmployee>
<ID>2054</ID>
<FirstName>Cathy</FirstName>
<LastName>Rosenthal</LastName>
<DepartmentID>4</DepartmentID>
<Salary>5555</Salary>
</tblEmployee>
<tblEmployee>
<ID>2058</ID>
<FirstName>Kathleen</FirstName>
<LastName>Husbands</LastName>
<DepartmentID>4</DepartmentID>
<Salary>108000</3alary>
</tblEmployee>
</tblDepartment>
</NewDataSet>
359
Создание объекта XmlReader
с помощью объекта Command
В главе 4, "Модель ADO.NET: провайдеры данных", описываются способы работы с объектом Command, который является ключевым объектом — провайдером данных на платформе .NET. В ней рассматриваются способы выполнения команд на основе объектов ExecuteReader, ExecuteScalar и ExecuteNonQuery. Хотя все провайдеры данных на платформе .NET реализуют эти методы, объект SqlCommand имеет дополнительный метод ExecuteXmlReader, который используется для извлечения и доступа к XML-данным из СУБД SQL Server.
Метод ExecuteXmlReader возвращает объект XmlReader точно так же, как он возвращает объект DataReader.
Для демонстрации применения метода ExecuteXmlReader вернитесь к проекту ADO-XML и выполните следующее.
Включите в форму frmXML под кнопкой Write XML еще одну кнопку, перетаскивая ее из панели элементов управления.
В окне свойств Properties укажите значение btnExecuteXML для свойства (Name) и значение ExecuteXMLReader для свойства Text этой кнопки.
Затем в определении класса формы f rmXML введите код из листинга 10.12.
Листинг 10.12. Код извлечения и обработки данных из СУБД SQL Server в формате XML
Private Sub btnExecuteXML_Click(ByVai sender As System.Object, _
ByVai e As System.EventArgs) Handles btnExecuteXML.Click
Dim cn As New SqlConnection ("data source=localhost;initial catalog=pubs;user id=sa")
Dim cmd As New SqlCommand ("select * from stores for xml auto, elements", cn)
Dim reader As Xml.XmlReader
Dim str As New System.Text.StringBuilder()
cn.Open()
' Выполнение SQL-команды Select с предложением FOR XML.
reader = cmd.ExecuteXmlReader()
' Поиск и извлечение данных из узлов-элементов.
While reader.Read()
Select Case reader.NodeType
Case Xml.XmlNodeType.Element
str.Append("<" & reader.Name & ">")
Case Xml.XmlNodeType.EndElement
str.Append("</" & reader.Name && ControlChars.CrLf)
Case Xml.XmlNodeType.Text
str.Append(reader.Value)
Case Else
В данном примере
End Select
End While
MsgBox (str.ToString)
cn.Closed
End Sub
Код в листинге 10.12 содержит пример упрощенного использования метода Ехе- в котором просто отображаются данные (вместе с дескрипторами) из таблицы базы данных pubs. СУБД SQL Server передается показанная ниже SQL- команда Select, в которой явно указаны возвращаемые поля в виде XML-элементов.
"select * from stores forauto, elements"
В таком случае обработка разных типов XML-узлов упрощается, потому что достаточно найти только начальный и конечный узлы, а текстовые узлы между ними будут содержать фактические данные. Более надежная обработка XML-документа основана на конструкции Select Case, в которой учтены узлы всех типов. После щелчка на кнопкебудет выполнен код из листинга 10.12 и на экране поя
вится диалоговое окно, которое показано на рис. 10.2.
[aoo-xml□
<store$><&orjd>6360</$torjd> the
<stor_address >788 Catamaugus Ave. </stor „address >
<city>5eattte</city>
<state > WA </state >
<zp>98056<V2ip>
</stores>
<5tores> <$torJd>7066</$tor Jd >
'
<stor_address>567 Pasadena Ave. </stor „address >
<city>Tu$tin</c8y>
<state>CA </state>
<2тр >92789 </zp>
</$tore$>
<stores> <stor Jd >7067 </$tor Jd> &Brews„name>
a.
<city >Los Gato!? </tity >
<state>CA</st^e>
<zip>96745</zip>
</йоге$>
<stores><$tor _y>713K/$tor_id>
and >
<city >Remuiade </cty>
<state>WA</sl:ate>
<zip>980M</zip>
</stores>
>
<stor_name>Fricative Bookshop </stor„name> St.
<city>Fremone</oty>
<stste>C A </$tate >
<2у>9(Ю19</Яр>
</store$>
<stores><5tor Jd >KM2</storJd>
<stor _name>Bcokbeat</stor_name>
<$tor „address >679 Car son St. </storaddre$s>
<dtY>Pordand</dty>
<state>OR</st3le>
РИС. 10.2. Диалоговое окно с XML-данными, извлеченными из СУБД SQL ServerРИС. 10.2. Диалоговое окно с XML-данными, извлеченными из СУБД SQL Server<zip>89076</zip>
РИС. 10.2. Диалоговое окно с XML-данными, извлеченными из СУБД SQL Server
РИС. 10.2. Диалоговое окно с XML-данными, извлеченными из СУБД SQL Server
</stwes>
ADO.NET и XML 361
Объект XmlDataDocument
В главе 9, "XML и .NET”, рассматривается объект XmlDataDocument и способы его использования для доступа к иерархическим данным в виде узлов загруженного в оперативную память XML-документа. В этой книге также рассматриваются способы извлечения реляционных данных (и доступа к ним) из традиционной SQL-совеместимой базы данных. При этом необходимо выяснить следующее:
как быть, если данные поступили из XML-источника, а нам известны только реляционные способы навигации и манипуляции записями;
и наоборот: как быть, если данные поступили из SQL-совместимой базы данных, а нам известны только XML-совместимые способы навигации и манипуляции записями.
Ответы на эти простые вопросы основаны на применении объекта XmlDataDocument. Он является производным от класса XmlDocument, но обладает расширенными возможностями. Помимо внутренней копии данных, он содержит XML-совместимые средства доступа к ним как к классу XmlDocument с помощью XML-узлов, а также реляционные средства доступа на основе объекта DataSet. Объект XmlDataDocument автоматически синхронизирует оба эти представления (или, иначе говоря, способа доступа) таким образом, чтобы любые изменения, выраженные в одной технологии, сразу же отображались средствами другой технологии. Такой подход позволяет легко смешивать и находить соответствие между разными источниками данных на основе разных технологий.
Для демонстрации этих средств вернитесь к проекту ADO-XML и выполните перечисленные ниже действия.
Вставьте еще две кнопки в форму frmXML сразу под кнопкой ExecuteXMLReader, перетаскивая их из панели элементов управления.
В окне свойств Properties укажите значение btnNavigateSQL для свойства (Name) и значение Navigate SQL для свойства Text первой кнопки.
В окне свойств Properties укажите значение btnAddRows для свойства (Name) и значение Add Rows to XML для свойства Text второй кнопки.
Для импорта пространства имен XPath включите строку кода Imports System. Xml. XPath в конце списка команд импорта в верхней части файла с кодом.
В код класса f rmXML включите две подпрограммы.
Private Sub btnNavigateSQL_Click(ByVai sender As System.Object, _
В у Vai e As System.EventArgs) Handles btnNavigateSQL.Click
Dim cnAs New SqlConnection
("data source=localhost;initial catalog=pubs;user id=sa")
Dim da As New SqlDataAdapter("Select * from authors", cn)
Dim ds As New DataSet()
' Вставка в объект DataSet данных из реляционной базы данных.
da.Fill(ds, "authors")
' Создание объекта XmlDataDocument на основе существующего ' объекта DataSet.
Dim xmlDoc As New Xml.XmlDataDocument(ds)
' Получение объекта-навигатора из XmlDataDocument.
Dim xmlNav As XPathNavigator = xmlDoc.CreateNavigator()
' Извлечение всех фамилий авторов из штата Калифорния (CA) .
Dim xlterator As XPathNodelterator
=
xmlNav.Select("//authors[state='CA' ]/au_lname")
' Последовательный обход всех выбранных узлов и ' отображение фамилий всех авторов.
Dim str As New System.Text.StringBuilder()
While (xlterator.MoveNext())-
str.Append(xlterator.Current.Value & ControlChars.CrLf)
End While
MsgBox(str.ToString)
End Sub
Private Sub btnAddRows_Click(ByVai sender As System.Object, _
ByVai e As System.EventArgs) Handles btnAddRows.Click
Dim dsPubs As New DataSet ()
' Считывание XML-данных из файла.
dsPubs.ReadXml("..\Pubs.xml")
' Вставка новой записи.
Dim row As DataRow = dsPubs.Tables("Publishers").NewRow()
row("pub_name") =Publishing Corp."
row("city") = "New York"
row("state”) = "NY"
row("Country") =
dsPubs.Tables("Publishers").Rows.Add(row)
' Связывание объекта DataSet с сеткой Data Grid
' для просмотра новых данных.
grdData.DataMember = "publishers"
grdData.DataSource = dsPubs
End Sub
Подпрограмма btnNavigateSQL_Click считывает данные из базы данных SQL Server, а затем выполняет обход всех записей, извлеченных с помощью XPATH- запроса. В этой подпрограмме ключевыми являются приведенные ниже строки.
Dim xmlDoc As New Xml .XmlDataDocument (ds)
Получение объекта-навигатора из XmlDataDocument.
Dim xmlNav As XPathNavigator = xmlDoc.CreateNavigator()
1 Извлечение всех фамилий авторов из штата Калифорния (CA) .
DimxlteratorAsXPathNodelterator xlterator =
xmlNav.Select("//authors[state='CA']/au_lname")
Сначалаобъект DataSet с данными связывается с новым объектом XmlDataDocu- ment. Затем создается объект-навигатор XPathNavigator на основе объекта- документа XmlDataDocument. После чегонаоснове объекта XPathNavigator создается объект-итератор XPathNodelterator. Далее строка ХРАТН-запроса передается методу Select для возвращения списка фамилий авторов (т.е. значений поля au_lname) из Калифорнии (т.е. у которых поле state имеет значение СА). После
РИС. 10.4. Вид сетки DataGrid после считывания данных из XML- файла вставки строкиРИС. 10.4. Вид сетки DataGrid после считывания данных из XML- файла вставки строки
РИС. 10.4. Вид сетки DataGrid после считывания данных из XML- файла вставки строки
РИС. 10.4. Вид сетки DataGrid после считывания данных из XML- файла вставки строки
363
этого выполняется последовательный обход всех узлов, извлеченных данным запросом, и создается строка с искомым перечнем авторов. Эта строка отображается в диалоговом окне, которое показано на рис. 10.3.
РИС. 10.3. Диалоговое окно с XML-данными, извлеченными из объекта Da taSet
Вторая подпрограмма, btnAddRows_Clicks, выполняет другую задачу. Сначала она считывает XML-данные с помощью метода ReadXml из файла Pubs . xml в набор данных dsPubs, как показано в листинге 10.1. Этот метод автоматически создает таблицу publishers в объекте DataSet. Далее новые данные вставляются с помощью реляционных методов и объектов, например объекта-записи DataRow. Новый объект DataRow включается в схему таблицы publishers, а полям присваиваются указанные значения. После этого новая запись вставляется в таблицу publishers, а результат вставки отображается в сетке DataGrid (рис. 10.4).
Резюме
В этой главе демонстрируется тесная взаимосвязь и интеграция способов доступа к данным и XML на платформе .NET Framework. В частности, показано, что объект DataSet обладает возможностями чтения и записи XML-данных и XML-схем. При отсутствии определения XML-схемы объект DataSet способен вывести его на основании анализа структуры данных в считанном XML-документе.
С другой стороны, объект XmlDa t a Document образует мост между реляционным и иерархическим миром XML. Доступ к данным внутри объекта XmlDataDocument можно получить либо XML-совместимым способом, либо реляционным. Изменения в одном представлении автоматически реплицируются в другом представлении. Разработчик базы данных может выбрать наиболее удобный способ доступа к данным и ма- нипулированияими.
Методам и инструментам реляционного и иерархического доступа к данным посвящено множество специализированных книг и учебных пособий. Невозможно в рамках одной главы или даже целой книги полностью описать их. Здесь следует лишь запомнить, что ADO.NET и XML-объекты на платформе .NET предназначены для наиболее тесной интеграции и взаимодействия друг с другом.
Вопросы и ответы
Иногда в документации по платформе .NET, помимо XSD-формата схемы данных, упоминается XDR-формат. Что это такое?
XSD-формат — это стандартный формат определения схемы ХМЕ-данных, принятый консорциумом World Wide Web Consortium (W3C), который специализируется на создании Web-ориентированных стандартов (более подробную информацию об этом консорциуме можно найти по адресу: WWW.w3c . org). Еще до окончания работ по созданию стандарта для XSD-формата компания Microsoft решила создать собственный временный формат XML-Data Reduced (XDR) для определений XML-схем. После завершения работы над стандартом XSD-формата в мае 2001 года Microsoft перешла к полной поддержке XSD-формата. Несмотря на то что на платформе .NET Framework схемы данных сохраняются только в XSD-формате, они могут считываться в обоих форматах для совместимости с прежними унаследованными системами. В наборе инструментальных средств разработчика NET Framework SDK предусмотрена специальная утилита xsd.exe, которая может (помимо прочего) конвертировать схему данных из формата XDR в формат XSD.
Как конкретно ADO.NET автоматически определяет схему данных после анализа XML-документа?
Алгоритм автоматического определения схемы данных хорошо документирован в справочных материалах Visual Studio .NET. Для ознакомления с ним следует обратиться к разделу Inferring DataSet Relational Structure from XML (Автоматическое определение XML-схемы данных после анализа реляционной структуры объекта DataSet). В нем приводится краткое описание алгоритма, некоторые особенности анализа таблиц, полей и отношений, а также ограничения алгоритма.
ADO.NET и XML Зб5
В этой главе описаны два способа доступа к данным: реляционный на основе объекта DataSet и иерархический на основе XmlDataDocument. В каких ситуациях следует их применять?
Вообще говоря, не существует единственно верного и универсального способа. Каждый из них следует применять в зависимости от используемых источников данных и поставленной задачи. Платформа .NET Framework позволяет гибко использовать данные из любых источников: реляционных или иерархических. Независимо от источника, данные можно сохранять как реляционные или как иерархические в соответствии с конкретными потребностями. Кроме того, если источник данных и полученные после обработки данные должны иметь формат XML, а вы в недостаточной степени владеете технологиями и инструментами XML, то поставленную задачу все равно можно успешно выполнить с помощью реляционных инструментов на основе объектов DataSet и DataTable.
ГЛАВА11ГЛАВА11
ГЛАВА
11
ГЛАВА
11
Web-формы:
приложения на основе ASP.NET для работы с базами данных
В ЭТОЙ ГЛАВЕ...
Обзор технологии ASP.NET
Доступ к базе данных с помощью ASP.NET
Повышениепроизводительности приложений с помощью хранимых процедур
Резюме
Вопросы и ответы
368
До сих пор демонстрировались различные способы работы с базой данных на разных этапах создания приложения и способы использования результатов выполнения приложения (на основе объектов DataSet, DataAdapter и Connection). В этой главе эти способы комбинируются для просмотра информации из базы данных в Web-среде с помощью Web-броузера. В контексте платформы .NET Framework Web-страница, которая применяется для извлечения или вставки данных, называется Web-формой (WebForm). Прежде, при создании приложения с помощью технологии ASP, единственным СПОСОбом извлечения данных из реляционной базы данных было применение объектов ADODB.Connection и ADODB.Recordset. С появлением платформы .NET модель ADO.NET позволяет использовать не только новые классы и параметры, но и встроенную поддержку для работы с XML-данными так, как если бы они были элементами базы данных. Описание новых способов работы на основе технологии ASP.NET начинается с базовых сведений, а затем приводятся более сложные понятия.
ОбзортехнологииASP.NET
После изобретения возможностей использования серверного кода для создания Web-страниц разработчики пытались найти способы его упрощения для конечных пользователей и разработчиков. Рассмотрим следующую бизнес-ситуацию: пользователи могут осуществлять доступ к Web-узлу, получать базовую контактную информацию и звонить по телефону для оформления заказа. (Не так давно именно такими ограниченными возможностями обладали все Web-узлы электронной коммерции.) Проблема заключается в том, что пользователи при этом хотели бы видеть выставленные на продажу товары, причем таких пользователей может быть очень много.
Для организации такого взаимодействия с Web-узлом начали появляться разные языки и технологии создания сценариев. Одной из таких технологий является технология активных серверных страниц (Active Server Pages — ASP). Она предоставляет средства вставки в Web-страницу кода, который выполняется во время обработки Web-сервером поступившего запроса. В контексте разработки приложений эта новинка была революционной: по крайней мере теперь разработчики могли использовать сценарий на языке VBScript, который выполняется на сервере. Этот тип взаимодействия постепенно начал превосходить другие подходы на основе языков JavaScript и ActiveX. Кроме того, язык VBScript был расширен таким образом, чтобы вызывать компилированные СОМ-объекты на Web-сервере и включать их функции в Web- страницу. На этом этапе появился Microsoft Transaction Server, который теперь называется набором компонентных служб Component Services.
Итак, что же произойдет при смешении серверного кода, клиентских сценариев, а также кода, откомпилированного для обеспечения безопасности и масштабируемости. Именно эти компоненты и образуют основу технологии ASP.NET. Полученный в результате такой комбинации ASPX-файл позволяет использовать мощный серверный код, основанный на полноценном языке программирования, которым привыкли пользоваться разработчики. Теперь поддержка языков программирования c#, Visual Basic, C++, FoxPro, Perl, COBOL и Fortran осуществляется благодаря общеязыковой исполняющей среде (Common Language Runtime — CLR), и эти языки могут использоваться в специальных Web-страницах для более полного удовлетворения требований пользователей.
369
Кроме того, все ASPX-файлы компилируются и кэшируются, т.е. после использования ASPX-страницы она компилируется во временный DLL-файл, который фактически выполняет всю работу при поступлении следующих запросов. В результате повышается производительность и уменьшается время задержки ответа на запрос.
HTML-элементы управления и серверные элементы управления
Еще одной новинкой технологии ASP.NET является использование серверных элементов управления. Серверный элемент управления кодируется в Web-странице, а все его события и свойства обрабатываются Web-сервером. Серверные элементы управления аналогичны HTML-элементам управления, но обладают существенными преимуществами.
Любому разработчику, которому приходилось создавать Web-страницу с формой ввода данных, уже известны HTML-элементы управления. Для программирования формы ввода данных используется элемент FORM вместе с несколькими элементами INPUT, например для создания текстового поля, списка или флажка, как показано ниже.
cinput type="text" name="txtxName" value=" ">
Элемент INPUT также доступен в технологии ASP.NET, но для работы с его содержимым требуется создать клиентский или серверный сценарий. В рассматриваемой бизнес-ситуации поставлена задача обеспечить полную совместимость созданного приложения со всеми типами Web-броузеров. При использовании клиентского сценария всегда существует вероятность, что какой-то Web-броузер не поддерживает данный сценарий либо выполнение сценариев отключено по соображениям безопасности. Для исключения этих проблем и предназначены серверные элементы управления.
Еще один интересный аспект использования серверных элементов управления связан с технологией ActiveX. Эта технология основана на использовании "толстого" клиента. ("Толстый" клиент — это компьютер с богатыми функциональными возможностями и ресурсами, которые часто избыточны с точки зрения сетевых вычислений. — Прим.ред.) По причинам, полное описание которых выходит за рамки данной книги, использование вставленных в Web-страницу элементов управления ActiveX, которыми можно управлять через Internet, было крайне неудачной идеей. В отличие от элемента управления ActiveX, серверный элемент управления не содержит никакого кода, который выполняется на клиентской стороне. Клиент получает только HTML-код, а все операции взаимодействия с пользователям выполняются Web- сервером после получения соответствующего запроса от пользователя ("posting back"). Далее на примере объекта DataGrid показан способ применения серверного элемента управления, который принимает набор данных, обрабатывает его и создает HTML-таблицу, которую можно отобразить в любом Web-броузере. Следует признать, что она несколько отличается от стандартной HTML-таблицы с данными.
НА ЗАМЕТКУ :;
Серверные элементы управления можно создавать с помощью интегрированной среды разработки Visual Studio .NET.
Завершая сравнение HTML-элементов управления и серверных элементов управления, рассмотрим приведенный ниже фрагмент кода ASP.NET, предназначенного для представления серверного элемента. управления, а именно текстового поля txtName. <asp:TextBox id="txtName" runat="server" text=""x/asp:TextBox>
Этот серверный элемент управления выполняет точно ту же задачу, которую выполняет HTML-элемент управления из приведенного фрагмента кода. Единственным отличием является то, что это текстовое поле обрабатывается Web-сервером.
Дополнительные преимущества технологии ASP.NET
Описанию технологии ASP.NET посвящено множество книг, здесь же описываются два компонента ASP.NET.
Первый компонент связан со свойством IsPostBack. При создании ASPX-файла (или, иначе говоря, Web-формы) ему по умолчанию передаются данные. Свойство IsPostBack используется для того, чтобы избежать появления сообщений об ошибках при отсутствии таких данных на Web-странице, которую пользователь еще даже не успел увидеть. Свойство IsPostBack имеет значение true, если данная Web- страница запрашивается пользователем, либо значение false, если данная Web- страница загружается самостоятельно впервые. Ниже приводится фрагмент кода с примером использования этого метода.
If Page.IsPostBack Then ' Обработка данных.
End If
Конечно, ASPX-файл или HTML-файл можно использовать для передачи данных другому ASPX-файлу. Это позволяет создать файл с нужной функциональностью, например переадресовать пользователя после проверки и исправления данных к другой Web-странице с помощью метода Response. Redirect.
Еще одно важное достоинство ASP.NET связано с процессом доставки готового приложения. Для этого достаточно скопировать все файлы с одного компьютера на другой. Конечно, на новом компьютере под управлением Web-сервера Internet Information Server (IIS) нужно также создать новый виртуальный каталог с помощью консоли управления IIS ММС. Для этого выберите команду Start^Settings^Control Panel C>Administrative Tools^ Internet Services Manager (Пуск1^ Параметры1^ Панель управ- ления1^Административные инструменты1^ Менеджер Internet-служб), в появившемся окне Internet Information Services (Информационные Internet-службы) выберите узел Default Web Site (Web-узел по умолчанию) и щелкните правой кнопкой мыши на нужном каталоге. Выберите в контекстном меню команду Properties. Появится одноименное диалоговое окно; выберите в нем вкладку Virtual Directory (Виртуальный каталог) или Home Directory (Домашний каталог), в зависимости от используемой операционной системы. Если выбранный каталог не сконфигурирован, то в группе элементов управления Application Settings (Параметры приложения) будет присутствовать кнопка Create (Создать). (В противном случае этой кнопки не будет и тогда следует щелкнуть на кнопке Cancel.) После щелчка на кнопке Create и конфигурирования каталога щелкните на кнопке ОК для закрытия диалогового окна. Теперь выбранный виртуальный каталог сконфигурирован как приложение.
371
Совсем необязательно инсталлировать интегрированную среду выполнения Visual Studio .NET на любом Web-сервере, на который нужно скопировать Web-приложение. Для работоспособности такого Web-приложения нужно только установить платформу .NET Framework, которую можно бесплатно скопировать с Web-узла компании Microsoft по адресу: http://www.microsoft.com.
Доступ к базе данных с помощью ASP.NET
Ядром любого приложения баз данных является база данных. Для использования базы данных необходимо иметь надежный и безопасный способ подключения. На платформе .NET этот способ реализуется с помощью пространства имен System. Data и одной строки подключения.
Учтите, что доступ к данным связан с рядом трудностей. Одна из них заключается в том, что пользователь может не иметь прав доступа к базе данных, включая выполнение запроса на выборку данных. Еще одна часто возникающая проблема связана с сохранением имен и паролей пользователей в простом текстовом файле, который обычно не отображается Web-сервером для внешних пользователей. Например, при просмотре кода ASP-страницы очень редко можно встретить следующие строки:
Set Conn = Server.CreateObject("ADODB.Connection")
Conn.Open("server=myServerName;uid=sa;pwd=")
В примерах данной главы используется параметр TRUSTED_CONNECTION = YES, который означает применение надежного подключения, т.е. пользователь должен быть идентифицирован операционной системой Windows, а аутентифицирующая информация о нем должна храниться в SQL Server. В большинстве практических приложений в SQL Server не принято создавать отдельные учетные записи ASPNET, если только не требуется ограничить возможности пользователя лишь операциями выборки данных.
НА ЗАМЕТКУ
Учетная запись ASPNET используется по умолчанию при анонимных Web-запросах I к Web-серверу с инсталлированной средой выполнения платформы .NET.
Включение учетной записи ASPNET в состав учетных записей SQL Server
Для создания надежного подключения (на основе параметра TRUSTED—CONNECTION) необходимо иметь учетную запись операционной системы Windows, которую следует включить в состав учетных записей SQL Server. Кроме того, SQL Server должен быть сконфигурирован так, чтобы разрешалось использовать аутентификацию операционной системы Windows. Хотя большая часть этого процесса уже описана в предыдущей главе, он рассматривается здесь более подробно для демонстрации прямой связи между управлением учетной записью ASPNET в SQL Server и использованием надежного подключения на основе параметра TRUSTED_CONNECTION. В данном примере рассматривается редкий случай создания отдельной учетной записи ASPNET в SQL Server для ограничения возможностей пользователя только операциями выборки данных.
372
Откройте программу SQL Server Enterprise Manager, выбрав команду Starts Microsoft SQL Servers Enterprise Manager. Раскройте узел используемой базы данных и узел Security (Безопасность), как показано на рис. 11.1.
Рис. 11Л. Выбор узла Security в программе SQL Server Enterprise Manager
Щелкните правой кнопкой мыши на элементе Logins (Учетные записи) и выберите в контекстном меню команду New Login (Создать учетную запись). На экране появится диалоговое окно SQL Server Login Properties — New Login (Свойства учетных записей SQL Server — Создать учетную запись), как показано на рис. 11.2.
Во вкладке General (Общие свойства) в текстовом поле Name (Имя) укажите учетное имя пользователя, выбрав его с помощью щелчка на кнопке с многоточием либо введя в формате имя_компьютера/имя_пользователя или имя_ домена/имя_пользователя. На рис. 11.3 показан пример выбора учетного имени пользователя после щелчка на кнопке с многоточием.
Прокрутите список всех учетных записей для поиска учетной записи ASPNET и щелкните дважды на ней. Затем щелкните на кнопке ОК для закрытия данного диалогового окна.
В нижней части вкладки General выберите в списке Database используемую базу данных Novelty (рис. 11.4).
Далее выберите вкладку Database Access (Доступ к базе данных). Установите флажок только возле базы данных Novelty. Затем в списке Database Roles for ‘Novelty’ (Роли базы данных 'Novelty') установите флажок роли public (рис. 11.5).
Выберите роль public и щелкните на кнопке Properties, чтобы открыть диалоговое окно, показанное на рис. 11.6.
373373РИС. 11.2.Диалоговое окно SQL Server Login Properties — New LoginРИС. 11.2.Диалоговое окно SQL Server Login Properties — New LoginSQL Server Login Properties - New LoginSQL Server Login Properties - New LoginList Namei From: |©\\PRICEKW2KLT2"Names:List Namei From: |©\\PRICEKW2KLT2"Names:'S Developers'S DevelopersUsers are prevented from making accid -^Visual Studio developers can author we.Users are prevented from making accid -^Visual Studio developers can author we.ACT User [Application Center Tes Account used to launch the ApplicationACT User [Application Center Tes Account used to launch the ApplicationAdministratorAdministratorBuilt-in account for administering the соBuilt-in account for administering the соASPNET (aspnet_wpaccount) Account Ira running ASP.NET Worker |ASPNET (aspnet_wpaccount) Account Ira running ASP.NET Worker |GuestGuestBuilt-in account for guest access to theBuilt-in account for guest access to theHelpAssistant (Remote Desktop Account for Providing Remote Assistam IUSR_PRICEKW2KLT 2 (Internet Вuilt-in account foranonymous accessHelpAssistant (Remote Desktop Account for Providing Remote Assistam IUSR_PRICEKW2KLT 2 (Internet Вuilt-in account foranonymous accessРИС. 11.3. Выбор учетного имени пользователяРИС. 11.3. Выбор учетного имени пользователя
373
373
РИС. 11.2.Диалоговое окно SQL Server Login Properties — New Login
РИС. 11.2.Диалоговое окно SQL Server Login Properties — New Login
SQL Server Login Properties - New Login
SQL Server Login Properties - New Login
List Namei From: |©\\PRICEKW2KLT2"
Names:
List Namei From: |©\\PRICEKW2KLT2"
Names:
'S Developers
'S Developers
Users are prevented from making accid -^
Visual Studio developers can author we.
Users are prevented from making accid -^
Visual Studio developers can author we.
ACT User [Application Center Tes Account used to launch the Application
ACT User [Application Center Tes Account used to launch the Application
Administrator
Administrator
Built-in account for administering the со
Built-in account for administering the со
ASPNET (aspnet_wpaccount) Account Ira running ASP.NET Worker |
ASPNET (aspnet_wpaccount) Account Ira running ASP.NET Worker |
Guest
Guest
Built-in account for guest access to the
Built-in account for guest access to the
HelpAssistant (Remote Desktop Account for Providing Remote Assistam IUSR_PRICEKW2KLT 2 (Internet Вuilt-in account foranonymous access
HelpAssistant (Remote Desktop Account for Providing Remote Assistam IUSR_PRICEKW2KLT 2 (Internet Вuilt-in account foranonymous access
РИС. 11.3. Выбор учетного имени пользователя
РИС. 11.3. Выбор учетного имени пользователя
Щелкните на кнопке Permissions (Разрешения), чтобы открыть диалоговое окно, предназначенное для низкоуровневого указания разрешений пользователя (рис. 11.7).
РИС. 11.4. Выбор используемой базы данных Novel ty в списке Database вкладки GeneralРИС. 11.4. Выбор используемой базы данных Novel ty в списке Database вкладки GeneralРИС. 11.5. Выбор базы данных и роли пользователяРИС. 11.5. Выбор базы данных и роли пользователя
РИС. 11.4. Выбор используемой базы данных Novel ty в списке Database вкладки General
РИС. 11.4. Выбор используемой базы данных Novel ty в списке Database вкладки General
РИС. 11.5. Выбор базы данных и роли пользователя
РИС. 11.5. Выбор базы данных и роли пользователя
РИС.Свойства роли public
Убедитесь в том, что выбран переключатель List all objects (Перечислить все объекты), и прокрутите список до конца, чтобы увидеть все таблицы из базы данных Novelty. Установите флажки всех таблиц в столбце SELECT (рис. 11.8). Флажок означает разрешение на выборку данных из данной таблицы, а крестик — запрет на выборку данных.
РИС.Диалоговое окно для низкоуровневого указания раз
решений
37J
Prooerties
РИС. 11.8.Диалоговое окно для установления разрешений на основные операции с таблицами
10. На этом этапе установите также флажок в столбце INSERT для таблицы tblOrder, как показано на рис. 11.9. Это позволит покупателям создавать заказы.
РИС. 11.9. Установка разрешения на операции вставки в таблицу tblOrder
После выполнения этих действий для закрытия этого и двух других диалоговых окон щелкните на кнопке ОК в каждом из них. После этого в списке учетных записей Logins в окне программы SQL Server Enterprise Manager появится пользователь ASPNET (рис. 11.10).
Рис. 11.10. Список учетных записей Logins в окне программы SQL Server Enterprise Manager
(Учтите, что в данном случае на рис. 11.10 показана учетная запись ASPNET, созданная на компьютере PRICEKW2KLT2 автора книги. — Прим. ред.)
Итак, учетная запись ASPNET включена в список учетных записей SQL Server. В следующем разделе показано, как ее использовать для подключения к базе данных без указания в строке подключения какой-либо информации об имени и пароле учетной записи.
Применение параметра TRUSTED-CONNECTION
Продемонстрируем теперь применение параметра TRUSTED_CONNECTION для подключения к базе данных и выполнения простого запроса с помощью технологии ASP.NET. Запустите интегрированную среду разработки приложений Visual Studio .NET и создайте новый проект ASP.NET Web Application. Для этого в диалоговом окне New Project (Новый проект) выберите тип проекта Visual Basic Project в области Project Types (Типы проектов) и шаблон ASP.NET Web Application (Приложение на основе ASP.NET Web-форм) в области Templates (Шаблоны).
Переименуйте автоматически созданный файл WebForml. aspx и укажите для него новое имя default. aspx, щелкнув на имени правой кнопкой мыши и выбрав команду Rename (Переименовать) в контекстном меню, а затем отредактировав выделенное
имя. Эта Web-форма будет используемой по умолчанию Web-страницей данного каталога. (Впоследствии в эту Web-форму будут постепенно вноситься другие изменения.)
Для начала рассмотрим код в листинге 11.1 с очень простым примером подключения к базе данных с помощью учетной записи ASPNET, выборки данных с помощью запроса на основе команды SELECT и отображения результатов запроса на Web-странице.
ЛИСТИНГ 11.1. Вспомогательный КОД Web-формы default. aspx. vb
Imports System.Data
Imports System.. Data. SqlClient
Public Class WebForml
Inherits System.Web.UI.Page
#Region " Web Form Designer Generated Code ”
* Этот вызов организован мастером Web Form Designer.
<System.Diagnostics.DebuggerStepThrough()> _ Private Sub InitializeComponent()
End Sub
Private Sub Page_Init(ByVai sender As System.Object, _ ByVai e As System.EventArgs) Handles MyBase.Init ' CODEGEN: Вызов этого метода организован мастером ' Web Form Designer.
' He редактируйте этот код вручную с помощью окна редактирования. InitializeComponent ()
End Sub
#End Region
Dim connString As String
Private Sub Page_Load(ByVai sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load ' Указание строки подключения.
connString = "server=(local);database=Novelty; TRUSTED_CONNECTION=Yes"
' Это вся информация, которая нам нужна
' для подключения к базе данных. Кроме того, если ' кто-то сможет получить доступ к коду этого файла, он не сможет использовать полученные сведения
' для регистрации входа в базу данных.
' Извлечение системной ссылки на указанную строку подключения. ' Это позволяет .NET проверить наличие данной строки
в куче. Если ее нет, то создается ее экземпляр в ' оперативной памяти и возвращается ссылка ' на нее, а если есть, то возвращается ссылка на нее.
String.Intern(connString) ShowCustomers()
End Sub
Private Sub ShowCustomers()
Это простой пример использования функции, которая извлекает данные из таблицы tblCustomer и отображает их.
Инициализация объекта подключения строкой
подключения.
378
Dim conn As New SqlConnection(connString) ' Затем инициализация объекта-команды ' строкой выполняемой команды SQL.
Dim cmd As New SqlCommand("SELECT * FROM tblCustomer", conn) conn.Open(),
Dim dReader As SqlDataReader =
cmd.ExecuteReader(CommandBehavior.CloseConnection)
While dReader.Read
Response.Write(dReader.GetString(1) )
Response.Write(" " & dReader.GetString(2))
Response.Write("<BR>")
End While
dReader.Close()
■conn.Close ()
End Sub
End Class
Обратите внимание на то, что в листинге 11.1 используются пространства имен System. Data и System. Data. SqlClient, которые содержат все необходимые классы и методы для подключения и опроса базы данных. Учтите, что код в листинге 11.1 является вспомогательным кодом для Web-страницы default. aspx. Это значит, что Web-страница default.aspx не содержит его, а только отображает результат его выполнения. Полностью код Web-страницы default. aspx показан в листинге 11.2.
ЛИСТИНГ 11.2.КОД Web-страницы default. aspx
<%@ Page Language="vb" AutoEventWireup="false"
Codebehind="default.aspx.vb" Inherits="Novelty.WebForml"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<title>WebForml</title>
cmeta name="GENERATOR" content="Microsoft Visual Studio.NET 7.0">
<meta name^"CODE_LANGUAGE" content="Vi sual Basic 7.0">
cmeta name="vs_defaultClientScript" content="JavaScript">
cmeta name="vs_targetSchema" _
content="http://schemas.microsoft.com/intellisense/ie5">
</HEAD>
<body MS_POSITIONING="GridLayout">
<form id="Forml" method="post" runat="server">
</form>
</body>
</HTML>
В верхней части листинга 11.2располагается директива, которая сообщает механизму выполнения ASP.NET о необходимости использования вспомогательного кода из файла default.aspx.vb. После щелчка правой кнопкой мыши на файле default . aspx в окне Solution Explorer и выбора команды Build and Browse (Скомпоновать и просмотреть Web-форму) произойдет следующее. Во-первых, Web-форма скомпили- руется в DLL-файл и при каждом последующем вызове этой Web-формы ASP.NET будет использовать ее откомпилированную версию. Во-вторых, откроется окно Web-броузера с отображением результатов выполнения кода, как показано на рис. 11.11. (Чтобы от-
379
крыть проект данного примера и выполнить приложение, нужно разрешить совместное использование текущего каталога с файлами примера. При работе с операционной системой Windows 2000 для этого щелкните правой кнопкой мыши на имени каталога, т.е. Novelty в данном примере, и выберите в контекстном меню команду Sharing (Совместное использование). Затем выберите вкладку Web Sharing (Совместное использование в Web-среде), переключатель Share this folder (Совместно использовать данный каталог) и в диалоговом окне Edit Alias (Редактировать псевдоним) введите Web-имя каталога, т.е. Novelty в данном примере. — Прим. ред.)
defaBfr.*sp\ I default aspy.vti Browse -|
CaroleVermeren Cathy Johnson Eric Haglund Me Ryan
* Richard Halpin
К athle en J ohns on Sorel Polito
. SorelTerman Randy Hobaica Matthew Haglund Cathy Vermeren
I Brad Townsend JenniferEves Steve Marshall Laura Davidson Angela Stefanac Marjorie Bassett Joe Chideya Katie Chideya Terri Allen
РИС. 11.11. Результатотображе- ния Web-формы default . a spx в окне Web-броузераРИС. 11.11. Результатотображе- ния Web-формы default . a spx в окне Web-броузераMike Doberstein Tem Woodruff Cathy Rosenthal
РИС. 11.11. Результатотображе- ния Web-формы default . a spx в окне Web-броузера
РИС. 11.11. Результатотображе- ния Web-формы default . a spx в окне Web-броузера
Margaret Eves
Mikki Lemay
RandyNelson
НА ЗАМЕТК у
Коды всех примеров в данной книге можно скопировать с Web-страницы книги на Web- сервере Издательского дома "Вильяме" по адресу: www.williamspublishing. com.
Применение элемента управления DataGrid
В первых версиях набора инструментальных средств разработчика .NET Framework SDK для демонстрации примеров итерирования коллекции данных применялся пользовательский элемент управления DataGrid, который имеет очень мало или не имеет вообще ничего общего с элементом управления ActiveX. По сути, он означает использование в Web-форме некоторых шаблонных функций, которые выполняются в серверной части в целях создания HTML-кода для клиентской части. Эта технология обладает очень большими возможностями, поскольку позволяет создавать весьма сложную логику, связанную с пользовательским интерфейсом, в виде повторно используемого компилированного объекта, который полностью безопасен и совместим
с любым типом Web-броузера. Дело в том, что серверные элементы управления в итоге создают HTML-код и вопросы совместимости полностью зависят от разработчика.
Элемент DataGrid является характерным примером элемента управления, который позволяет разработчику просто связать объект DataSet с объектом DataGrid и мгновенно получить визуальное представление данных. В листинге 11.3 показан пример создания базового запроса для заполнения данными элемента управления Da- taGrid. Для использования любого серверного элемента управления его нужно объя- а после объявления его методы и свойства можно использовать так же, как любые другие объекты Web-страницы. Итак, здесь создается новая Web-форма aspx, которая содержит элемент управления DataGrid.
ЛИСТИНГWeb-формаaspx
Import Namespace =%>
Import Namespace =%>
<%@ Page
Codebehind=”WebGrid.aspx.vb" Inherits=”Novelty.WebGrid"%> <!DOCTYPE HTML PUBLICHTML 4.0
<HTML>
<HEAD>
<title>WebGrid</title> As String
=
Dim conn As New
DimAs New* FROMconn)
conn.Open()
Dim dReader As SqlDataReader =
cmd.ExecuteReader(CommandBehavior.Closeconnection) = dReader
dataGridl.DataBind
dReader.Close() conn.Close() %>
<metaVisual
Basic
<meta name="vs_defaultClientScript" content="JavaScript">
<meta name="vs_targetSchema" <5
content="http://schemas.microsoft.com/intellisense/ie5">
</HEAD>
<body
<form id="Forml" method="post" runat="server"> <asp:DataGrid id="DataGridl"
101;
POSITION: absolute; TOP: runat="server" Width-"64Opx" Height="480px" BackColor="#fffff5" BorderColor="black" ShowFooter="true" CellPadding="l" CellSpacing="l" Font-Name="Arial" Font-Size="8pt"
HeaderStyle-BackColor="#cOcOcO" EnableViewState="false"
</asp:DataGrid>
</form>
</body>
</HTML>
После выполнения этого кода по отношению к базе данных Novelty полученные результаты будут иметь такой вид, как на рис. 11.12.
^rSNwre^LesfNane
Company
Qty
Phone
S
LastNameSo
Carole| Ver merer
6227 East
Crossing Drive
Ocean
Glen
NH
90609
6034485994
2
Cathy iJchrrson
1629West River Street
Big Carter
NC
18602
9193559205
3
Eric«Haglund
9193West
Beach Street
Brown
Heights
OK
83481
40593106B9
4
JulieIfiyan
9161 Fort Beach
Way
South Pori
26973
5025245220
-
Richard |на!р1п
9790 Happy River Street
North Lake
MN
62875
6124066311
6
Kathleen iJc-hnson
9385 West
Heights Street
Brown Towne
59609
3138032214
2104 Brown
Brook Drive
Blue Valley
MT
54401
4067260212
в
Sorel iTerman
1920West Point Street
Blue Bkrtfs
08965
6086246867
Randy Ihob&ca
4619 North
Plains Drive
Brown Ridge
CT
09793
2039421728
10
Matthew iHaglund
8725 Sunset Crossing Avenue
New Brook
79013
5014589191
1
РИС. 11.12. Внешний вид Web-<pop.M/w WebGrid.a spx
Серверный элемент управления предлагает способ отображения данных без создания вручную какого-либо кода, связанного с механизмом отображения данных. Учтите, что серверные элементы управления в большинстве своем созданы на основе фрагментов Web-страниц,, или пэйджлетов (pagelet). Они не являются полноценными ASPX-страницами, имеют расширение . ascx и представляют только некоторые разделы Web-страницы, например повторно используемую форму или код, который применяется для отображения сообщения об ошибке и является частью более общей Web-страницы с информацией о происходящих ошибках. Такие серверные элементы управления позволяют выполнять практически любые функции с помощью Web- ориентированного пользовательского интерфейса.
НА ЗАМЕТКУ ——=
В среде с динамическим содержимым некоторые переменные, например строка подключения к базе данных, часто остаются статическими. В листинге 11.1 для строки подключения в куче ищется (interned) экземпляр строки connString. Если он найден, то проверяется его содержимое. Если значение экземпляра в куче совпадает co значением строки connString, то оно используется приложением. В противном случае создается новый экземпляр и ему присваивается значение строки connString. Данный подход позволяет экономить ресурсы, потому что среда CLR гораздо быстрее выполняет проверку кучи, чем создание нового экземпляра объекта-строки. Такое повторное использование строк позволяет повысить производительность приложения.
ЭДанный способ не следует применять для строк, значение которых часто изменяется, потому что нет никакого смысла проверять значение экземпляра, когда навернякачто оно другое. Несмотря на это, его можно применять дляес
ли свойства содержат особый набор значений, например при работе с
ем. Для повышения производительности приложения и эффективного использования ресурсов в таких случаях применяется метод intern. В главе 12,и
технологии промежуточного уровня", описываются другие технологии повышения эффективности приложений.
Повышение производительности приложений с помощью хранимых процедур
Благодаря достоинствам платформымногие разработчики стали полагаться
на клиентскую часть приложения для реализации специализированных бизнес- правил. Хотяна платформекомпилируются и выполняются бы
стрее, чем обычные ASP-страницы, все же для большинства организаций управление такой бизнес-логикой (или ее поддержка) остается достаточно сложным.
В большинстве случаев сервер базы данных оказывается недоступным для разработчиков, т.е. они не могут управлять количеством процессоров, размером оперативной и дисковой памяти, а также способом резервного копирования. Для большинства операций правильно сконфигурированный сервер базы данных является наименее требовательным к ресурсам компьютером. Ниже перечислены типичные рекомендуемые конфигурации компьютеров, которые могут использоваться для Web-сервера и сервера базы данных.
Компъ ■Спеуи^гскауг <
Web-серверPentium 4, частота 800 МГц, 1 Гбайт оперативной памяти, массив
RAID 5 дисков SCSI емкостьюГбайт
Сервер базы Quad-Pentium 4 Хеоп, частота 800 МГц, 4 Гбайт оперативной памяти
данныхмассив RAID 5 дисков SCSI емкостью 72 Гбайт для базы
данных и журналов регистрации транзакций, дополнительные сетевые платы и источники бесперебойного питания
Часто для повышения производительности рекомендуется использовать хранимые Вот что происходит при поступлении Web-запроса к базе данных.
Пользователь открываетвводит данные, а затем передает их
Web-серверу.
Web-сервер анализирует полученные данные и выполняет проверку и/или сбор данных, посланных пользователем.
Затем пользователю пересылается отклик, обычно в виде Web-страницы с уведомлением об успехе или неудаче запроса.
Пользователь продолжает работу с Web-приложением.
383
Основная задача заключается в том, чтобы сократить количество запросов к Web- серверу и не увеличивать размер Web-страниц. Ниже перечислены действия, которые выполняет компьютер при поступлении запроса.
От пользователя поступает запрос с некоторыми данными.
Имеют ли данные корректный формат? Если да, то поместить их в базу данных; в противном случае обработать возникшие исключительные ситуации.
Можно лиустановитьсоединениес базой данных?Еслида,топродолжитьра
боту; в противном случае обработать возникшие исключительные ситуации.
Может ли база данных получить данные? Если да, то продолжить работу; в противном случае обработать возникшие исключительные ситуации.
Подключен ли клиент или уже нет? Если да, то послать отклик; в противном случае закрыть подключение к базе данных.
Очевидно, что Web-сервер должен выполнить множество операций, помимо обработки кода в Web-страницах. Более того, в предыдущих перечнях операций не учитывается возможность манипулирования создаваемыми объектами.
Попробуем теперь изменить код в листингах 11.1 и 11.2для применения хранимой процедуры, предназначенной для возвращения данных Web-странице. Для этого нужно создать саму хранимую процедуру, которая, по сути, создает основу для двухуровневого приложения.
ВНИМАНИЕ! :
При работе с любым приложением ASP.NET для сбора данных из формы нужно использовать метод Server.HTMLEncode(Request.Form("objectName")), который позволяет предотвратить межузловые атаки на основе сценариев. Этот метод кодирует информацию в виде строкового литерала, понятного SQL Server, что позволяет исключить риск применения злоумышленных операций, например вставки команды в виде подстроки "; TRUNCATE TABLE MASTER" в текстовое поле txtFirstName Web-страницы и выполнения ее со стороны SQL Server.
Некоторые разработчики опасаются использовать хранимые процедуры в Web- приложениях. Эти опасения вызваны сложностями использования объектов Command и SQLCommand в технологиях ADO иADO.NET вместе с хранимыми процедурами. Как отмечается в главе 4, "Модель ADO.NET: провайдеры данных", указание параметров в коде необходимо для "предварительной квалификации" данных за счет установки их типа. При этом SQL Server и платформа .NET избавлены от излишних затрат времени на определение типа передаваемых данных (целое число, строка и т.д.). В данном примере рассматривается хранимая процедура без параметров, код которой показан в листинге 11.4.
НА ЗАМЕТКУ
С помощью типов, описанных в разделе о включении учетной записи ASPNET в список учетных записей SQL Server, необходимо конфигурировать роль publ ic с указанием разрешения EXEC на выполнение процедуры. В листинге 11.4показан код процедуры, а в листинге 11.5 — способ ее применения.
ЛИСТИНГ 11.4. КОД процедуры sp GetCustomersOrders
CREATE PROCEDURE sp_GetCustomersOrders
SELECT tblCustomer.LastName, tblCustomer.FirstName, tblOrder.OrderDate
FROM tblOrder
INNER JOIN tblCustomer ON tblCustomer.ID =
ORDER BY tblCustomer.LastName
GO
Обратите внимание, что в запросе используются только действительно нужные поля, а не все поля таблицы tblCustomer, как показано в листинге 11.3. Вообще говоря, никогда не следует запрашивать поля таблицы, которые не будут использоваться. Это особенно важно при расширении масштаба приложения.
Рассмотрим теперь измененный код подпрограммы ShowCustomers, который показан в листинге 11.5. Обратите внимание, что в него включена еще одна строка для обработки даты, возвращенной хранимой процедурой.
Листинг 11.5. Обращение к хранимой процедуре
ИЗ подпрограммы ShowCustomers
Private Sub ShowCustomers()
' Это простой пример использования функции,
' которая извлекает данные из таблицы tblCustomer
' и отображает их.
' Инициализация объекта подключения строкой
' подключения.
Dim conn As New SqlConnection(connString)
' Затем инициализация объекта-команды
1 строкой выполняемой команды SQL.
Dim cmd As New SqlCommand("exec sp_GetCustomersOrders", conn) conn.Open()
Dim dReader As SqlDataReader =
cmd.ExecuteReader(CommandBehavior.CloseConnection)
While dReader.Read
Response.Write(dReader.GetString(0) )
Response.Write(" " & dReader.GetString(1) )
Response.Write(" " & dReader.GetDateTime(2))
Response.Write("<BR>")
End While
dReader.Close()
conn.Close ()
End Sub
(Учтите, что для работы этого приложения в базу данных Novelty должна быть включена хранимая процедура sp_GetCustomersOrders, как описывается в главе 3, "Знакомство с SQL Server 2000". — Прим. ред.)
С помощью хранимых процедур, как правило, удается значительно повысить производительность работы Web-приложений с базами данных. Дело в том, что хранимые процедуры компилируются на сервере баз данных и выполняются гораздо быстрее по сравнению с динамически создаваемыми запросами. В листинге 11.1 команда SELECT должна интерпретироваться перед каждой операцией извлечения данных из базы данных SQL Server. Поэтому компилированные версии Web-страницы и хранимой процедуры выполняются гораздо быстрее. В аспекте повышения производитель-
385
ности эти особенности технологии ASP.NET и платформы .NET Framework являются их несомненным преимуществом.
Резюме
В данной главе представлены основные сведения о способах доступа к SQL Server с помощью технологии ASP.NET. Здесь показано, как извлекаются данные на основе динамических запросов и хранимых процедур без ручного кодирования механизма отображения данных в объекте DataGrid. В этой главе также упоминается промежуточный уровень, который более подробно описывается в главе 12, "Web-службы и технологии промежуточного уровня".
Вопросы и ответы
К каким базам данных можно осуществлять доступ с помощью технологии ASP.NET?
Технология ASP.NET обладает той же совместимостью с другими базами данных, что и технология ADO.NET, т.е. для любого источника данных, совместимого с ADO.NET, в ASP.NET предлагаются те же функции.
Почему для извлечения данных не используется XML-анализатор?
XML-анализатор предназначен для извлечения данных из элементов структуры, а в технологии ADO.NET предусмотрены функции анализа данных, которые имеют как формат XML, так и собственный формат базы данных. Кроме того, XML-анализатор не используется для повышения производительности.
ГЛАВАГЛАВА1212
ГЛАВА
ГЛАВА
12
12
Web-службы и технологии промежуточного уровня
В ЭТОЙ ГЛАВЕ...
Применение промежуточного уровня для презентационной логики
Обработка данных на промежуточном уровне
Доступ к объектам с помощью Web- служб
Резюме
Вопросы и ответы
Вероятно, многие читатели или еще не работали с компонентами промежуточного уровня, или имеют опыт работы с приложениями, которые включают только Web- сервер и сервер баз данных, где большая часть прикладной логики содержится в ASP- страницах, а в базе данных хранится только используемая информация. Организацию системы, при которой прикладная логика находится на промежуточном уровне или распределена среди нескольких уровней, принято было называть канализацией plumbing). В такой системе прикладная логика предоставляет пользователю все необходимые данные и службы, но полностью скрыта от него.
В течение многих лет такая "канализация" реализовывалась с помощью DLL- файлов на языке Visual Basic или Visual C++. Используемый код имел вид откомпилированного объекта, который можно было применять совместно, повторно или удаленно. Наиболее распространенным примером частого повторного использования кода являются объявления объектов ADODB.Connection и ADODB.Recordset, которые принимают строку подключения и запрос и возвращают результирующий набор записей. Эту логику можно упаковать в виде функции, принимающей в качестве параметров строку подключения и запрос, а возвращающей результирующий набор записей. Теоретически этот способ просто прекрасен, но в реальной ситуации возникают проблемы с контролем версий, удаленным доступом и общим недопониманием в среде разработчиков.
Компанией Microsoft достигнут невероятный прогресс в развитии промежуточного уровня в рамках платформы .NET Framework. Она открыла низкоуровневые библиотеки для всех языков программирования в среде VS.NET. В результате разработчики на языке Visual Basic получили гораздо более удобный и простой способ доступа к потокам и маршалингу для более эффективного управления производительностью. Следующим крупным шагом вперед было введение контроля версий на стороне сервера. Это позволяет хранить на одном компьютере проектируемую и рабочую версии кода без конфликтов между ними. Серверный контроль версий происходит благодаря тому, что на платформе .NET для их хранения используются разные каталоги, а не параметры системного реестра. Нельзя не упомянуть здесь и Web-службы. Хотя в эпоху интенсивного развития Internet эту технологию нельзя назвать совершенно новой, все же Web-службы, вероятно, являются наиболее перспективным путем развития идеологии распределенных вычислений. В данной главе рассматриваются механизмы работы промежуточного уровня и описываются способы его реализации.
Применениепромежуточногоуровня для презентационнойлогики
Данные, вводимые пользователем в Web-форму (например, дата), часто нуждаются в проверке на соответствие заданным требованиям. Большинству разработчиков известно, что эта простая задача может быть выполнена несколькими разными способами в клиентской части или на презентационном уровне. Однако это требование ограничивает клиентский код только языком HTML. В этом контексте становится ясным значение промежуточного уровня: именно на этом уровне происходит взаимодействие клиента с обрабатываемыми им данными.
В Visual Basic .NET предусмотрена удобная встроенная функция IsDate для управления процессами проверки правильности формата введенной даты. Она возвращает логическое значение, которое определяет успешное или неудачное преобразование переданной ей информации в корректный формат даты. Рассмотрим подробнее эту функцию на более высоком уровне.
Создадим Web-форму datecheck. aspx с помощью шаблона ASP.NET Web Application среды Visual Studio .NET. В данной Web-форме будут располагаться только два серверных элемента управления: текстовое поле и кнопка. После щелчка на кнопке введенная в текстовом поле информация передается серверу, на котором выполняется функция Is Date. Затем результат выполнения функции передается обратно Web- форме. В листинге 12.1 приводится вспомогательный код данной Web-формы datecheck. aspx. vb, а в листинге 12.2 — код самой Web-формы datecheck. aspx.
ЛИСТИНГ 12.1. Вспомогательный КОД Web-формы datecheck . aspx.vb
Public Class datecheck
Inherits System.Web.UI.Page
Protected WithEvents TextBoxl As System.Web.UI.WebControls.TextBox
Protected WithEvents Buttonl As System.Web.UI.WebControls.Button
#Region " Web Form Designer Generated Code "
' Этот код создан конструктором Web-форм.
<System.Diagnostics.DebuggerStepThrough()> Private Sub _ InitializeComponent()
End Sub
Private Sub Page_Init(ByVai sender As System.Object, _
ByVai e As System.EventArgs) Handles MyBase.Init
CODEGEN: Этот вызов метода организован конструктором Web-форм.
' Не изменяйте его вручную в окне редактора кода. InitializeComponent()
End Sub
#End Region
Dim Msg As String
Private Sub Page_Load(ByVai sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Buttonl.Text = "Check Date" TextBoxl.Text =
End Sub
Private Sub Buttonl_Click(ByVai sender As System.Object, _ ByVal e As System.EventArgs) Handles Buttonl.Click Msg = IsDate(Request.Form.Item("TextBoxl")).ToString Msg += "<BR>" Msg += Request.Form.Item("TextBoxl") If Page.IsPostBack Then
Response.Write(Msg)
Buttonl.Text = "Date Checked"
End If
End Sub
End Class
В листинге 12.1 нет никаких следов взаимодействия с клиентской частью.
ЛИСТИНГКОДСаМОЙaspx
<%@ Page
Codebehind="datecheck . aspx. vb" Inherits="Noveltyl. datecheck" %> <!DOCTYPE HTML PUBLICHTML 4.0
<HTML>
<HEAD>
<title>datecheck</title>
Visual
cmeta name="CODE_LANGUAGE" content="Visual Basic 7. 0">
cmeta name="vs_defaultClientScript" content="JavaScript">
cmeta name=”vs_targetSchema" <9
content="http://schemas.microsoft.com/intellisense/ie5">
</HEAD>
<body
cform id="Forml" method="post" runat="server">
<asp:TextBox101;
LEFT:POSITION: absolute; TOP:
runat="server" Width="165px" Height="20px">
</asp:TextBox>
<asp:Button102;
LEFT:absolute; TOP:
runat="server" Width="104px" Height="25px" <P
Text="Button">
</asp:Button>
</form>
</body>
</HTML> '
В листинге 12.2 элементы управления формы содержат директиву runat=server, которая указывает на выполнение сервером действий, определенных во вспомогательном коде datecheck. aspx. vb. В листинге 12.3 приведен фактически генерируемый HTML-код, который передается клиенту. Код серверного элемента управления никогда не передается клиенту. Клиент получает только HTML-результат выполнения сервером кода данного элемента управления.
ЛистингHTML-код клиента
<!DOCTYPE HTML PUBLICHTML 4.0
<HTML>
<HEAD>
<title>datecheck</title>
Visual
<metaBasic
<meta
cmeta name="vs_targetSchema" <? content="http://schemas.microsoft.com/intellisense/ie5">
</HEAD>
<body
<form
<input_
value="dDwxNDg5OTk5MzM7dDw7bDxpPDE+ «Р
Oz47bDxOPDtsPGk8Mz47PjtsPHQ8cDxwPGw8VGV4dDs+ <?
G2w8Q2hlY2sgRGFOZTs+Pjs+Ozs+Oz4+Oz4+ <5
/>
cinput name="TextBoxl" type="text" <9
7:47:24"
101;
LEFT:POSITION: absolute; TOP:/>
<input
Date"
102;
LEFT:POSITION: absolute; TOP:/>
</form>
</body>
</HTML>
В листинге 12.3 демонстрируется, что практически любая прикладная логика может быть сохранена на промежуточном уровне. В следующем разделе описываются способы работы с базой данных с помощью промежуточного уровня. Учтите, что подключение к базе данных и выполнение запроса организованы сложнее, чем проверка введенных данных.
Обработка данных на промежуточном уровне
До сих пор презентационный уровень описывался как визуальное представление приложения для клиента, а промежуточный уровень — как связующее звено между клиентом и основной частью приложения. Теперь пришло время показать роль окончательного уровнятехнологии, т.е. уровень данных. Обычно
уровень данных состоит из базы данных, которая может иметь традиционную реляционную структуру, формат XML или формат файла INI. Далее в примерах этой главы в качестве хранилища данных используется SQL Server 2000.
В листинге 11.1 из главы"Web-формы: приложения на основедля
работы с базами данных", показан простой пример использования промежуточного уровня для подключения к базе данных, выполнения запроса и возвращения объекта в виде HTML-таблицы. Этот код никак не влияет на возвращаемые данные, а только отображает их. На самом деле запрос выполняется в базе данных. В листинге 12.4 вторяется код подпрограммыиз листинга 11.5, который в
щем примере используется в другом контексте.
ЛИСТИНГКОД Подпрограммы ShowCustomers
Private Sub
Это простой пример использования функции, которая извлекает данные из таблицы и отображает их.
Инициализация объекта подключения строкой
подключения.
Dim conn As New
Затем инициализация объекта-команды
строкой выполняемой команды SQL.
Dim cmd As Newconn)
conn.Open()
Dim dReader As SqlDataReader =
cmd.ExecuteReader(CommandBehavior.Closeconnection)
While dReader.Read
Response.Write(dReader.GetString(0))
Response.Write(" " & dReader.GetString(1) )
Response.Write(" " & dReader.GetDateTime(2))
Response.Write("<BR>")
End While
dReader.Close()
conn.Close ()
End Sub
Обратите внимание на использование хранимой процедуры
sp_GetCustomersOrders, которая является типичным примером размещения прикладной логики на уровне данных приложения. Это сделано просто потому, что кратчайшее расстояние между двумя точками определяется прямой линией. Код, расположенный на сервере баз данных, компилируется для наиболее эффективного выполнения сервером. Поэтому в результате хранимая процедура, которая располагается на уровне данных, будет выполнена быстрее, чем хранимая процедура, которая располагается на промежуточном уровне.
Однако, к сожалению, хранимые процедуры можно использовать далеко не во всех ситуациях. Это ограничение наводит на мысль воспользоваться еще одним простым принципом определения кратчайшего пути как пути наименьшего сопротивления. В приведенном примере наименьшее сопротивление означает, что следующий уровень над уровнем данных используется для выполнения прикладной логики. Рассмотрим, как в многоуровневом приложении, где клиентский уровень используется только для отображения данных, можно реализовать остальные уровни.
Создание повторно используемых компонентов промежуточного уровня
В этом разделе описано создание простого запроса в виде повторно используемого объекта. Основная цель данного примера— продемонстрировать способ создания функции в одном месте и использования ее сразу в нескольких приложениях. Создайте новый или откройте уже имеющийся проект Visual Basic .NET. Щелкните правой кнопкой мыши на имени проекта в окне Solution Explorer и выберите в контекстном меню командуAddci'Add Component. В диалоговом окне Add New Item в текстовом поле Name введите имя нового компонента GetRowCount. vb.
НА ЗАМЕТКУ
Коды всех примеров в этой и предыдущей главах основаны на решении Noveltyl. ) Пространство имен Noveltyl используется в остальных примерах данной главы.
.....,!
После создания пустого компонента нужно включить в него код функции GetRow- Count, которая возвращает целочисленное значение с количеством записей в таблице tblCustomers базы данных Novelty. В листинге 12.5 приведен полный код этого
компонента.
ЛИСТИНГ12.5. КОДКомпонентаGetRowCount. vb
Imports System.Data
Imports System.Data.SqlClient
Public Class GetRowCount
Inherits System.ComponentModel.Component
Public Function GetRowCount() As Integer
Try
Dim connString As String
' Помните обсуждение в главе 11 функции
' String.Intern? Если в памяти уже есть строка
' с тем же значением, то вместо создания нового
' экземпляра будет использована ссылка на нее.
connString =
"setver=(local);database=Novelty;TRUSTED_CONNECTION=Yes"
Dim conn As New SqlConnection(connString)
Dim cmd As New SqlCommand("select count(*) from tblCustomer", conn) conn.Open()
Dim dReader As SqlDataReader =
cmd.ExecuteReader(CommandBehavior.Closeconnection)
While dReader.Read
Включить первую и единственную запись
в результирующий набор.
GetRowCount = dReader.GetValue(0)
End While
dReader.Close()
conn.Close()
Catch
System.Console.WriteLine("An error has occured " & Err.Description) End Try
End Function
#Region " Component Designer generated code "
Public Sub New(ByVai Container As System.ComponentModel.IContainer) MyClass.New()
Код, созданный конструктором компонентов. Container.Add(Me)
End Sub
Public Sub New ()
MyBase.New()
' Вызов метода, созданный конструктором компонентов. InitializeComponent()
' Инициализация после вызова метода InitializeComponent().
End Sub
1 Переопределение метода Dispose для очистки списка компонентов. Protected Overloads Overrides Sub Dispose(ByVai disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
' Код, созданный конструктором компонентов.
Private components As System.ComponentModel.IContainer
' ЗАМЕЧАНИЕ: Код, созданный конструктором компонентов.
' Его можно изменять только с помощью конструктора компонентов.
' Не изменяйте его вручную в окне редактирования кода.
<System.Diagnostics.DebuggerStepThrough() > _
Private Sub InitializeComponent()
components = New System.ComponentModel.Container()
End Sub
#End Region
End Class
Более глубокое изучение иерархии наследования используемых компонентов выходит за рамки данной КНИГИ, поэтому далее описывается лишь простой пример ее использования. Хотя в данном коде метод ToString не используется, но он применяется после компиляции вспомогательного кода. Для его применения нужно вставить в код строку Inherits System.ComponentModel.Component, которая позволяет использовать функции класса System.ComponentModel.Component, унаследованные от класса System.Object. Это наследование позволяет использовать функции данного класса без выполнения дополнительных действий.
Для компоновки нового компонента щелкните на его имени в окне Solution Explorer правой кнопкой мыши и выберите в контекстном меню команду Build. Теперь готовый компонент можно использовать, но как? Создайте Web-форму GetRow- CountTest. aspx со вспомогательным кодом, который показан в листинге 12.6. Обратите внимание, что в самом начале кода используется строка импорта компонента Noveltyl GetRowCount с функциями компонента GetRowCount.
ЛИСТИНГ 12.6. Вспомогательный КОД GetRowCountTest.aspx. vb
Imports Noveltyl.GetRowCount
PublicClassGetRowCountTest
Inherits System.Web.UI.Page
#Region " Web Form Designer Generated Code "
' Вызов метода, созданный конструктором компонентов.
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
End Sub
Private Sub Page_Init(ByVai sender As System.Object, _
By Vai e As System.EventArgs) Handles MyBase.Init
CODEGEN: Этот вызов метода организован конструктором Web-форм. ' Не изменяйте его вручную в окне редактора кода.
InitializeComponent()
End Sub
#End Region
Private Sub Page_Load(ByVai sender As System.Object, _
ByVai e As System.EventArgs) Handles MyBase.Load
Dim GRC As New GetRowCount()
Response.Write(GRC.GetRowCount.ToString)
GRC.Dispose()
End Sub
End Class
Здесь совсем не обязательно вызывать метод Dispose, предназначенный для ОЧИСТки памяти от уже не нужного объекта. Создав данную страницу и вставив в нее КОД, щелкните правой кнопкой мыши на ее имени в окне Solution Explorer и выберите в контекстном меню команду Build and Browse. После выполнения всех созданных сценариев и доступа к базе данных Novelty будет отображенаWeb-страница с числом 2000.
Использование компонента
в другом приложении
Попробуем теперь использовать созданный компонент в совершенно другом приложении. Для этого нужно создать новое приложение Visual Basic .NET на основе шаблона Windows Application среды Visual Studio .NET и создать в нем ссылку на файл Noveltyl.dll, который располагается в каталоге bin проекта Noveltyl. При использовании компонента из другого пространства имен или другого проекта нужно помнить, что dll-файл компонента находится в каталоге bin своего проекта. Например, dll-файл Web-приложения с именемДОеЬ_приложениеобычно находится в каталоге c:\inetpub\wwwroot\Web_пpилoжeниe\bin.
Для указания ссылки на внешний компонент щелкните правой кнопкой мыши на элементе References в окне Solution Explorer и выберите в контекстном меню команду Add Reference. В диалоговом окне References щелкните на кнопке Browse и укажите dll-файл используемого компонента. Для закрытия диалогового окна References щелкните на кнопке ОК. В данном примере предполагается, что dll-файл используемого компонента Noveltyl.dll находится в каталоге c:\inetpub\wwwroot\ Noveltyl\bin.■
Здесь следует напомнить, что, как и при работе с прежним приложением Windows, после указания директивы Imports его пространство имен становится полностью доступным. В общем вспомогательный код Web-формы из листинга 12.7 напоминает код из листинга 12.6. Он создан на основе шаблонного кода, сгенерированного конструктором Web-форм среды Visual Studio .NET.
Листинг 12.7. Вспомогательный код Web-формы Fermi. vb
Imports Noveltyl.GetRowCount
Public Class Forml
Inherits System.Windows.Forms.Form
^Region " Windows Form Designer generated code "
Public Sub New ()
MyBase.New()
Этот вызов метода организован конструктором Web-форм.
396
InitializeComponent()
' Инициализация после вызова метода InitializeComponent() .
End Sub
' Переопределение метода Dispose для очистки списка компонентов.
Protected Overloads Overrides Sub Dispose(ByVai disposing As Boolean) If disposing Then
If Not (components Is Nothing) Then components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
' Код, созданный конструктором Web-форм.
Private components As System.ComponentModel.IContainer
' ЗАМЕЧАНИЕ: Код, созданный конструктором компонентов.
Его можно изменять только с помощью конструктора компонентов.
Не изменяйте его вручную в окне редактирования кода.
Friend WithEvents Labell As System.Windows.Forms.Label
Friend WithEvents Buttonl As System.Windows.Forms.Button
<System.Diagnostics.DebuggerStepThrough() > _
Private Sub InitializeComponent()
Me.Labell = New System.Windows.Forms.Label()
Me.Buttonl = New System.Windows.Forms.Button()
t Ie.SuspendLayout()
1Labell
Me.Labell.Location = New System.Drawing.Point(8, 16)
Me.Labell.Name = "Labell"
Me.Labell.Size = New System.Drawing.Size(248, 16)
Me.Labell.Tabindex = 0
Me.Labell.Text = "Labell"
'Buttonl
Me.Buttonl.Location = New System.Drawing.Point(264, 8)
Me.Buttonl.Name = "Buttonl"
Me.Buttonl.Size = New System.Drawing.Size(72, 24)
Me.Buttonl.Tabindex = 1
Me . Buttonl.Text = "Test It! "
'Formi
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.Clientsize = New System.Drawing.Size(344, 54)
Me.Controls.AddRange(New System.Windows.Forms.Control() (Me.Buttonl, Me.Labell})
Me.Name = "Forml"
Me.Text = "Forml"
Me.ResumeLayout(False)
End Sub
#End Region
Private Sub Buttonl_Click(ByVai sender As System.Object, _
ByVai е As System.EventArgs) Handles Buttonl.Click
Dim GRC As New Noveltyl.GetRowCount()
Labell.Text = "There are ” & GRC.GetRowCount.ToString _
& ” rows in the table."
GRC.Dispose()
End Sub
End Class
В код, созданный конструктором форм, добавлены только объекты Label 1 и Buttonl. Текст надписи будет задан только во время выполнения приложения. Теперь можно приступить к созданию Web-службы.
Доступ к объектам с помощью Web-служб
Итак, мы приступаем к работе на совершенно новом уровне, т.е. с использованием Web-служб. С самого начала Web-среда рассматривалась как способ передачи данных между двумя точками. Именно эта исходная концепция способствовала развитию и популяризации Web-среды. Однако она обладала несколькими ограничениями и только недавно консорциум World Wide Web Consortium (W3C) начал реализовывать стандарты технологии обмена данными. Реализация Web-служб на платформе .NET основана именно на этих стандартах и использует язык XML для идентификации и передачи данных.
Допустим, что компания Jones Novelties, Incorporated решила предоставить другим компаниям быстрый доступ к своим данным без необходимости создания пользовательского интерфейса. Для решения этой задачи прекрасно подходят Web-службы.
Прежде чем приступить к решению этой бизнес-ситуации, следует привести основные сведения о Web-службах. Читатели с опытом создания Web-служб могут пропустить остаток этого раздела без ущерба для понимания остального материала или освежить свои знания, бегло познакомившись с ним.
По определению Web-службы — это объекты, которые обмениваются данными с помощью протоколов Internet, например HTTP. Причем для определения данных или набора выполняемых сервером инструкций используется XML. Эти инструкции также могут возвращать данные. Например, для включения нового пользователя с фамилией Doe и именем John в базу данных можно послать некому воображаемому Web-серверуwww.someserver. com следующий запрос:
http://www.someserver.com/sevices/dataserver.asmx?op=AddUserToDB&FName= John&LName=Doe
В данном примере используется протокол HTTP и запрос GET для вызова службы dataserver, которая имеет функцию AddUserToDB с двумя параметрами — FName и LName. Было бы просто замечательно, если бы у нас был способ доступа к Web-службе и информирования о выполняемых действиях. К счастью, Microsoft вместе с несколькими другими компаниями предусмотрела эту возможность и создала язык определения Web- служб Web Services Description Language, или WSDL (эта аббревиатура произносится как английское словосочетание wiz-dull). WSDL способен анализировать код Web-службы и находить ту информацию, которую нужно сообщить пользователям (людям или машинам) о данной Web-службе и выполняемых ею функциях. Пример такой функции приводится далее, в разделе о программном способе доступа к Web-службе.
398
Как уже отмечалось, доступ к Web-службе осуществляется с помощью HTTP- протокола (методы GET и POST). Основное различие между ними заключается в том, что при использовании GET параметры запроса передаются в URL-указателе, а при использовании POST — инкапсулируются внутри самого запроса.
Кроме того, для взаимодействия Web-служб разработан протокол объектного доступа Simple Object Access Protocol (SOAP), который позволяет Web-серверам обмениваться сообщениями с инструкциями запросов, заключенными в конверт (envelope). SOAP-конверт имеет формат XML, как показано в приведенном ниже примере.
<?xml version="l.0" encoding"utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3c.org/2001/XMLSchema-insatance> xmlns:xsd="http://www.w3c.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ShowGetRowCountResponse xmlns="http://localhost/">
<ShowGetRowCountResult>int</ShowGetRowCountResult>
</ShpwGetRowCountResponse>
</soap:Body>
</soap:Envelope>
Для передачи такого запроса необходимо создать собственный Web-запрос и послать его серверу. К счастью, на платформе .NET в пространстве имен System.NET для выполнения этих действий предусмотрены специальные средства, которые существенно упрощают выполнение этой задачи.
Рассмотрим бизнес-ситуацию, для решения которой следовало бы применить Web- службы.
База данных содержит имена 100 тыс. человек в возрасте от 14 лет до 21 года. Вместе с этими данными хранятся постоянно обновляемые ответы посетителей Web-узла на ежедневно обновляемые вопросы. Допустим, что компания Pepsi хочет регулярно знакомиться с результатами таких опросов, например за 1 млн долларов в месяц. Как предоставить ей доступ к вашей базе данных? Именно в такой ситуации пригодятся Web-службы.
Приведем еще один сценарий.
Допустим, что некий Web-узел выполняет аутентификацию входа на другие Web-узлы, которые платят ежемесячную абонентную плату и предоставляют пароль и учетное имя для создания cookie-файлов на клиентских компьютерах с обозначением аутентификации пользователей. Похоже на службу Microsoft Passport, не так ли? В этой ситуации также пригодятся Web-службы.
Web-службы дают разработчику возможность публиковать свои функции в Web- среде (Internet) или корпоративной среде (intranet), которые входят в состав других приложений или баз данных и располагаются на том же или на другом компьютере, о которых разработчик может даже не подозревать. Web-службы, по сути, стали именно тем, для чего разрабатывались СОМ-компоненты, но Web-службы можно вызывать с любого компьютера, подключенного к Internet или внутренней сети под управлением любой операционной системы.
Итак, продолжим работу с нашим компонентом и включим в него дополнительную
внешнююфункцию.
399
Публикация существующего компонента с помощью Web-службы
В большинстве случаев для публикации функций существующего компонента с мощью Web-службы достаточно поместить соответствующий код в Web-службе в виде открытого метода с возвращаемым значением. Например, для публикации с помощью Web-службы метода GetRowCount класса GetRowCount из предыдущего примера выполните перечисленные ниже действия.
В проекте Noveltyl создайте новую Web-службу
НА ЗАМЕТКУ
Для работы с компонентом из другого пространства имен следует создать ссылку на него, как описывается выше в главе, в разделе об использовании компонента из гого приложения.
В файлеasmx вставьте код нового Web-метода Show-
GetRowCount, который приведен в листинге 12.8.
Добавьте в тело метода ShowGetRowCount три строки кода, как показано в листинге 12.8.
Щелкните правой кнопкой мыши на имени решения в окне Solution Explorer и выберите в контекстном меню команду Build для компоновки решения.
После компоновки решения щелкните правой кнопкой мыши на файле NoveltyServices . asmx и выберите в контекстном меню команду View in Browser (Просмотр в окне
Сразу после загрузкиполученнойNoveltyServices
щелкните на гипертекстовой ссылке ShowGetRowCount в верхней части Web- страницы.
Убедитесь в правильности полученных результатов. (Сначала в окне Web- броузера появится описание данной Web-службы с предложением проверить ее работоспособность, щелкнув на кнопке Invoke (Вызвать Web-службу). Щелкните на этой кнопке, и в окне Web-броузера появится XML-файл с приведенными ниже строками.
<?xml version="1.0" encoding="utf-8” ?>
<int
Здесь значениетипа int — это результат выполнения метода Show-
GetRowCount Web-службы NoveltyServices. — Прим. ред.)
ЛИСТИНГФайл NoveltyServices . asmx
Imports
Imports
_
Public Class NoveltyServices
Inherits
#Region Web Services Designer Generated Code
Public Sub
MyBase.NewO
' Этот вызов метода организован конструктором Web-служб.
InitializeComponent()
Инициализация после вызова метода
End Sub
Код, созданный конструктором Web-служб.
Private components As
'Код, созданный конструктором
Его можно изменять только с помощью конструктора Web-служб.
Не изменяйте его вручную в окне редактирования кода.
<System.Diagnostics.DebuggerStepThrough()> _
Private Sub
components = New
End Sub
Protected Overloads Overrides Subdisposing As Boolean)
' CODEGEN: Этот вызов метода организован конструктором Web-служб.
' Не изменяйте его вручную в окне редактора
If disposing Then
If Not (components Is Nothing) Then components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
#End Region
Public FunctionAs Integer
' Эти строки кода остались неизменными.
Dim GRC As New
ShowGetRowCount =
()
End Function
End Class
Вот и все. Теперь после размещения этого кода на открытом Web-сервере любой пользователь может с помощью Web-броузера открыть данную Web-страницу и выполнить указанный метод. Обычно эта цель достигается программными средствами, а не вручную. В следующем разделе рассматривается пример такой ситуации.
Доступ к Web-службе программными средствами
Попробуем теперь использовать созданную Web-службу программными средствами из другогобудь то обычное приложение Windows или
Для соединения снужно создатьДля создания такого соединения можно послать простойна основе протокола HTTP, который обычно
заканчивается вопросительным знаком (?) с параметрами, разделенными амперсандами (&), например: http://search.yahoo.com/bin/search?p=VB.Net&VS.Net.
Кроме того, для создания соединения можно послать простой POST-запрос на основе протокола HTTP, параметры которого передаются в HTTP-заголовках. Еще один способ основан на применении протокола SOAP, благодаря которому Web- серверу посылается специализированный XML-файл, а Web-служба, в свою очередь, возвращает специализированный XML-файл в контейнере, заданном в SOAP-запросе.
Для демонстрации программного доступа к Web-службе создайте проект нового консольного приложения Visual Basic NET на основе шаблона Console Application среды Visual Studio .NET. Учтите, что основные принципы доступа к Web-службе остаются одинаковыми независимо от типа проекта. После создания проекта щелкните правой кнопкой мыши на элементе References в окне Solution Explorer и в контекстном меню выберите команду Add Web Reference (Создать Web-ссылку). На экране появится диалоговое окно Add Web Reference (рис. 12.1). Укажите в текстовом поле Address (Адрес) URL-указатель используемой Web-службы и нажмите клавишу <Enter>. После этого в правой части диалогового окна Add Web Reference сразу же появится описание Web-службы NoveltyServices (см. рис. 12.1). Такой результат получается благодаря языку WSDL, который вставляет метаданные с описанием Web-службы в ее код. Щелкните на кнопке Add Reference для закрытия данного диалогового окна и вставки созданной ссылки в проект консольного приложения. В окне Solution Explorer теперь появится новая Web-ссылка, аналогичная показанной на рис. 12.2.
Рис. 12.1. Диалоговое окно Add Web Reference
РИС. 12.2. Новая Web-ссылка в окне Solution Explorer
НА ЗАМЕТКУ—
i Если Для доступа К каталогу или Web-серверу нужно пройти аутентификацию, то код ; | нужно разместить в приложении, а не в Web-службе.
Теперь, после создания Web-ссылки, остается только включить в код консольного приложения три строки, которые показаны в листинге 12.9 с полным кодом основного модуля Module1. vb консольного приложения ConsoleApplicationl.
ЛИСТИНГ 12.9. КОД ОСНОВНОГО МОДУЛЯ Modulel.vb■
Module Modulel
Sub Main()
Dim GRC As New localhost.NoveltyServices()
Try
System.Console.WriteLine(GRC.ShowGetRowCount.ToString)
GRC.Dispose()
Catch
System.Console.WriteLine(Err.Description)
End Try
End Sub
End Module
Здесь объект GRC обозначает Web-службу localhost. NoveltyServices () • Дело в том, что в данном примере используется локальный Web-сервер localhost, KOTO- рый в листинге 12.8 играет роль пространства имен для данной Web-службы. Учтите, что пространства имен должны иметь уникальные имена (например, название вашей компании) или полностью квалифицированное имя домена, на котором располагаются ваши Web-службы. Например, сотрудники компании Microsoft могут использовать в качестве имени для пространства имен адресwww.microsoft.com. Тогда объявление нового объекта ob j ectname в данном пространстве имен будет иметь следующий вид:
new www.microsoft.com.objectname
После вставки дополнительных строк кода в основной модуль Modulel. vb и указания Web-ссылки на Web-службу можно скомпоновать все решение. Для этого щелкните
правой кнопкой мыши на имени решения в окне Solution Explorer и выберите в контекстном меню команду Build Solution. Для запуска полученного приложения откройте консольное окно Windows и в режиме командной строки укажите каталог проектов VS.NET, который по умолчанию имеет вид
C:\Documents and 5еРР1пдз\<имя_лользователя>\Му Documents\Visual Studio Projects
Далее найдите в основном каталоге этого консольного приложения (т.е. в каталоге ConsoleApplicationl в данном примере) его подчиненный каталог bin. В нем содержится выполняемый файл проекта ConsoleApplicationl.exe. Введите в командной строке путь к нему, имя, а затем нажмите клавишу <Enter>.
C:\Documents and ЗеЪМпдз\<имя_пользователя>\Му DocumentsWisual Studio Projects\ConsoleApplicationl\bin\ConsoleApplicationl. exe
Спустя несколько секунд в командной строке появится результат выполнения консольного приложения и вызванной им Web-службы, а именно: количество записей в таблице tblCustomer базы данных Novelty.
Заключительные замечания
В последних главах описываются и демонстрируются на примерах несколько технологий, предназначенных для достижения одной цели: максимально эффективного использования базы данных для экономии денежных средств. Использование новых технологий для экономии денежных средств — это довольно спорный путь, потому что часто сами понятия новая технология и экономия денежных средств являются взаимно исключающими.
С самого начала книги, где рассматривались этапы планирования и создания базы данных, и до последних страниц, где описываются способы расширения функциональности приложения за счет повторно используемого кода и Web-служб, авторы преследовали одну и ту же цель, а именно: создать надежное приложение с помощью наиболее эффективных средств. Многие примеры из этой книги можно буквально скопировать и вставить в приложение, используя практически без всяких изменений (не считая строк запросов). Авторы уверены, что использование представленной информации позволит читателям успешно создавать и размещать приложения с помощью Visual Basic.NET и SQL Server на основе традиционной технологии кли- ент/сервер ("толстый" клиент + Windows-формы) или Web-ориентированной технологии ("тонкий" клиент + Web-формы).
Резюме
В этой главе описываются способы организации и реализации промежуточного уровня для повышения производительности приложения и повторного использования кода. Здесь показано, как повторно используемый компонент можно применить с помощью Web-службы практически в любом приложении. Все эти важные концепции автор постарался изложить в максимально сжатом виде, чтобы читатель смог убедиться в простоте их реализации без дополнительных затрат времени и усилий на изучение всех тонкостей этого процесса.
Вопросы и ответы
Можно ли вызывать Web-службы из языка Java?
Да. В языке программирования Java предусмотрена библиотека классов j ava.net, предназначенная для подключения к Web-службе (по заданному URL-указателю посредством протокола HTTP), которая работает на платформе .NET. Для синтаксического анализа полученного результата в языке программирования Java применяются специализированные классы для работы с XML-файлами.
Как опубликовать Web-службу через брандмауэр?
Web-службы обычно доступны через порт 80 (который используется для HTTP), поэтому, с точки зрения системного администратора, для них нет никаких ограничений. На самом низком уровне Web-служба — это не что иное, как Web-страница без графического интерфейса пользователя. Что касается программного доступа, то, конечно, можно создать несколько методов аутентификации доступа на Web-сервер.
Предметный указатель
A
Active Server Pages (ASP), 368
ActiveX, 368
ADO.NET, 26; 164; 165; 330
ASP.NET, 368
ASP.NET Web Application, 376
ASPX-файл, 368
c
Common Language Runtime (CLR), 368
СОМ-объект, 368
D
Data Definition Language (DDL), 43; 66;
92; 110
Data Manipulation Language (DML), 66
Desktop Engine (MSDE), 100
DiffGram, 340
Document Object Model (DOM), 310; 313
E
Entity Relationship Diagram (ERD), 37
Extensible StyleSheet Language (XSL), 310
Extensible StyleSheet Language
Transformations (XSLT), 310
H
HTML, 308
HTTP-протокол, 398
I
IntelliSense, 280
Internet Information Server (IIS), 318
J
JavaScript, 368
M
Microsoft Access Upsizing Tools, 131
Microsoft SQL Server
запуск и остановка, 104
создание рабочей базы данных, 108
создание таблиц, 110
удаление объекта, 144
Microsoft SQL Server 2000, 99
Microsoft Transaction Server, 368
MSXML-анализатор, 312
0
Query Analyzer, 89
s
Simple Object Access Protocol (SOAP), 308;
311; 398
SOAP-конверт, 398
SQL Query Analyzer, 116
SQL Server Enterprise Manager, 106
SQL Server Service Manager, 104
SQLXML, 318
Structured Query Language (SQL), 22; 66
V
VBScript, 368
Visio, 28
Visual Studio .NET, 288
Visual Studio .NET Enterprise Architect, 28
w
Web-служба, 397
Web-форма, 368
Windows Forms, 53
WSDL, 401
X
XML, 165; 280; 308
XML Path Language (XPATH), 310
XML Web-служба, 313
XML-Data Reduced (XDR), 364
XML-анализатор, 312
XML-документ, 26
XML-источник, 361
XSD-файл, 340
XSD-формат, 364
А
Архивируемые данные, 191
Аутентификация Windows, 136
Аутентификация на основе HTTP, 321
Б
База данных, 20 нормализация, 48 проектирование, 22 реляционная, 20 схема, 28
Бизнес-правила, 124
Бизнес-решение, 21
Бизнес-ситуация
SQL-сценарий для создания базы данных, 144
знакомство с компаниейJones Novelties
Incorporated, 21
комбинация нескольких связанных
таблиц, 253
подготовкаXML-файлов для бизнес-
партнеров, 347 проектирование таблиц
и отношений, 23
просмотр данных из разных источников, 275
создание процедуры для
архивирования старых заказов по годам,191
создание триггера для поиска созвучных слов, 132
В
Ввод данных, 206
Виртуальная таблица, 133
Виртуальные имена, 322
Виртуальный каталог, 320
Внешнее объединение, 79
Внешний ключ, 44; 94
Внутреннее объединение, 79
Вставка записей, 206
Выполнение команд SQL, 117
Вычисления типа клиент/сервер, 98
Г
Генерация первичных ключей, 114
д
Диаграмма отношений между объектами базы данных, 37
Диапазон контейнера, 310
Директива runat=server, 390
Драйвер базы данных, 38
3
Запись, 20
Запись XML-данных, 334
Запрос, 21; 66
на добавление, 90
на обновление, 89
на объединение, 87
на создание таблиц, 91
на удаление, 90
Запросы с группировкой, 83
Запуск сценария, 296
И
Идентификационное поле, 112
Импорт пространства имен, 171
Индекс, 31
Индикатор состояния сервера, 105
Инсталляция SQL Server 2000, 101
Инструмент предварительного просмотра
данных, 253
Исключительная ситуация, 196; 265
Источник данных, 170
Итоговые значения, 83
К
Канализация, 388
Каскадные обновления, 47
Каскадные удаления, 47
Класс XMLDocument, 315
Классическая модель ADO, 164
Ключевое слово
ALL, 87
ASC, 76
DESC, 74
PRIMARY, 93
TOP, 75
UNIQUE, 93
Команда
Action1^ New Login, 135
create proc, 128
Data(Generate Dataset, 54
drop, 144
File^Disconnect, 141
Kill, 143.
New Database, 108
NewTable, 111
New view, 122
Query^ ClearWindow, 117
Tools^SQL Server Query Analyzer, 128
USE, 119
Командные файлы, 298
Компоненты промежуточного уровня,
388
Компоненты среднего уровня, 124
Конверт, 398
Конкатенированный ключ, 64
Конструктор запроса, 251; 303
Конструктор представлений, 78
Контроль данных
на уровне механизма управления базами данных, 62
Критерий, 66; 71
Л
Левое внешнее объединение, 81
м
Метаданные, 308
Метод
AcceptChanges, 208; 270
AddNew, 61
BeginEdit, 270
BeginTransaction, 195
CancelCurrentEdit, 61
DisplayDataSet, 212
Dispose, 395
EndEdit, 270
ExecuteNonQuery, 180
ExecuteReader, 185
ExecuteScalar, 184
ExecuteXmlReader, 174; 359
Fill, 233
FillSchema, 238
Find, 213; 269
FindRows, 269
GetChanges, 239
GetColumnError, 210
ReadXmlSchema, 334
RejectChanges, 270
RemoveAt, 61
Response.Redirect, 370
Select, 239
Update, 238
Модель распределенных вычислений, 98
Модификация структуры таблицы, 95
н
Направление передачи данных, 177
Нарушение параллельного доступа, 262
Неэффективная база данных, 48
Новый источник данных, 37
Нормализация, 48
0
Обновление отдельной записи, 207
Обработка ошибок ввода данных, 210
Общеязыковая исполняющая среда, 368
Объединение, 77
внешнее, 79
внутреннее, 79
левое внешнее, 81
правое внешнее, 81
Объединения, 21
Объект
ADODB.Connection, 368
ADODB.Recordset, 368
BindingContext, 56
Command, 169; 173; 359
CommandBuilder, 240; 241
Connection, 169; 170
Constraint, 202
DataAdapter, 169; 233; 266
DataColumn, 202
DataReader, 26; 169; 186; 234
DataRelation, 201; 202; 236
Объект
DataRow, 202
DataRowView, 270
DataSet, 26; 200
DataTable, 201
DataView, 268
OdbcCommand, 169
OdbcConnection, 169
OdbcDataAdapter, 169
OdbcDataReader, 169
OleDbCommand, 169
OleDbConnection, 169
OleDbDataAdapter, 169
OleDbDataReader, 169
SqlCommand, 169
SqlConnection, 169
SqlDataAdapter, 169
SqlDataReader, 169
UpdateCommands, 263
XmlDataDocument, 361
XmlReader, 359
XPathNavigator, 362
XPathNodelterator, 362
Объект-адаптер, 53
Ограничение
ForeignKeyConstraint, 220
UniqueConstraint, 220
Ограничения, 93; 220
Окно
Add Data, 299
Add Database Reference, 289
Add New Item, 303
Add New Project, 288
Advanced SQL Generation Options, 250
Browse for Folder, 293
Choose a Query Type, 250
Choose Name, 111
Choose Your Data Connection, 250
Columns Collection Editor, 225
Create Command File, 299
Data Link Properties, 250; 289
Database Properties, 108
Database Role Properties, 139
DataSetCode Property Pages, 228
Define Query Parameters, 306
Execute Scripts and Queries, 297
Execute Scripts or Queries, 306
Generate Create Scripts, 292
Generate Dataset, 54
Generate the SQL statements, 250
IIS Virtual Directory Management for
SQL Server, 318
New Virtual Directory Properties, 320
Property Pages, 33
Relations Collection Editor, 226
Run On, 297
Save Change Script, 295
Server Role Properties, 138
Services Accounts, 102
Setup Type, 102
Solution Explorer, 289
SQL Server Login, 289
SQL Server Login Properties - New
Login, 135
SQL Server Service Manager, 104
Tables Collection Editor, 224; 227
View Wizard Results, 251
Virtual Name Configuration, 322
Оператор
AND, 71
BETWEEN, 72
IN, 74
LIKE, 73
OR, 71
Определение индекса, 31
Оптимистическая блокировка, 262
Основная запись, 51
Отмена внесенных изменений, 61
Отношение, 43
Отношения, 23
многие-ко-многим, 52
один-к-одному, 51
один-ко-многим,51
Отношения между таблицами, 36
п
Пакет команд SQL, 126
Пакетное обновление, 60
Папка
Databases, 108
Stored Procedures, 127; 128
Views, 121
Параллельный доступ к данным, 262
Параметр
TRUSTED_CONNECTION, 376
Параметры безопасности SQL Server, 135
Параметры строки подключения, 170
Первичный ключ, 32; 114
генерация на основе случайного
значения, 131
Пессимистическая блокировка, 262
Платформа .NET Framework, 165
Платформа базы данных, 21
Подзапрос, 88
Подстановочный символ, 73
Подчиненная запись, 52
Поле, 20
Поле AutoNumber, 113
Порядок сортировки, 74; 268
Правила контроля данных, 62
Правое внешнее объединение, 81
Предварительный просмотр данных, 253
Предложение
ALTER, 95
AS, 82
CONSTRAINT, 93
CREATE, 92
CREATE INDEX, 94
DROP, 95
FOREIGN KEY, 94
FROM, 70
GROUP BY, 84
HAVING, 84
INSERT, 90
LEFTJOIN, 79
ORDER BY, 74
SELECT, 69
SET, 89
TOP N PERCENT, 76
UPDATE, 89
WHERE, 71
Представление, 121
использование, 124
создание, 121
Представления, 67
Представления данных, 269
Презентационный уровень, 391
Префикс tbl, 25
Прикладная логика, 388
Провайдер данных, 167
Odbc, 168
Oledb, 168
SqlClient, 168
Проверка введенных данных, 61
Проверка источника данных, 39
Программа-мастер Data Adapter
Configuration Wizard, 250
Промежуточный уровень, 388; 391
Пространство имен
Microsoft.Data.Odbc, 169
System.Data, 378
System.Data.Odbc, 168
System. Data. Oledb, 168; 169
System.Data.SqlClient, 169
System.NET, 398
System.xml, 315
Процессор базы данных, 21
Псевдоним, 82
Публикация компонента, 399
Пэйджлет, 381
P
Редактор
коллекции отношений, 226
коллекции полей, 224
коллекции таблиц, 224
Режим аутентификации, 38
Режим аутентификации Windows, 103
Реинжиниринг, 37
Роли баз данных, 137
c
Свойство
CommandText, 174
CommandTimeout, 174
CommandType, 174
Connection, 174
DataRowCollection, 213
DataRowVersion, 208
DefaultView, 269
DeleteCommand, 240
HasErrors, 210
InsertCommand, 240
Parameters, 174
RowError, 210
RowFilter, 269
RowState, 208; 269
Transaction, 174
UpdateCommand, 240
Серверная часть, 98
Серверные роли, 137
Серверный элемент управления, 370
Системные базовые классы, 167
Скалярное значение, 184
Смешанный режим аутентификации, 321
Событие RowUpdating, 62
Соединение с Web-службой, 400
Создание
индексов, 94
представление, 121
скрытого столбца, 124
таблицы, 92
Сортировка
в убывающей последовательности, 74
данных, 74
по нескольким полям, 75
Ссылка на базу данных, 290
Ссылочная целостность, 45; 216
Столбец первичного ключа, 115
Строка, 20
Строка подключения, 170
Сущность, 37
Схема базы данных, 28; 33
Схема таблиц, 203
Сценарии,290
Сценарий
запуск, 296
т
Таблица, 22
Тестирование соединения с базой
данных, 39
Тип данных, 22
int, 113
varchar, 111
Тип запроса, 250
Тип установки SQL Server, 102
Типизированный объект, 280
Транзакции, 190
Триггер, 131
V
Удаление элементов базы данных, 95
Узел, 309
Уникальный идентификатор записи, 112
Уникальный индекс, 32
Учетная запись, 135
Учетная запись ASPNET, 371
Ф
Файл
web.config, 308
конфигурации, 308
Фиксация обновлений, 59
Фильтр состояния записи, 268
Фильтрация записей, 268
Формат DiffGram, 340
Фрагмент Web-страницы, 381
Функция
AVG, 86
COUNT, 86
МАХ, 86
MIN, 86
STDEV, 86
SUM, 85; 86
VAR, 86
X
Хранимая процедура, 118; 126; 179; 249
sp_help, 119
sp_helpdb, 120
sp_helptext, 130
sp_lock, 143
sp_who, 143
запуск, 128
Ц
Целостность данных, 220
ч
Чтение XML-данных из файла, 333
ш
Шаблон сценария, 291
Шаблонные функции, 379
э
Экспорт данных, 299
Элемент управления,
взаимодействующий с данными, 58
Элемент управления данными, 52
я
Явное указание команд обновления, 244
Язык манипулирования данными, 66
Язык определения данных, 43; 66; 92; по
Язык структурированных запросов, 22; 66
Научно-популярное издание
Джеффри П. Мак-Манус, Джеки Голдштейн, Кевин Т. Прайс
Обработка баз данныхна Visual Basic .NET,3-е издание
Литературный редактор Верстка
Художественный редактор Корректоры
Т. П. Кайгородова
М.А. Удалое
С. А. Чернокозинский
З.В. Александрова, Л. А. Гордиенко.
О. В. Миш^тина, Л. В. Чернокозинская
Издательский дом "Вильяме".
101509, Москва, ул. Лесная, д. 43, стр. 1.Изд. лиц. ЛР № 090230 от 23.06.99Госкомитета РФ по печати.
Подписано в печать 18.08.2003. Формат 70X100/16.
Гарнитура Times. Печать офсетная.
Усл. печ. л. 33,54. Уч.-изд. л. 23,76.
Тираж 3500 экз. Заказ № 527.
Отпечатано с диапозитивов в ФГУП "Печатный двор"Министерства РФ по делам печати,телерадиовещания и средств массовых коммуникаций.
197110, Санкт-Петербург, Чкаловский пр., 15.
ISBN 5-8459-0379-3
ISBN 5-8459-0456-0
ISBN 5-8459-0370-Х
ISBN 5-8459-0276-2
ISBN 5-8459-0428-5
Практическое использование ADO.NET
Доступ к данным в Internet
.-»—~ »
ISBN 5-8459-0421-8
ISBN 5-8459-0291-6
ISBN 5-8459-0450-1
ISBN 5-8459-0272-X
ISBN 5-8459-0343-2
ISBN 5-8459-0424-2
Cisco Svsrews
Мир книг по базам данных от издательской группы “ДИАЛЕКТИКА-ВИЛЬЯМС”
ISBN 5-8459-0138-3
вСИСТЕМЫБАЗ г
ДАННЫХ 1
ISBN 5-8459-0384-Х
ISBN 5-8459-0297-5
ISBN 5-8459-0270-3ISBN 5-8459-0270-3ISBN 5-8459-0158-8ISBN 5-8459-0158-8ISBN 5-8459-0291-6ISBN 5-8459-0291-6
ISBN 5-8459-0270-3
ISBN 5-8459-0270-3
ISBN 5-8459-0158-8
ISBN 5-8459-0158-8
ISBN 5-8459-0291-6
ISBN 5-8459-0291-6
Проектирование баз данных с помощью UML
ISBN 5-8459-0355-6
АНАЛИЗ ТРЕБОВАНИЙ И ПРОЕКТИРОВАНИЕ СИСТЕМ
^*Д4»АБ0ГКД
ИНОННЭДДцИОПНьК сисл м С ИСЛОЛЫПВАНИ1М UMI
ISBN 5-8459-0302-5
компьютерные
ШЕСТОЕ И ЩАИИЕ
Введение в
науки
ISBN 5-8459-0276-2
ISBN 5-8459-0179-0
Cisco Systems
АНАЛИЗ ТРЕБОВАНИЙ И ПРОЕКТИРОВАНИЕ СИСТЕМ
ин<М*т*ум(#чНцМ iim ■ ИСПС|ЛгЛиИДНИГМчМ1
ISBN 5-8459-0276-2
ISBN 5-8459-0239-8
Ф1ЫШ1П КРЛ'ГГКН
Лишение в
RATIONAL UNIFIED
PROCESS
ВтОГОЕ ИХИИНЕ
РАЗРАБОТКА WEB-
ПРИЛОЖЕНИЙ
С ИСПОЛЬЗОВАНИЕМ
UML
ISBN 5-8459-0203-7
ISBN 5-8459-0265-7
ISBN 5-8459-0368-8
ISBN 5-8459-0346-7
ШАБЛОНЫ ПРОЕКТИРОВАНИЯ НсеыП iKhU.miimtrnii.iNViKrnwwvwn <л*ш» и нникшмамшю
ISBN 5-8459-0301-7
ПРИМЕНЕНИЕ
ШАБЛОНОВ ПРОЕКТИРОВАНИЯ Дополнительные ШТРИХИ
ISBN 5-8459-0393-9
PuPAHOTKl
КОРПОРАТИВНЫХ JAVA-
ПВИЛОЖЕНИЙ I ПОМОЩЫО
J2EE н UML
АВ^зуальное
MwmrojtiHKE (.ПОМОЩЬЮ RATIOmROSE2002 И1Ml.
ПроектированиешдшныхстомопплоUML
\МРИШ
TI I'I'H liHVn'MHt
ISBN 5-8459-0355-6
IS8N 5-8459-0299-1
ISBN 5-8459-0425-0
u много других книг Вы
найдете на наших сайтах
Cisco Systems
^1
АЛГОРИТМИЧЕСКИЕ
ТРЮКИ ДЛЯ ПРОГРАММИСТОВ
Генри С. Уоррен, мл.
АЛГОРИТМИ11 ЕС КИЕТРЮКИДЛЯПРОГРАММИСТОВ
Генри Уоррен, мл
М
www.williamspublishing.com
в продаже
Эта книга, несмотря на ее название в оригинале — Hacker's Delight — не имеет никакого отношения к подбору паролей или взлому сетей. Здесь слову "хакер" возвращено его первозданное значение — человека увлеченного, талантливого программиста, способного к созданию чрезвычайно эффективного и элегантного кода.
В любой области человеческой деятельности эффективность эстетич на, и программирование не является исключением.
В этой книге вы познакомитесь с приемами работы с целочисленной арифметикой, которые доставят вам массу чисто эстетического наслаждения — при этом попутно повысив вашу квалификацию.
В книге воплощен сорокалетний стаж ее автора в области разработки компиляторов и архитектуры компьютеров. Здесь вы найдете множество приемов для работы с отдельными битами, байтами, вычисления различных целочисленных функций — причем большей части материала сопутствует строгое математическое обоснование. Каким бы высоким не был ваш профессионализм — вы все равно найдете в этой книге что-то новое для себя или по крайней мере книга заставит вас посмотреть на уже знакомые вещи с другой стороны. Не в меньшей степени эта книга пригодна и для начинающего программиста, который может просто воспользоваться готовыми советами из книги, применяя их в своей повседневной практике.
Ф Использование SQL Server 2000Ф Использование SQL Server 2000* Объекты ADO.NET* Объекты ADO.NET* Более сложные компоненты и технологии ADO.NET* Более сложные компоненты и технологии ADO.NET♦ Провайдеры данных SQL Server, OLEDB и ODBC♦ Провайдеры данных SQL Server, OLEDB и ODBC♦ Применение проектов баз данных для работы со сценариями SQL♦ Применение проектов баз данных для работы со сценариями SQL* Интеграция XML и ADO.NET* Интеграция XML и ADO.NET♦ Создание Web-ориентированных приложений баз данных на основе ASP.NET♦ Создание Web-ориентированных приложений баз данных на основе ASP.NETФ Web-службы и объекты промежуточного уровняФ Web-службы и объекты промежуточного уровняAddison-WesleyPearson Educationhttp://www.awprofessional.comAddison-WesleyPearson Educationhttp://www.awprofessional.comДжеффри П. Мак-Манус — разработчик, специализирующийся на создании Web-ориентированных приложений, а также автор популярных статей и докладов на конференциях VBITS, VSLiveДжеффри П. Мак-Манус — разработчик, специализирующийся на создании Web-ориентированных приложений, а также автор популярных статей и докладов на конференциях VBITS, VSLiveи VBConnections. Часто публикуется в компьютерных журналах и является автором четырех книг по программированию баз данных и компонентным технологиям, а также двух книг об использовании технологий .NET. включая Создание приложении ASP.NET, XML и ADO.NET в среде Visual Basic .NET, вы лущеную в 2003 году Издательским домом "Вильяме".ФФФДжеки Голдштейн — президент компании Renaissance Computer Systems, которая занимается разработкой программного обеспечения и консультированием в области применения программных продуктов и технологий Microsoft. Является региональным директором MSDN в Израиле и экспертом по подготовке презентаций во время официальныхмероприятий Microsoft Developer Days. Имеет многолетний опыт интеграции новых технологий, а также часто выступает на международных конференциях разработчиков VSLive, TechEd и VBITS.ФФФКевин Т. Прайс — старший архитектор программного обеспечения и соавтор нескольких книг об XML, безопасности и технологиях .NET.и VBConnections. Часто публикуется в компьютерных журналах и является автором четырех книг по программированию баз данных и компонентным технологиям, а также двух книг об использовании технологий .NET. включая Создание приложении ASP.NET, XML и ADO.NET в среде Visual Basic .NET, вы лущеную в 2003 году Издательским домом "Вильяме".ФФФДжеки Голдштейн — президент компании Renaissance Computer Systems, которая занимается разработкой программного обеспечения и консультированием в области применения программных продуктов и технологий Microsoft. Является региональным директором MSDN в Израиле и экспертом по подготовке презентаций во время официальныхмероприятий Microsoft Developer Days. Имеет многолетний опыт интеграции новых технологий, а также часто выступает на международных конференциях разработчиков VSLive, TechEd и VBITS.ФФФКевин Т. Прайс — старший архитектор программного обеспечения и соавтор нескольких книг об XML, безопасности и технологиях .NET.Издательский дом "ВИЛЬЯМС"http://www.williamspublishing.comИздательский дом "ВИЛЬЯМС"http://www.williamspublishing.com
Ф Использование SQL Server 2000
Ф Использование SQL Server 2000
* Объекты ADO.NET
* Объекты ADO.NET
* Более сложные компоненты и технологии ADO.NET
* Более сложные компоненты и технологии ADO.NET
♦ Провайдеры данных SQL Server, OLEDB и ODBC
♦ Провайдеры данных SQL Server, OLEDB и ODBC
♦ Применение проектов баз данных для работы со сценариями SQL
♦ Применение проектов баз данных для работы со сценариями SQL
* Интеграция XML и ADO.NET
* Интеграция XML и ADO.NET
♦ Создание Web-ориентированных приложений баз данных на основе ASP.NET
♦ Создание Web-ориентированных приложений баз данных на основе ASP.NET
Ф Web-службы и объекты промежуточного уровня
Ф Web-службы и объекты промежуточного уровня
Addison-Wesley
Pearson Educationhttp://www.awprofessional.com
Addison-Wesley
Pearson Educationhttp://www.awprofessional.com
Джеффри П. Мак-Манус — разработчик, специализирующийся на создании Web-ориентированных приложений, а также автор популярных статей и докладов на конференциях VBITS, VSLive
Джеффри П. Мак-Манус — разработчик, специализирующийся на создании Web-ориентированных приложений, а также автор популярных статей и докладов на конференциях VBITS, VSLive
и VBConnections. Часто публикуется в компьютерных журналах и является автором четырех книг по программированию баз данных и компонентным технологиям, а также двух книг об использовании технологий .NET. включая Создание приложении ASP.NET, XML и ADO.NET в среде Visual Basic .NET, вы лущеную в 2003 году Издательским домом "Вильяме".
ФФФ
Джеки Голдштейн — президент компании Renaissance Computer Systems, которая занимается разработкой программного обеспечения и консультированием в области применения программных продуктов и технологий Microsoft. Является региональным директором MSDN в Израиле и экспертом по подготовке презентаций во время официальныхмероприятий Microsoft Developer Days. Имеет многолетний опыт интеграции новых технологий, а также часто выступает на международных конференциях разработчиков VSLive, TechEd и VBITS.
ФФФ
Кевин Т. Прайс — старший архитектор программного обеспечения и соавтор нескольких книг об XML, безопасности и технологиях .NET.
и VBConnections. Часто публикуется в компьютерных журналах и является автором четырех книг по программированию баз данных и компонентным технологиям, а также двух книг об использовании технологий .NET. включая Создание приложении ASP.NET, XML и ADO.NET в среде Visual Basic .NET, вы лущеную в 2003 году Издательским домом "Вильяме".
ФФФ
Джеки Голдштейн — президент компании Renaissance Computer Systems, которая занимается разработкой программного обеспечения и консультированием в области применения программных продуктов и технологий Microsoft. Является региональным директором MSDN в Израиле и экспертом по подготовке презентаций во время официальныхмероприятий Microsoft Developer Days. Имеет многолетний опыт интеграции новых технологий, а также часто выступает на международных конференциях разработчиков VSLive, TechEd и VBITS.
ФФФ
Кевин Т. Прайс — старший архитектор программного обеспечения и соавтор нескольких книг об XML, безопасности и технологиях .NET.
Издательский дом "ВИЛЬЯМС"
http://www.williamspublishing.com
Издательский дом "ВИЛЬЯМС"
http://www.williamspublishing.com
Это практическое руководство разработчика программного обеспечения на Visual Basic .NET и ADO.NET, предназначенное для создания приложений баз данных на основе WinForms, Web-форм и Web-служб. В книге описываются практические способы решения задач доступа к данным, с которыми сталкиваются разработчики на Visual Basic .NET в своей повседневной деятельности. Авторы делятся своими секретами наиболее эффективного использования технологий на основе платформы .NET.
Книга начинается с основных сведений о создании баз данных, использовании языка структурированных запросов SQL и системы управления базами данных Microsoft SQL Server 2000. Затем рассматриваются способы использования основных объектов модели ADO.NET для доступа к данным в реляционных базах данных. Благодаря подробным примерам читатели могут изучить способы использования основных свойств и методов, а также узнать о более сложных компонентах и технологиях. Многочисленные листинги с кодом на языке Visual Basic .NET иллюстрируют используемые концепции, а бизнес-ситуации показывают практическую область их применения. Заключительные главы посвящены описанию языка XML и вопросов интеграции модели ADO.NET с технологией ASP.N ЕТ в целях создания приложений для работы с базами данных разного типа.
В книге рассматриваются следующие основные темы:
♦ SQL-запросы и команды
Третье издание книги Обработка баз данных на Visual Basic® .NET позволяет разработчикам на языке Visual Basic получить новые знания и овладеть новыми навыками, которые пригодятся им для быстрого создания мощных приложений баз данных нового поколения на основе технологий платформы .NET.