WWW.DISSERS.RU

БЕСПЛАТНАЯ ЭЛЕКТРОННАЯ БИБЛИОТЕКА

   Добро пожаловать!

Pages:     || 2 | 3 | 4 |
-- [ Страница 1 ] --

Предисловие Криса Селлза Development ADDISON Series WESLEY Практическое использование ADO.NET Доступ к данным в Internet Шон Вилдермьюс Практическое использование ADO.NET Pragmatic ADO.NET for

the Internet World Shawn Wildermuth A Boston • San Francisco • New York • Toronto • Montreal London • Munich • Paris - Madrid Capetown Sydney • Tokyo • Singapore • Mexico City Практическое ADO.NET Доступ к данным в Internet Шон Вилдермьюс Москва • Санкт-Петербург Киев 2003 32,973.26-018.2.75 В44 УДК 681.3,07 Издательский дом Зав. редакцией С.Я.

Руководитель проекта В. В. Александров Перевод с английского А.А. Журавлева, О.А.

Под редакцией В. Журавлева По общим вопросам в Издательский дом "Вильяме" по адресу:

info@williamspublishing.com, Шон.

В44 Практическое использование Доступ к данным в Internet. :

Пер. с англ. — М. : Издательский дом "Вильяме", 2003. — 288 с. : ил. — тит. англ.

ISBN 5-8459-0450-1 (рус.) Эта книга представляет собой практическое руководство по использованию пер вой библиотеки доступа к данным, спроектированной специально для создания Web-приложений. Содержащийся в книге материал поможет разработчи кам изучить основные концепции и познакомиться с практическими ме тодами решения распространенных задач. На первых автор пред лагает совершить небольшой экскурс в историю создания компанией Microsoft тех нологий универсального доступа к данным и проследить эволюционный путь Большая часть книги использованию библиотеки ADO.NET для взаимодействия с базами данных и остальной частью Framework. Кроме автор дает ряд полезных советов в отношении создания масштабируемых и высокопроизводительных приложений. В конце книги автор подробно излагает стратегию преобразования кода ADO в код ADO.NET.

Книга рассчитана на пользователей средней и высокой квалификации.

ББК 32.973.26-018.2. Все названия программных продуктов являются зарегистрированными торговыми марками соответствующих фирм.

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

translation from the English edition published by Pearson Esucation, Inc.

Copyright © 2003 by Shawn All rights reserved. No 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 re trieval system, without permission from the Publisher.

Russian language edition published by Williams Publishing House according to (he Agreement with Enterprises International, Copyright © ISBN 5-8459-0450-1 © Издательский дом "Вильяме", 0-201-74568-2 (англ.) © Shawn Wildermuth, Оглавление Предисловие Благодарности Часть I. Основы Глава 1. Причины возникновения и краткий обзор Глава 2. ADO.NET: подключение к источнику данных Глава 3. Выполнение команд Глава 4. Получение данных Часть II. Класс DataSet Глава 5. Создание DataSet Глава 6. Типизированные классы DataSet Глава 7. Манипулирование объектом DataSet Глава 8. Обновление базы данных Часть III. Практическое использование ADO.NET Глава 9. ADO.NET и XML Глава 10. Привязка данных в ADO.NET Глава 11. Масштабируемость и производительность приложений ADO.NET Приложение А. Стратегии перехода от ADO к ADO.NET Предметный указатель Содержание Введение Предисловие Для кого предназначена эта книга Часть I. Основы Глава 1. Причины возникновения и краткий обзор ADO.NET Глава 2. ADO.NET: подключение к источнику данных Глава 3. Выполнение команд Глава 4. Получение данных Часть II. Класс Глава 5. Создание объекта DataSet Глава 6. Типизированные классы DataSet Глава 7. Манипулирование объектом DataSet Глава 8. Обновление базы данных Часть III. Практическое использование ADO.NET Глава 9. ADO.NET и XML Глава 10. Привязка данных в ADO.NET Глава Масштабируемость и производительность приложений ADO.NET Приложение А. Стратегии перехода от ADO к ADO.NET Благодарности Часть I. Основы ADO.NET Глава 1. Причины возникновения и краткий обзор Краткая история универсального доступа к данным ADO.NET Краткий курс ADO.NET Пространства имен ADO.NET Структуры данных Объектная модель управляемого поставщика данных ADO.NET Резюме Глава 2. ADO.NET: подключение к источнику данных Первые впечатления Соединения Строки соединений Встроенная система безопасности Изменение базы данных Организация пула соединений События объекта Connection Фабрика соединений 6 Содержание Получение информационной схемы базы данных с помощью поставщика OLE DB Обработка ошибок в Исключения ADO.NET Резюме Глава 3. Выполнение команд Команды Создание объекта Command Типы команд Выполнение команд Результирующий набор данных Использование параметров Транзакции и ADO.NET Уровни изоляции Точки сохранения в SQL Server Службы Enterprise Services и СОМ+ Пакетные запросы Резюме Глава 4. Получение данных Чтение данных Краткий обзор SQL-оператора SELECT Объект DataReader Создание объекта DataReader Функциональность объекта DataReader Доступ к данным с помощью объекта DataReader Параметры метода ExecuteReader Что такое результирующие наборы данных? Обработка множественных результирующих наборов данных Работа с метаданными объекта DataReader Создание простого приложения, с базой данных Доступ к базе данных Объекты данных Код Windows-формы Резюме Часть II. Класс DataSet Глава 5. Создание объекта DataSet Что такое объект DataSet? Структура объекта DataSet Объект DataSet и управляемые данных Заполнение объекта DataSet Объект DataAdapter Создание объекта DataSet на основе информации, хранящейся в базе данных Создание объекта DataSet на основе XML-документа Создание объекта DataSet программным путем Определение схемы объекта DataSet Зачем нужно определять схему объекта DataSet? Вывод схемы DataSet с помощью объекта DataAdapter Использование языка XSD для определения схемы объекта DataSet Содержание Создание схемы объекта программным путем ПО Схема столбца Резюме Глава 6. Типизированные классы DataSet Что такое типизированный класс DataSet? Создание типизированного класса DataSet Использование Visual Studio для создания типизированного класса DataSet Использование программы XSD.exe для создания типизированного класса DataSet Настройка сгенерированного кода с помощью специальных аннотаций Использование типизированного класса DataSet Упрощение уровня Разработка бизнес-логики Резюме Глава 7. Манипулирование объектом DataSet Изменение данных Добавление строк Удаление строк Чтение и запись значений столбцов строки Версия строки Состояние строки Перемещение по объекту DataSet Перемещение вдоль отношений Объект View Поиск в объекте DataSet Поиск с метода Поиск с помощью объекта DataView Слияние объектов DataSet Резюме Глава Обновление базы данных Проблемы, связанные с использованием отсоединенных данных Параллелизм в ADO.NET Обновление объекта DataSet с помощью объекта Реализация оптимистического параллелизма Реализация пессимистического параллелизма Реализация деструктивного параллелизма Несколько наиболее распространенных вопросов, связанных с обновлением данных Работа с объектами DataSet, состоящими из нескольких таблиц Использование локальных транзакций для обновления базы данных Получение идентификатора новой строки от базы данных SQL Server Резюме Часть III. Практическое использование ADO.NET Глава 9. ADO.NET и и XML Класс DataSet и XML Преобразование данных объекта DataSet в формат XML 8 Содержание Сохранение данных объекта DataSet в формате XML Пространства имен объекта DataSet Заполнение объекта DataSet данными из XML-файла Стратегии использования XML-документа в формате Схема объекта DataSet Класс Document Поиск данных в объекте DataSet с помощью запросов XPath Преобразование объекта DataSet с языка XSLT Резюме Глава 10. Привязка данных в Что такое привязка данных в Привязка данных в Windows-формах Простая привязка данных Сложная привязка данных Привязка данных к элементу управления Привязка типа "родитель-потомок" Использование класса Привязка данных в ASP.NET Простая привязка данных Сложная привязка данных Привязка данных к элементам управления, предназначенным для работы с данными Привязка данных с помощью объекта DataReader Некоторые соображения, производительности Резюме Глава Масштабируемость и производительность приложений ADO.NET А стоит ли волноваться?

Проектирование масштабируемых систем Связность компонентов системы Что предшествовало ADO.NET Проблемы, связанные с подсоединенными данными Проблемы масштабирования сервера баз данных Как может помочь ADO.NET Кэширование данных на Web-сервере Масштабирование информации, хранящейся в базе данных На практике Можно ли масштабировать объекты DataReader? Производительность ADO.NET Взаимодействие с базой данных Взаимодействие с объектами DataSet Несколько полезных советов Используйте схему объекта DataSet Используйте типизированные объекты DataSet для создания уровней бизнес-правил Сокращайте количество обращений к базе данных данные часто и заранее Найдите и не отпускайте от себя администратора базы данных Изолируйте разработчиков от базы данных Ограничьте использование объекта DataReader на ASP.NET Используйте фабрики соединений Не помещайте строки соединения в исходный код Не предоставляйте пользователям доступ к базе данных Резюме..

Приложение А. Стратегаи перехода от ADO к ADO.NET Планирование перехода на ADO.NET Изменение архитектуры Чего не хватает в ADO.NET? объектов ADO Отображение типов данных ADO на типы данных Поставщики и управляемые поставщики Преобразование кода установки соединения Преобразование кода создания объекта команды Преобразование кода создания объекта Recordset объектов ADO Recordset в ADO.NET Резюме Предметный указатель /О Моей дорогой без терпения и заботы которой этой книги никогда бы не было.

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

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

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

Стоит ли говорить, что все это время работа велась очень продуктивно.

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

Как и ранее, Шон с энтузиазмом принялся за дело. Он создал свой собст венный Web-форум, посвященный NET, а также подписался на новый список рассылки по организованный компанией где отвечал на огром ное количество вопросов. Вскоре в базе знаний Шона был собран огромный материал о важности в современном "сетевом" мире, снабженный детальной ин формацией о практическом применении этой технологии. В итоге у Шона возник вполне законный вопрос — а не написать ли ему обо всем этом книгу? Я ответил, что если у него появится свободное время, то следует так и сделать. Написать хорошую книгу (а в случае с Шоном о другом речь просто идти не могла) очень сложно. Един ственный известный способ добиться этого заключается в том, чтобы иметь в своем сердце что-то, о чем ты просто не можешь не рассказать всему остальному миру.

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

Крис (Chris Sells), Предисловие не ADO. Наверное, это наиболее важный момент, который в книге. Понимание предполагает не только умение извлечь из базы данных или обновить ее, но прежде всего, понимание целей проектирова ния Созданная компанией Microsoft новая технология доступа к данным отличается от ее предыдущих разработок и превращает ADO в некоего доисторического монстра, Эта книга не является справочником или "полным" и руково дством по Прежде всего она написана для чтобы помочь разработчи кам делать свою повседневную работу. Другими словами, книга содержит в себе мно жество полезных советов по использованию Для кого предназначена эта книга Книга предназначена для разработчиков, у которых есть основные знания о данных и платформе И хотя читателю не обязательно иметь опыт работы с ADO, тем, у кого он есть, будет легче усвоить изложенный в книге материал.

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

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

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

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

Глава 3. Выполнение команд В конечном итоге взаимодействие с базой данных сводится к выполнению команд и получению результатов. В этой главе рассматривается объект Command и его ис пользование для выполнения простых параметризованных запросов, хранимых процедур и операторов пакетного выполнения.

Глава 4. Получение данных Объект ADO.NET предназначен для извлечения информации из базы данных. В главе 4 рассматривается использование объекта DataReader для доступа к базе данных и приводится пример класса, в котором этот объект выступает в каче стве источника данных.

Часть II. Класс DataSet Объект DataSet — это ADO.NET. Для его эффективного использования не обходимо понять, как он может помочь при решении повседневных задач доступа к данным.

Глава 5. Создание объекта DataSet Эта глава поможет разобраться в том, что представляет собой объект DataSet и по чему его необходимо использовать. Рассматривается множество способов создания объекта DataSet, включая применение для этой цели объекта DataAdapter и документа XML. В эту главу вошло также описание использования схемы объекта DataSet.

Глава 6. Типизированные классы DataSet Типизированный объект DataSet помогает разработчику создавать код, способный адаптироваться к изменению схемы. Глава 6 полностью посвящена новой модели программирования, в соответствии с которой типизированные объекты DataSet соз даются и используются в качестве основы для уровня бизнес-логики приложения.

При рассмотрении примеров создания типизированных объектов DataSet используют ся средства Visual Studio и командной строки.

Глава 7. Манипулирование объектом DataSet После создания объекта DataSet вам необходимо научиться использовать его для манипулирования данными и понять его структуру. В главе 7 рассматри вается объектная модель DataSet, а также приведены примеры решения типичных возникающих при обработке данных.

Глава 8. Обновление базы данных После проведения манипулирования с объектом DataSet необходим способ отра зить внесенные изменения в базе данных. Глава 8 посвящена решению парал лелизма в рамках отсоединенного режима доступа к информации. В нее включены примеры использования встроенных средств обеспечения оптимистического паралле лизма, а также описан способ реализации пессимистического и деструктивного парал лелизма. Кроме того, в данной главе обсуждается вопрос нарушения параллелизма и приводятся конкретные примеры решения проблем, связанных с возникновением таких нарушений.

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

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

Глава 10. Привязка данных в ADO.NET В среду интегрированы две технологии, основанные на использовании форм:

Windows-формы и Web-формы. К счастью, все ADO.NET и поддерживают непосредственную привязку данных, о чем и рассказывается в главе 10.

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

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

Предисловие Благодарности Я хочу поблагодарить всех, кто помог мне написать эту книгу. Прежде всего я хочу выразить признательность Крису (Chris за его помощь на каждой стадии данного проекта. От самого начала работы над книгой до скрупулезного пересмотра каждой ее главы большее количество раз, чем я могу сосчитать, Крис делал чтобы книга получилась хорошей. К тому же он был моим наставником и вдохновителем, поддерживающим меня на каждом шагу и понять, на чем нужно сосре доточиться прежде всего. Я также благодарен бессчетному количеству людей из спи ска рассылки DevelopMentor которые отвечали на мои вопросы, а также задавали мне такие которых я даже и не думал касаться в этой книге. Кроме того, я признателен всем сотрудникам компании Infor mation Systems за терпение, проявленное ими во время моей работы над этой книгой.

Напоследок я хотел бы поблагодарить всех тех людей, которые способствовали выходу книги в свет. Это Триша Палее Palese), Стефани Томас (Stephanie Tho mas), Крис Таварес (Chris Боб Бошемин (Bob Питер (Peter Zaleksy), Кристоф Фок Скотт Юранек (Scott Juranek), Гленн Тиммз (Glenn Эдвард (Edward Hinton), Марк Изрейел Israel), Джозеф Фикара (Joseph (David (Todd Стивен Райт (Steven Wright), Маршал Харрисон (Marshall Harrison), Кристин Эриксон (Kristin Дженни фер (Jennifer Allen) и Омри Гэцит Шон (Shawn штат Массачусетс, июнь 2002, 16 Благодарности Часть I Основы ADO.NET Глава 1. Причины возникновения и краткий обзор ADO.NET Глава ADO.NET: подключение к источнику данных Глава 3, команд Глава 4, Получение данных Глава Причины возникновения и краткий ADO.NET В этой Краткая история универсального доступа к данным Преимущества ADO.NET Краткий NET Добро пожаловать в мир ActiveX Data Objects или же просто ADO.NET — именно так мы будем называть эту библиотеку в дальнейшем. К счастью для нас, ADO.NET — не просто еще один API, это в самом буквальном смысле новая филосо фия доступа к данным. Тем не менее прежде чем перейти к непосредственному изу чению ADO.NET, давайте совершим небольшой экскурс в историю.

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

Поскольку доступ к базе данных очень важен для большого числа приложений, в самом начале 90-х годов Microsoft разработала стратегию, на своим разработчикам, часто создававшим приложения, которым необходимо было взаимодействовать с базами данных на других платформах, таких, как мини компьютеры и мэйнфреймы Предложенное Microsoft решение заключалось в использовании интерфейса прикладного программирования (Application Programming Interface — API) ODBC (Open Database Connectivity — открытый интерфейс доступа к базам который позволял получать доступ к данным на подобных системах.

Вместе с тем Microsoft знала, что многим разработчикам потребуется также реше ние для настольных баз данных. Поэтому в 1992 году увидел свет язык Visual Basic 2. и интерфейс Objects, писать на Visual Basic код доступа к ODBC API. VT Objects представлял собой очень простой интерфейс, который обеспечивал немногим более, чем подключение к серверным базам данных. Другими в этом интерфейсе Microsoft представила только ту часть ODBC которая могла удовлетворить потребности в области доступа к данным.

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

В результате компания выпустила настольную базу данных Access 1.0, включающую в себя новую технологию доступа к данным Jet и новую версию интерфейса VT Object с несколько измененным названием — DAO (Data Access Objects — объекты доступа к данным). Уже в первой версии была представлена концепция объектов /8 Основы и Recordset, которая существует и по сегодняшний день. К сожалению, были присущи и недостатки — отсутствие поддержки многопоточного окруже ния крайне негативно сказывалось при возрастании нагрузки и при работе в больших клиент-серверных окружениях.

Microsoft попыталась заново "изобрести колесо", выпустив интерфейс доступа к данным OLE (OLE for Databases — OLE для баз данных). OLE DB привязывал разработчиков баз данных к модели компонентных объектов Microsoft (Component Object Model — COM). Итак, OLE DB — это набор поддержи модель доступа к данным "потребитель/поставщик" на корпоративном уров не. Для OLE DB безразличен источник данных — будь то настольная база данных Access), база данных масштаба предприятия (например, SQL Server или Oracle) или же и вовсе не являющийся базой данных (например, электрон ная таблица Excel). Если разработчику было нужно свои данные через OLE DB, он должен был написать поставщик данных OLE DB. К сожалению, созда ние поставщиков OLE DB оказалось чрезвычайно сложным заданием для большинст ва разработчиков (разве что за исключением наиболее талантливых Выпускать потребители данных OLE DB было проще, однако про граммистам пришлось использовать Visual заплатив за это утратой свободы вы бора языка программирования.

Одной из последних разработок Microsoft стала технология ActiveX Data Objects (ADO), для OLE DB. Интерфейс ADO был разрабо исключительно с целью упростить работу с OLE DB. Вскоре после выхода ADO разразилась Разработчикам понадобился легкий в использовании API для того, чтобы сделать свои Web-узлы и ориентированными на взаимодействие с базами данных. Так как код ADO легко встраивался в Web страницы, новая технология отлично интегрировалась с информационным сервером Internet (Internet Information Server — IIS) и (Active Server Page — ак тивная серверная страница). Вскоре ADO стал стандартом де-факто для Web-узлов Internet. Действительно, небольшим Web-узлам ADO подходил как нельзя лучше;

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

Листинг Классический доступ к базе данных с использованием ADO Option Explicit ' Создание объекта Connection.

Dim conn as Object Set conn = Dim sConn as String = ' ' Создание объекта Command.

Dim as Object Set cmd conn Причины возникновения и обзор - "SELECT * FROM ' Выполнение к данных - получение ' объекта Recordset.

Dim as Object Set гэ Работа с объектом Recordset.

Do While not Dim sRecord as String Dim field as Object For Each field in sRecord = sRecord & fi & Next sRecord Loop Соединение после всех ' с В большинстве случаев соединение с базой данных оставалось открытым на про тяжении всего времени манипулирования с объектом Recordset. Это происходило даже при извлечении больших массивов информации. Кроме того, для обеспечения парал лелизма в ADO было принято блокировать записи или страницы. В результате базы данных испытывали огромную нагрузку, свободные соединения быстро заканчи вались, особенно для крупных Web-приложений. Проблема нехватки соединений ре шалась путем перевода объекта Recordset в отсоединенный режим. Несмотря на то что это было очень удачное решение, зачастую воспользоваться им могли только наиболее опытные разработчики. Повторно связать объект Recordset с соединением и согласо вать его содержимое с содержимым базы данных было чрезвычайно сложно.

Относительно недавно компания Microsoft представила всему миру платформу Одной из наиболее важных частей этой платформы является библиотека спросите, почему? Microsoft позволяет со старыми системами посредством уровня взаимодействия с СОМ. В листинге 1. приведено простое демонстрирующее пример использования ADO через уровень взаимодействия с СОМ.

Листинг 1.2. Использование ADO в управляемом коде using using // Установка соединения с базой данных.

Connection conn = new + // Выполнение запроса к базе данных.

Command cmd = new = conn;

= "SELECT * FROM object recaffected = null;

object = new 20 Основы ADO.

_Recordset rs ref // Вывод всех записей на while { for (int x - 0;

x < x++) ( Fields [x] ;

I // rs ;

} // соединения.

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

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

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

Поначалу разработчики попытались использовать старые методы доступа к дан ным, открывая соединения и их в кэш, на то время, пока пользователь не закончит свою работу. К сожалению, Web-приложения не поддерживали такую воз можность. Так, закрытие Web-обозревателя не приводило к уведомлению о том, что пользователь завершил работу. Единственным способом закрыть подобное соединение было его уничтожение после исчерпания лимита времени удержания в кэш данных. К счастью, этот метод оказался совсем не плох, и все были довольны, Однако затем, когда число к среднестатистическому Web-узлу увеличилось с 1000 до 100000 или даже до миллиона раз в день, серверы баз данных резко замед лили свою работу, если не полностью вышли из строя. Подобно обиженному ребенку, они начинали отвергать все попытки установки соединения.

Протокол передачи гипертекстовых файлов (Hypertext Transfer Protocol — HTTP), на который была сделана ставка при разработке Web-клиентов, преподнес неприят ный сюрприз — оказывается, старые методы доступа к данным были неэффективны в долгосрочном периоде. Протокол HTTP не имеет состояния, поэтому возникла не обходимость в разработке метода доступа к данным, который бы копировал такое по ведение. Поначалу была сделана попытка придумать механизмы, уменьшающие потребность в установке соединений с базой и блокировке страниц;

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

ADO.NET позволяет продолжать быть "привязанным" к базе данных. В листинге 1. приведен фрагмент имитирующей логику рассмотренных ранее в этой главе примеров с использованием ADO, Листинг 1.3. Пример в стиле ADO // Установка соединения с данных.

conn = new + + ;

// Выполнение запроса к базе данных.

new = conn;

= "SELECT * FROM CUSTOMER";

// Выполнение команды для создания объекта DataReader.

OleDbDataReader reader — // Вывод всех записей на while for {int x - 0;

x < { I Вывод пустой строки.

// Закрытие соединения с О В этой форме ADO.NET не отличается от своих предшественников. Ключевой момент заключается в том, что ADO.NET сам по себе не решает проблем масштабируемости. Пре. образовав старый ADO-код в код вы ничуть не улучшите его. Важно понять, что ADO.NET изначально был разработан для работы в отсоединенном режиме доступа к данным. Несмотря на то что в нем старый способ с базой данных в подсоединенном режиме, эта парадигма программирования не поощряется. Дру гими словами, ADO.NET изначально настраивает нас на работу в "отсоединенном" мире.

ADO.NET позволяет сократить "время жизни" соединения, как показано в лис тинге 1.4.

1.4. Сокращение "времени жизни" соединения в using System;

using using // Создание OleDbConnection conn new + 22 /.

// Установка соединения с базой (J // Создание объектов DataSet и Command.

= new ( "SELECT FROM CUSTOMER", // команды.

int count = // Закрытие () Краткий курс NET — это библиотека которые позволяют подсоединяться к данным и манипулировать ими. Несмотря на то что большинство примеров в этой книге написаны на языке С#, общеязыковая среда (Common Language Runtime — CLR) позволяет писать практически аналогичный код на Visual Basic C++ с управляемыми расширениями или на любом другом управляемом Пространства имен ADO.NET С целью разграничения функциональности классов ADO.NET они рассредоточены по различным пространствам имен. На рис. 1.1 показаны взаимоотношения этих про странств имен и содержащихся в них классов.

В ADO.NET пространства имен используются для отделения различных частей мо дели управляемого поставщика Изображенное на рис. пространство имен System. Data включает в себя общие структуры данных, не зависящие от конкретного поставщика. В него входит класс DataSet и целое связанных с ним классов (DataTabLe, DataRow, DataRelation, Constraint и т.д.). Внутри про странства имен System. Data содержится пространство имен Data.

в которое входят базовые для управляемых поставщиков данных классы. Эти классы определяют стандарт, которому должен соответствовать управляемый поставщик данных ADO.NET. Другие управляемые поставщики разрабатываются путем создания набора эти интерфейсы. Пространство имен YourProvider (дословный "ваш ред.), изображенное на рис. 1.1, является примером пространства имен пользовательского управляемого поставщика данных.

Структуры данных ADO.NET ADO.NET поддерживает три способа непосредственного доступа к хранящейся в базе данных через объекты Command, DataReader и DataSet.

Классы команд для управляемого поставщика SQL и OleDbCommand для управляемого поставщика OLE DB) используются для получения результатов выполнения запросов к базе данных. Класс команды всегда реализует интерфейс Изначально компания Microsoft создала компиляторы для Visual Basic, и C++.

Кроме этого, сторонние выпустили компиляторы для таких языков, как Perl, Python, Scheme, Smalltalk, COBOL, Component Pascal, APL, Standard ML, Mercury и Глава Г Причины возникновения и краткий обзор ADO.NET методы которого могут использоваться для получения скалярного ре зультата (значения первого столбца первой строки результирующего набора данных) (метод или выходных параметров хранимой процеду ры (метод ).

Пространство имен DataSet Data Relation • Data Table Др.

Пространство имен IDbCommand ' • ••' '-• Пространство имен имен Пространство имен SqICIient System.

SqlConnection SqIDataReader Data Reader др. ДР.

Рис. 1.1. между пространствами имен По своим функциональным объект DataReader (классы SqIDataReader и OleDbDataReader) похож на объект ADO Recordset. Поскольку при чтении данных используется режим однонаправленного курсора, в ADO нужно быть очень постоянно отслеживая вызовы метода и индикатор конца записи. Механизм чтения данных объекта является более устойчи вым к ошибкам, за один шаг считать текущую запись в память и проверить наличие индикатора конца записи. Наиболее эффективно объект DataReader приме няется при получении результатов выполнения запроса и их передаче на дальнейшую обработку. Стоит что компания Microsoft использует объект DataReader для заполнения объекта DataSet через объект в своих управляемых ках Пример использования объекта DataReader приведен в листинге 1.3.

Вне всякого сомнения, "центром вселенной" является новая структура — объект DataSet, на первый взгляд, объект в OLE DB или объект Recordset в ADO. Тем не менее DataSet — это гораздо более сложная 24 Часть I. Основы структура данных, позволяющая представлять в памяти почти любую модель. Объект DataSet содержит коллекцию таблиц, с которыми могут быть связаны экземпляры классов Constraint или даже ForeignKeyConstraint.

Указанные взаимоотношения представлены на рис. 1.2.

DataSet Таблицы DataTable DataRelation Constraint ForeignKeyConstraint Рис. 1.2. Структура объекта AD Для обычного разработчика это означает, го класс DataSet предоставляет средст ва моделирования реляционных данных в памяти. И если в целом предпо лагает работу с данными в отсоединенном режиме, объект DataSet позволяет "скрыть* этот факт и обращаться к информации так, как будто бы она находится в базе дан ных. Листинг 1.5 является, по сути, новой версией листинга 1.4, на этот раз с исполь зованием объекта DataSet.

Листинг 1.5. Использование объекта DataSet using System;

using using // Создание объекта Connection.

conn = new + + + // Установка соединения с базой // Создание DataSet and Command.

DataSet ds = new daAuthors = new "SELECT * FROM CUSTOMER", // Заполнение объекта DataSet.

1. Причины возникновения и краткий обзор // мы можем () // Извлечение таблицы из объекта DataSet.

DataTable = // строк row in Вывод на консоль значения всех полей заданной foreach (Object in I } // Вывод пустой строки.

} Этот код не сложнее того, в котором использовался объект В нем приме няется объект DataAdapter (в данном экземпляр класса OleDbDataAdapter) и, в первый раз, объект Объект DataAdapter представляет собой специальный тип составной команды, со четыре отдельных объекта Command — команды выборки, вставки, обнов ления и удаления данных. Несмотря на то что сейчас это выглядит совершенно не важным, вы оцените всю мощь объекта DataAdapter при рассмотрении механизма об новления с помощью объекта DataSet хранящейся в базе данных информации.

Возможно, вы думаете: "Использование объекта DataSet для обращения к данным в памяти пригодится только при чтении данных, но как же быть, если необходимо их изменить или Не беспокойтесь, объект DataSet предоставляет ность извлечения подмножества данных, которые были изменены, удалены или до бавлены, что позволяет проверить их достоверность перед тем, как отразить измене ния непосредственно в базе данных. Обычное использование объекта DataSet преду сматривает добавление, изменение и удаление записей. Поскольку мы уже создали объект DataAdapter, нам остается связать с ним объект и вызвать ме тод DataAdapter () для обновления, добавления или удаления записей. Со ответствующие операции выполняются с помощью команд, заданных свойствами объ екта DataAdapter и (листинг 1.6).

Листинг Обновление базы данных с помощью объекта DataSet using using // Создание объекта Connection.

conn = new + + + // Создание объектов DataSet и DataSet ds = new OleDbDataAdapter = new * FROM CUSTOMER", // Создание объекта 26 I. Основы // оболочки объекта DataAdapter, поддерживающей динамическое команд // добавления и удаления OleDbCommandBuilder = new // Заполнение объекта DataSet.

// Извлечение таблицы из объекта DataSet.

= // Установка первичного colArr = new = colArr;

// Вставка новой строки.

new * "Greg";

"Maddux";

DataRow // Удаление // Изменение полей в = "New Name";

// Сохранение в базе conn. Open () Отметим несколько важных моментов. Во-первых, мы нигде не устанавливали значение свойства или этого был создан экземпляр класса OleDbCommandBuilder. При создании объекта конструктору передается объект DataAdapter. Объект регистрирует себя в качестве "слушателя" объекта DataAdapter и, при необходимости, на лету создает команды обновления, удаления или вставки данных. К тому же мы ус тановили значение свойства PrimaryKey для чтобы объект CommandBuilder знал правила создания объектов Command. Обратите внимание: после выполнения запроса к базе данных (в методе Fill) соединение было закрыто, затем мы заново открыли его непосредственно перед выполнением метода Объектная модель управляемого поставщика ADO.NET В первоначальном выпуске ADO.NET содержатся два управляемых поставщика данных: OLE DB и SQL Server. Управляемый поставщик SQL Server позволяет под ключаться к базе данных Microsoft SQL Server, для этого ее "родные" меха низмы. Для доступа к базам данных абстрактного уровня OLE DB в ADO.NET предусмотрен управляемый поставщик OLE DB. Технология OLE DB позволяет подключаться к огромному числу различных хранилищ данных, таких, как Глава 1. Причины возникновения и ADO.NET SQL Server, Oracle, DB2, Access, dBase, FoxPro, и даже к текстовым файлам с симво лами-разделителями.

Управляемый поставщик OLE DB препятствует использованию поставщика OLE DB для ODBC. В связи с этим компания Microsoft выпустила отдельный управляемый поставщик ODBC, позволяющий получать доступ к любым источникам данных, для которых предусмотрен драйвер ODBC. Принятие решения об использовании сущест вующего OLE DB или драйвера ODBC зависит от конкретной базы дан ных. В некоторых случаях быстрее работает OLE DB, а в некоторых — ODBC.

Получить доступ к базе данных SQL Server можно как с помощью управляемого поставщика SQL Server, так и с помощью управляемого поставщика OLE DB. Возни кает вполне законный вопрос: "А какой из них После того как я продемон стрирую вам отличия этих вы сами сможете дать на него ответ. Ниже приведены два примера выполнения одной и той же операции: в листинге 1.7 исполь зуется управляемый поставщик OLE DB, а в листинге 1.8 — управляемый поставщик SQL Server.

Листинг 1.7. Доступ к базе данных SQL Server с помощью управляемого поставщика // Управляемый поставщик OLE DB.

using System;

using // Создание объекта Connection.

conn = new + + + // Создание объектов DataSet и Command.

DataSet ds = new daAuthors = new "SELECT * FROM // Заполнение объекта DataSet.

// таблицы из DataSet.

DataTable // Обработка всех строк таблицы.

row in { // Вывод на консоль значений всех полей заданной foreach in { } // пустой строки.

28 Часть Листинг Доступ к базе SQL Server с помощью поставщика // SQL using System;

using // Создание Connection.

conn = new * + + // Создание объектов DataSet и Command.

DataSet = new = new "SELECT * FROM CUSTOMER", // Заполнение объекта DataSet.

// Извлечение таблицы из объекта DataSet.

DataTable // Обработка всех строк ( DataRow row in { // на консоль значений всех полей заданной foreach (Object in { I // Вывод пустой строки.

} Единственное изменение, сделанное нами в SQL-версии кода помимо переимено вания классов, заключается в том, что нам не нужно определять данных в строке соединения. Синтаксис строк соединения OLE напоминает таксис строк соединения SQL Server, хотя они не полностью идентичны. Как вы ви дите, необходимые для замены одного данных другим, в целом несущественны. Поскольку мир большая часть кода не будет зависеть от используемого Общее правило гласит;

управляемый SQL Server, если вам точно известно, что в качестве хранилища данных всегда будет использоваться SQL Server. Теоретически управляемый поставщик SQL Server должен работать лучше. Для доступа к базе данных он использует не OLE DB, а протокол TDS (Tabular Data Stream — поток табличных данных), улучшить производительность.

Тем не менее мои собственные тесты показали, что производительность двух управляемых поставщиков данных практически одинакова. Таким образом, настоящая причина, по которой я советую использовать поставщик данных SQL Server, заключа ется в предпочтении иметь функциональность, только для SQL Server (например, получение результатов запросов в формате XML), или же работать с типа ми данных SQL Server вместо типов OLE DB. Как мы увидим в главе 9, управляемый поставщик SQL Server предоставляет набор классов, предназначенных для Глава Причины возникновения и краткий обзор процесса получения результатов запросов в формате XML для их последующей обра ботки с помощью В классы управляемых поставщиков взаимодействуют с хранилищем данных посредством базовых методов установки соединения и выполнения команд.

На рис. 1.3 показана взаимосвязь между классами соединения, команды и параметров.

Создавая объекты Connection, Command и Parameter, управляемые поставщики позво ляют подключаться к базе данных и производить с ней какие-либо действия.

Data SqlDataAdapter DataSet lection I Error Рис. 1.З. Взаимоотношения между поставщика SqICIient Классы располагаются в пространстве имен Data. В пространстве имен System. содержатся базовые классы управляемых поставщиков, которые нельзя создавать;

как правило, это абстрактные классы, определяющие базовую функциональность управляемых поставщиков, следо вательно вы никогда не будете использовать данные классы напрямую. Каждый управляемый поставщик имеет свое собственное пространство имен. Так, управляе мый поставщик SQL Server находится в пространстве имен управляемый поставщик OLE DB — в пространстве имен управляемый поставщик Microsoft в пространстве имен OracleClient и, наконец, управляемый поставщик в пространстве имен Я не если к моменту выхода этой книги компании Sybase и IBM также напишут свои управляемые поставщики данных.

30 Часть \. Основы Глава XML В этой главе...

и XML Класс Data Существуют мнения относительно важности XML в разработке про граммного обеспечения. Компания Microsoft разработала собственную стратегию направленную на использование и Web-служб в качестве "клея", обеспе такое взаимодействие между компаниями, которое не зависит от исполь в них операционных систем и механизмов хранения данных. Поскольку это данные, a механизм работы с данными в тот факт, что поддерживает работу с XML-документами наравне с остальными дан ными, выглядит вполне естественно. В этом аспекте библиотека NET является интегрированной с инфраструктурой XML XML Microsoft в отношении XML необходимо с двух сторон: с одной стороны, Microsoft стремится предоставить те же инструменты для дос тупа к которые используются для доступа к информации, храня щейся в базе данных, с другой — обеспечить в средства преобразования ин формации базы данных в формат XML. Кстати сказать, название ADO.NET не совсем корректное, так как ADO.NET не является прямым "наследником" ADO. Поскольку ADO.NET — всего лишь механизм работы с данными в то не кажется ли вам, что с этой точки зрения представляет просто еще один формат данных?

Во времена ADO/OLE DB для обработки информации из некоего источника дан ных (например, базы данных) необходимо было создать поставщик OLE DB, который довольно сложен и запутан. В для доступа к информации из источника, отлич ного от базы данных, можно либо создать управляемый либо воспользо ваться преимуществами тесной интеграции с XML.

Класс DataSet и XML Класс DataSet имеет встроенную поддержку XML. Поскольку с данными и интеграция с XML являются одинаково важными аспектами нальности класса DataSet, становится ясно, что они были заложены в него еше на этапе проектирования.

Преобразование данных объекта DataSet в формат XML Получение XML-представления объекта это довольно простая Единственным нетривиальным моментом здесь является "тонкая" настройка 212 Часть использование ADO.NET Часть Практическое использование ADO.NET Глава 9.

Глава 10. Привязка данных Глава Масштабируемость и производительность приложений ADO.NET // для объекта DataSet.

// Определение переменной для упрощения к таблице.

DataTable // Добавление сведений о новом товаре.

DataRow = "Home Base "Smith's Hardware";

= 12.54;

15.00;

try :

// данных.

Updated the catch ( Обратите внимание на добавление в команду INSERT дополнительного оператора SELECT, который используется для извлечения из базы данных значения последнего созданного идентификатора и его сохранения в параметре @ProductID. Поскольку в результате выполнения команды значение параметра будет изменено, его необходимо определить как выходной параметр. этому после вставки строки она будет иметь корректное значение идентификационного столбца. Несмотря на то что иден тификационный столбец можно определить как автоинкрементный в объекте DataTable (см. главу 5, "Создание объекта DataSet"), вы можете воспользоваться при веденным выше примером для перепоручения этой функции базе данных.

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

Резюме на то что доступ к данным в отсоединенном режиме предоставляет ог ромные за него также следует платить. Первой задачей, с не обходимо справиться разработчику, является реализация параллелизма при доступе к отсоединенным данным. При этом самое сложное решение, как правило, заключается в выборе оптимальной модели параллелизма для конкретного проекта или прило жения. Кроме того, в этой главе мы познакомились с "обратной стороной медали" Часть II. Класс DataSet от хода выполнения она либо фиксируется, либо отменяется. Следует от метить, что самая важная часть приведенного выше кода заключается именно в при сваивании транзакции свойству (на пер вый взгляд, это может оказаться совершенно неочевидным). Дело в том, что когда объект создает команды INSERT, UPDATE и DELETE, он копирует все настройки команды Получение идентификатора новой строки от базы данных SQL Server Бывают в которых создание идентификатора новой строки в таблице на базу данных. С этой целью база данных SQL Server поддерживает поле IDENTITY и серверную переменную значение последнего соз данного идентификатора. Для того чтобы этой функциональностью в необходимо выполнить следующие действия (листинг Листинг 8.17. Получение идентификатора новой строки от базы данных SQL Server // Создание объекта DataAdapter.

SqlDataAdapter dataAdapter new FROM Создание команды insQry INTO insQry Cost, Price) += ( @Description, insQry insQry insQry SELECT 0 ;

insCmd. * // Определение переменной для упрощения // доступа к коллекции insParams = // Определение параметров.

, SqlDbType.NVarChar, 255, // Установка значения свойства InsertCommand // объекта DataAdapter.

insCmd;

// Создание объекта DataSet.

DataSet dataSet new Использование объекта DataAdapter Глава В. Обновление базы данных DataRow delRow = // Создание для генерирования // команд изменения и удаления данных.

custBldr = new » new // Обновление базы данных.

SqlTransaction tx try ( Для того начать транзакцию/ // открыть {) /' // для SELECT.

// // на tx • invDA, tx;

tx;

// Удаление информации базы данных.

// Используем обратный порядок - сначала // обновляется дочерняя таблица.

// Добавление и изменение в базе // Используем прямой порядок - сначала // обновляется родительская I I // На этапе } catch ex) I // при if (tx null) finally I t } I Для того чтобы провести все операции обновления в рамках транзакции, мы соз даем локальную транзакцию соединения, после чего присваиваем ее свойству DataAdapter. Selectcommand. Transaction каждого объекта DataAdapter. В зависимости 208 Часть Класс DataSet Как показано в приведенном выше фрагменте при выполнении операции удаления информации из базы данных используется обратный порядок обновления содержимого таблиц, а при выполнении операций добавления и изменения — прямой порядок. Это можно реализовать, методу Update объект DataTable, содержа щий только определенные (удаленные, или измененные) строки. По скольку метаданные уровня столбцов для полученных в результате вызова метода Getchanges объектов DataTable первоначальным таблицам, объект будет работать как ни в чем не бывало.

Использование локальных транзакций для обновления базы данных Что произойдет, если при выполнении кода из предыдущего примера случится ошибка? Останется ли база данных в непротиворечивом состоянии? Да, если для свя зывания операций обновления будут использованы локальные транзакции. Локальные транзакции позволяют провести отмену внесенных в базу данных изменений в случае возникновения ошибки на стороне клиента. Пример использования транзакций при веден в листинге Листинг 8.16. Использование транзакций для обновления базы данных // Определение переменной для упрощения к DataTable = DataTable invTable // Определение отношения между = DataColumn = invCustlDColumn, // Добавление сведений о новом DataRow = = "10 Aaron Way";

= "Atlanta";

= "30307";

= // Изменение сведений о клиенте.

DataRow oldRow = "53 Peachtree Center";

= "30342";

// Изменение информации о DataRow ("rel") = "Net 10th";

// Удаление клиента.

8. базы данных catch i } Легко заметить, что приведенный выше код напоминает код обновления базы дан ных на основе объекта с одной Разница заключается в том, что для таблицы создается собственный объект и Порядок вызова метода DataAdapter. Update очень важен. Как видите, сначала мы обновляем строки в дочерней таблице. Это связано с тем, что проводится удаление клиента, а определенное отношение поддерживает каскадные удаления.

Следует отметить, что если каскадные удаления поддерживаются как базой данных, так и объектом DataSet, то при обновлении базы данных могут возникнуть связанные с тем, что объект DataSet попытается удалить дочерние которые уже были удалены базой данных при удалении родительских строк. Обновление "в обратном порядке" (начиная с родительской таблицы) позволяет обойти эту пробле му, однако вызывает дополнительные трудности при добавлении новых родительских и дочерних строк. Дело в том, что если сначала будет обновляться дочерняя имеющая ограничения по внешнему ключу, то добавление новых дочерних строк бу дет требовать наличия строк в родительской таблице. Выходом из этой ситуации является отдельное обновление, вставка и удаление информации из ба зы данных. В листинге 8.15 показан расширенный код, корректное выполнение операции добавления в базу данных.

Листинг Добавление информации в базу данных при использовании объекта из нескольких таблиц // Создание CommandBuilder для генерирования // команд удаления, изменения и данных.

- new invBldr - new // Обновление try I // Удаление // порядок - сначала // обновляется дочерняя // и информации в // прямой порядок - сначала // обновляется родительская [ I i catch (SqlException ex) ( 206 II, Класс DataSet invDA * FROM // DataSet.

DataSet dataSet - new // объекта DataAdapter // для заполнения объекта DataSet.

// Определение переменной для упрощения доступа к DataTable DataTable invTable // Определение отношения.

DataColumn // Добавление сведений о новом DataRow "Mike";

- "10 Aaron - "30307";

= "(404) // сведений о DataRow oldRow [0] = "53 Peachtree Center";

- "30342";

// Изменение информации о DataRow = = "Net // Удаление DataRow delRow // для генерирования // команд изменения и // Обновление базы try // Сначала обновляется дочерняя потому что осуществляем удаление Глава 8. Обновление базы данных = "Atlanta";

= "30307";

"(404) 543-8765";

// Удаление клиента.

// Изменение сведений о клиенте.

DataRow - "Andrew";

try :

// Обновление Сазы Updated the } catch (SqlException ex) I \ Этот пример практически идентичен примеру улучшенной реализа ции оптимистического параллелизма. Однако не используется поле позво удостовериться в том, что строка не с момента ее извлечения из базы данных. Обновление и удаление строк всегда происходит без учета возможности возникновения исключений.

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

Работа с объектами DataSet, состоящими из нескольких таблиц В большинстве случаев вы будете иметь дело с объектами нескольких таблиц. Чем же отличается обновление базы данных на основе объекта DataSet, одну таблицу, от обновления базы данных на основе объекта DataSet, содержащего несколько таблиц? Как показано в листинге 8.14, основное раз личие заключается в том, что каждой таблице объекта DataSet должен соответствовать собственный объект DataAdapter.

Листинг 8.14. Удаление информации из базы данных при использовании состоящего из нескольких таблиц // Создание объектов DataAdapter.

SqlDataAdapter = * FROM 204 Часть //. Класс DataSet // Определение переменной для упрощения // доступа к коллекции = // Определение параметров.

50, true;

50, true;

= true;

= true;

SqlDbType.NVarChar, 50, true;

2, - true;

10, = true;

= true;

true;

- true;

8, true;

// значения свойства // объекта DataAdapter.

= Создание объекта DataSet dataSet = new // Использование объекта DataAdapter для // заполнения объекта DataSet.

// Определение переменной для упрощения доступа к DataTable custTable = // Добавление сведений о новом DataRow = = "Mike";

"10 Aaron Way";

Глава 8. Обновление базы данных 50, "City") true;

2, = true;

= = true;

= true;

0, true;

insParams Float, 8, true;

// Установка значения // объекта DataAdapter.

= // Определение команды DELETE.

string = = FROM CUSTOMER WHERE * = = // Определение переменной // доступа к коллекции // Определение параметров, // значения свойства // объекта DataAdapter, = delCmd;

Определение команды UPDATE.

string updQry = += "UPDATE CUSTOMER SET updQry += CustomerlD = updQry += = updQry += = updQry += = updQry Address = @Address, updQry += Apartment = updQry City updQry += State Zip = updQry += HomePhone = updQry += BusinessPhone = updQry += DOB = updQry Discount = updQry "WHERE CustomerlD = SqlCommand conn. CreateCommand ();

updQry;

202 Часть II. Класс Следует отметить, что в том случае, когда необходимо реализовать отсоединенный пессимистический параллелизм, рекомендуется использовать шаблон для ре дактирования/вернуть после окончания редактирования".

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

Листинг 8.13. Реализация деструктивного параллелизма // DataAdapter.

SqlDataAdapter new * FROM CUSTOMER", // Создание команды INSERT.

string = INTO FirstName, MiddleName, City, State, Zip, Discount) VALUES ( @Apartment, @City, @Discount = insQry;

// Определение переменной для упрощения // доступа к коллекции SqlParameterCollection insParams = // Определение параметров.

50, 50, true;

= true;

50, = true;

true;

Глава 8. базы данных // Определение для // доступа к коллекции параметров.

= Определение параметров, 0, 50, 50, = true;

50, true;

= true;

= true;

50, = true;

2, = true;

10, true;

- true;

0, true;

= true;

// Установка значения свойства // объекта DataAdapter.

= updCmd;

// Обновление базы try • updating the catch (SqlException ex) // объекта DataSet.

;

200 Часть II. Класс DataSet /* На этом */ COMMIT // Уточнение запроса - указание // идентификационного номера клиента.

= // Создание DataAdapter.

SqlDataAdapter dataAdapter = new // Создание объекта DataSet.

DataSet dataSet = new // Использование объекта DataAdapter // заполнения объекта DataSet.

try catch (SqlException ex) I return;

// Определение переменной для упрощения доступа к DataTable custTable // Обновление сведений о DataRow = customer "55 Peachtree Center";

"30312";

// Создание команды идеале здесь нужно было бы использовать хранимую процедуру.

string updQry;

= CUSTOMER SET = = @FirstName/ = @LastName, Address = Apartment = City = State @State, Zip = DOB = Discount = = WHERE Stamp AND CustomerlD = AND CheckedOut = 1";

= updQry;

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

После внесения изменений запись клиента нужно возвратить обратно в базу ных. При этом она будет обновлена, а флаг "извлечена для редактирования" должен быть сброшен (установлен в ноль или Обратная операция не требует исполь зования транзакции, поскольку она атомарна (все изменения базы данных выполня ются в пределах одного В завершение необходимо сбросить объект DataSet, для того чтобы разрешить доступ к записи клиента. Пример реализации пес симистического параллелизма приведен в листинге Листинг 8.12. Реализация пессимистического параллелизма // Код для из базы данных записи // (в идеале здесь было бы // хранимую собой // идентификационный номер getSQL;

Начало транзакции. */ BEGIN DECLARE SELECT @CustID = /* Проверим, ли клиента извлеченной для */ = FROM Customer WHERE = IF already checked ROLLBACK RETURN END /* Отметить как для */ UPDATE Customer SET WHERE /* Извлечение записи клиента */ SELECT CustomerlD, City, DOB, Stamp FROM Customer WHERE CustomerlD @CustID 198 Часть Класс DataSet Row:

else t Row:

// Сброс свойства else // сообщения остальных типах // нарушения Violation:

Row:

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

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

Реализация пессимистического параллелизма Пессимистический параллелизм — это просто блокировка строк. Если из базы данных была извлечена строка, она блокируется и никто другой не может получить к ней доступ. Пессимистический параллелизм идеально согласуется с моделью работы с данными типа "извлечь для редактирования/вернуть после окончания редактирова ния". Для того чтобы реализовать этот тип параллелизма, необходимо иметь возмож ность ограничить доступ к строкам базы данных, пока они находятся в состоянии "извлечены для редактирования". Так как база данных не в состоянии блокировать строки в отсоединенном режиме, нам придется придумать свое собственное решение.

Предположим, что нам необходимо создать SQL-код, который будет извлекать из данных для редактирования запись определенного клиента. Следует отметить, что большая часть "магии" пессимистического параллелизма заключена в SQL-операторах (в идеальном случае — в хранимых Необходимо убедиться, что запись клиента не извлечена из базы данных, и, если все в порядке, отметить ее как из 8. базы данных + + • + // updateSQL •, - OR IS AMD // Корректировка строк и их if - 2) - 4) == "AND { - 2);

whereSQL = // Добавление параметра 0, // Создание SQL-оператора.

= updateSQL + whereSQL;

// значения свойства объекта DataAdapter.

Создание состоящего из одной // и выполнение обновления данных // с помощью объекта DataAdapter.

= try Updated! + Row Level Collision catch { Row:

} catch (SqlException { Часть II. Класс • Предложить пользователю перезаписать данные. К сожалению, это не всегда возможно и не всегда приемлемо.

• Если позволяют можно допустить коллизию на уровне Это означает, что вам необходимо проанализировать текущую запись в базе данных и проверить на предмет изменения каждый из ее столбцов. Если столбец не был изменен, его можно обновить. Как при этом возникает проблема, связанная с необходимостью убедиться в что одновременно были обновле ны сразу несколько столбцов (например, City, State и zip).

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

Листинг 8.11. Обработка нарушений допускающая коллизию на уровне строк // Поиск нарушений if { (DataRow row in ) // Мы можем только ошибки обновления if t // Создание новой команды // Новая команда обновления будет // использоваться для устранения ошибок.

// Сохраняем старую команду обновления // (если она существовала).

SqlCommand = null;

if null) { oldUpdateCmd = Создание новой команды.

= // Анализ измененных строк и // нового string = "UPDATE CUSTOMER SET string "WHERE = for (int x = 0;

x < ++x) { if { // col + + = 8. Обновление базы данных // Создание объекта для // команд вставки, обновления и удаления данных.

= new try ( I catch ax) // о нарушении if HasErrors) foreach row in !

Violation Row:

I В приведенном примере имитируется внесение изменений в базу данных с нарушения параллелизма (изменения вносятся до фактического обновления объекта Data Table), В результате этого сразу же после попытки обновления таблицы Customers будет сгенерировано исключение DBConcurrencyException. После такой неудачной попытки обновления таблицы мы можем проанализировать ее строки и выявить те из них, которые привели к нарушению параллелизма. В данном примере мы не стараемся исправить ситуацию, а просто сообшаем о ней пользователю. По скольку объект DataTable содержит информацию об ошибках, ее можно использовать для фактического решения проблемы, т.е. для того чтобы заставить объект DataAdapter снова обновить объект DataTable.

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

Такое поведение объекта может оказаться не тем, которое требуется в конкретной ситуации. Если установить значение свойства в true (по умолчанию используется значение false), то объект DataAdapter попыта ется обновить все строки и отметит те из них, которые привели к нарушению парал лелизма. В этом случае после завершения обновления таблицы вам придется вручную проверить свойство на наличие информации об ошибках. Ес ли таковые имелись, то в свойстве каждой "сбойной" строки будет содер жаться информация об ошибке. Если же ошибок не то есть основание считать, что обновление базы данных прошло успешно.

Способ обработки нарушений параллелизма зависит от типа создаваемого проекта.

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

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

194 Часть Класс catch ex) { // Если не удалось, // об этом ;

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

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

В процессе обновления базы данных объект подсчитывает количество добавленных, измененных или удаленных строк. Если результат выполнения хотя бы одной из указанных операций равен нулю (обычно этого не происходит из-за того, что не нашлось строки, соответствующей предложению WHERE, которая была измене на после своего извлечения из базы данных), объект DataAdapter генерирует исключе ние Как правило, это свидетельствует о нарушении па раллелизма. В следующем примере (листинг 8.10) мы создадим нарушение паралле лизма и научимся его обрабатывать.

Листинг 8.10. Обработка // Определение переменной для упрощения доступа к DataTable custTable = // Внесение изменений в о = = // Имитация внесения в = = "UPDATE Customer SET Zip + WHERE // Определение параметров (идентификационного номера клиента и // команды = + 1, // Внесение в Глава 8. базы данных = true;

updParams. IsNullable = true;

= true;

, = true;

. IsNullable = true;

= // Установка значения свойства UpdateCommand // объекта DataAdapter.

= // Создание объекта DataSet DataSet dataSet = new // Использование объекта DataAdapter // для заполнения объекта DataSet.

// Определение переменной для упрощения доступа к DataTable custTable = // Добавление о новом клиенте.

DataRow = = = "Mike";

newRow ] = "10 Aaron = "30307";

= // Удаление клиента.

DataRow delRow // Изменение информации о DataRow oldRow = "Andrew";

try // Обновление базы данных, Updated the Часть //. Класс DataSet // Определение = // значения // объекта // Создание команды UPDATE.

string updQry = - CUSTOMER SET CustomerlD = FirstName = LastName = Address @Address, Apartment City = State - Zip = HomePhone = BusinessPhone = DOB = Discount - @Discount WHERE Stamp = AND CustomerlD = SELECT = Stamp FROM CUSTOMER WHERE CustomerlD = = updQry;

// Определение переменной для упрощения // доступа к коллекции updparams = // Определение параметров.

= true;

50, = true;

= true;

true;

2, true;

Глава 8. Обновление базы данных = = // Создание переменной для упрощения // доступа к коллекции = // Определение параметров.

50, 50, = true;

= true;

= true;

Add 50, = true;

true;

true;

10, true;

= true;

= true;

true;

= true;

// Установка свойства // DataAdapter.

= insCmd;

// Создание команды DELETE.

string delQry = FROM CUSTOMER WHERE CustomerlD AND Stamp = SqlCommand = delQry;

// Определение переменной для упрощения к коллекции SqlParameterCollection = delCmd. Parameters;

190 Часть II. Класс = true;

50, - true;

2, true;

10, true;

= true;

true;

Add SqlDbType. 0, = true;

= true;

= // Установка значения // DataAdapter.

= В свойствах параметра указано не только то, что необходимо использовать первоначальную версию строки, но и что этот параметр должен быть входным выходным InputOutput). Таким образом, параметр будет базой данных и его значение будет равно текущему значению столбца stamp. Необходимость иметь новое значение поля stamp обусловлена требованием иметь самую "свежую" метку времени создания/изменения строки при каждом вызове метода с целью обеспечения оптимистического параллелизма.

В листинге 8.9 приведено определение всех команд объекта DataAdapter.

Листинг 8.9. Определение всех команд объекта DataAdapter Создание и открытие SqlConnection conn = new + + "Integrated // Создание объекта DataAdapter.

SqlDataAdapter dataAdapter = new FROM // Создание команды string insQry = insQry = INTO MiddleName, Address, Apartment, City, Zip, BusinessPhone, DOB, Discount) VALUES ( @Apartment, Глава 8. Обновление базы данных Команда UPDATE Команда UPDATE, создаваемая объектом требует передачи перво начального и нового значения строки, это позволяет убедиться в том, что строка не изменилась с момента ее извлечения из базы данных. Для повышения эффективности команды л использовал метку времени создания/обновления строки. При обновлении строки необходимо получить новую метку времени создания/обновления, чтобы в следующий раз иметь самую новую версию последней. К счастью, позволя ет осуществить это за одно обращение к базе данных — производится обнов ление значения строки, после чего извлекается новое значение поля stamp, как пока зано в листинге 8.8.

Листинг 8.8. Определение значения свойства объекта // Создание команды UPDATE.

string updQry = = CUSTOMER SET = = LastName = = Address = Apartment = City State Zip = DOB Discount = WHERE Stamp = @Stamp AND CustomerlD SELECT Stamp FROM CUSTOMER WHERE CustomerlD = = updQry;

// Определение переменной для упрощения // доступа к коллекции Parameters;

// Определение параметров.

= true;

50, = true;

true;

188 Часть II. Класс Команда INSERT, созданная объектом практически удовлетворяет наши потребности. Код, в листинге 8.6, дублирует эту команду.

Один из интересных моментов приведенного выше кода заключается в использо вании методом имени поля, из которого необходи мо извлечь значение. Четвертый параметр этого метода, фактически определяет ото бражение параметров на столбцы таблицы базы данных. В течение выполнения мето да DataAdapter. Update {} созданная нами команда INSERT вызывается по одному разу для каждой измененной строки.

Команда DELETE Объект CommandBuilder создает сложную команду которая прежде чем удалить строку, проверяет, не изменилась ли она с момента ее последнего изменения (извлечения из базы данных). В рассматриваемом примере я использую для этой цели поле SQL Server Timestamp (большинство баз данных имеют что-то подобное этому полю). Поле предназначено для обновления метки времени созда ния/обновления строки при каждом ее (строки) изменении. Наличие этого поля по зволяет нам гарантировать отсутствие изменений строки с момента ее извлечения из базы данных. Поскольку мы используем поле Timestamp, а не проводим проверку значений столбцов команда DELETE будет принимать только два как показано в листинге 8.7.

Листинг 8.7. Определение значения свойства DeleteCommand объекта команды string delQry = FROM CUSTOMER WHERE = AND Stamp = = // Определение переменной для упрощения // доступа к коллекции = // Определение параметров.

0, "Stamp") // Установка значения свойства // DataAdapter.

= delCmd;

Обратите внимание на то, что свойство параметра stamp устанав ливается равным (первоначальная версия строки). Это означает, что даже если кто-то изменит поля Stamp, сравнение будет все равно происходить с использованием первоначального значения данного поля.

Глава 8. Обновление базы данных Листинг 8.6. Определение значения свойства InsertCommand объекта Создание команды INSERT.

string insQry INTO FirstName, LastName, MiddleName, Apartment, City, State, Zip, DOB, Discount) VALUES ( ©FirstName, ©LastName, ©HomePhone, ;

= // Создание переменной для упрощения // доступа к коллекции SqlParameterCollection Определение 50, = true;

= true;

= true;

= true;

= true;

2, = true;

10, = true;

true;

= true;

= true;

= true;

// Установка значения свойства объекта DataAdapter, insCmd;

186 Часть Класс В большинстве случаев при работе с серверами баз данных (такими, как SQL Server, Oracle, и т.д.) увеличения быстродействия можно достичь с помощью по мещения всех команд INSERT, DELETE и UPDATE в хранимые процедуры. Для облегче ния восприятия материала предложенные мной примеры будут написаны на "родном" коде SQL. При желании вы можете использовать их в качестве основы при создании хранимых процедур для сервера баз данных.

Создание команд для объекта DataAdapter Прежде чем создать собственные команды, необходимо понять, как команды объекта DataAdapter используют параметры при обновляемых данных. Напомним, что объект DataAdapter содержит команды (объекты Command) для выполнения опера ций вставки, обновления и информации из базы данных. Каждая из этих команд имеет набор параметров, столбцы базы данных. Объект DataAdapter использует параметры в своеобразной точки "стыковки" между объектом и базой данных. Ниже перечислены самые важные свойства классов параметров и • — имя столбца в таблице которому соответствует этот параметр.

• — версия строки DataRow, которой соответ ствует этот параметр. Более подробно версии строк рассматривались в главе 7, "Манипулирование объектом С помощью этого свойства можно ото бразить первоначальную версию столбца на один параметр, а модифицирован ную версию — на другой.

• это свойство уже нами в главе 3, "Выполнение команд". Здесь оно имеет особое значение, так как после выполнения команды значение выходного (Output) или входного-выходного put) парамет ра будет помещено в столбец SourceColumn строки DataRow.

Для создания собственных команд обновления необходимо провести их "тонкую" настройку. Первые два свойства параметра (SourceColumn и привя зывают его к определенному столбцу. Свойство SourceVersion предназначено для обозначения версии строки, с которой связать параметр. В общем случае рекомендуется связывать с версией DataRowVersion. Current параметры, предна значенные для фактического обновления информации в базе данных, а с версией — параметры, для использования в пред ложении WHERE с целью идентификации строки.

По умолчанию свойство Direction имеет значение Input, однако вам, потребуется изменить его, создавая параметр, использующийся для возвращения значения из базы данных (например, номера для вставляемой в таблицу строки). При желании вы настроить оператор INSERT так, чтобы столбец первичного ключа был выходным параметром, позволяя тем самым SQL оператору или хранимой процедуре его (параметра) значение встав ки строки в базу данных. Более подробно возвращение из базы данных идентифика ционного номера строки рассматривается в разделе "Получение идентификатора новой строки от базы данных SQL Server", далее в этой главе.

Команда Команда INSERT, созданная объектом является достаточно эффективной. Но в связи с тем, что мы хотим полностью избавиться от объекта нам придется собственноручно создать все команды обновления базы данных, как показано в листинге 8.6.

Глава 8. Обновление базы данных IS NULL AND IS NULL) OR (Discount AND IS NULL AND IS OR = ) Листинг 8.4. Запрос DELETE FROM CUSTOMER WHERE (CustomerlD AND IS NULL AND IS NULL) OR - AND NULL AND Gp6 IS NULL) OR = AND IS NULL AND IS NULL) OR (MiddleName = AND IS NULL AND IS NULL) OR (Address = AND IS NULL AND IS NULL) OR = AND IS NULL AND IS NULL) OR (City = AND IS NULL AND IS NULL) OR (State AND IS NULL AND IS NULL) OR = AND IS NULL AND IS NULL) OR = AND IS NULL AND IS NULL) OR AND IS NULL AND IS NULL) OR (DOB - AND IS NULL AND @p26 IS NULL) OR (Discount = AND IS NULL AND @p28 IS NULL) OR (CheckedOut = ) Листинг 8.5. Запрос INSERT INSERT INTO CUSTOMER! CustomerlD, FirstName, LastName, MiddleName, Address, Apartment, City, State, Zip, HomePhone, BusinessPhone, DOB, Discount ) VALUES ( @pl, @p2,,, @p5,,,,,, @pll,, ) Операторы SQL, созданные объектом очень жизнеспособны, но не очень эффективны, так как сгенерированный SQL-код имеет слишком большой размер, приводящий к увеличению времени компиляции запроса на сервере и объема передаваемой базе данных информации. К тому же объект создает SQL-операторы во время выполнения программы, что негативно влияет на скорость обновлений создаются заново для каждого обновления). К счастью, в большинстве случаев вы будете располагать дополнительными сведениями о базе дан ных, что позволит вам найти более эффективное решение. К примеру, я предпочитаю использовать хранимые процедуры для выполнения каждой операции обновления, и благодаря плана запроса, а также предварительной компиляции кода на сервере увеличивается быстродействие приложения.

Повышение эффективности команд, создаваемых объектом CommandBuilder Объект CommandBuilder наиболее удобно использовать при работе с аморфными данными или когда большее значение имеет скорость разработки, а не производи тельность. Если же ситуация отличается от описанной, вам следует создать свои соб ственные объекты команд DELETE, INSERT и UPDATE для объекта DataAdapter.

184 II. Класс DataSet • Если после заполнения объекта с помощью метода DataAdapter.

SelectCommand в схему были внесены перед вызовом метода (} необходимо вызвать метод В противном случае, объект не сможет связать столбец таблицы DataTable с информацией, полученной из свойства SelectCommand.

• Объект создает команды по мере необходимости. Объект коман ды существует немногим дольше того времени, которое требуется на обновление данных. Это означает также, что жизненный цикл объекта CommandBuilder дол жен перекрывать жизненный цикл команды, выполняемой в процессе вызова метода • Для того чтобы объект CommandBuilder проводил обновление с тран удостоверьтесь, что транзакция соединения связана со свойством SelectCommand. Это приведет к наследованию всех связанных с данным со единением транзакций.

• Объект CommandBuilder создает команды на основе метаданных столбцов таб лицы. Таким образом, обновление базы данных будет осуществляться без учета отношений, ограничений или наличия других таблиц в объекте DataSet.

Если все эти условия будут вы можете рассчитывать на ненное создание объектом CommandBuilder команд INSERT, и DELETE.

Объект CommandBuilder создает SQL-код, который обеспечивает оптимистический параллелизм путем проверки того, что состояние каждого столбца в обновляемых и удаляемых строках идентично его первоначальному состоянию. Поддержка оптими стического параллелизма в отсоединенном режиме доступа к данным достаточно про ста — мы всего лишь убеждаемся в том, что строка не претерпела изменений с мо мента ее извлечения из базы данных. Сгенерированный SQL-код практически иден тичен для каждого управляемого поставщика данных за исключением некоторых мелких деталей (например, в управляемом поставщике OLE DB вместо именованных параметров используются знаки вопроса). В листингах 8.3, 8.4 и 8.5 приведены при меры параметрических запросов, созданных с помощью объекта CommandBuilder.

Листинг 8.3. Запрос UPDATE CUSTOMER SET Address =, = WHERE ( = AND IS NULL AND IS NULL) OR (FirstName - AND IS NULL AND IS OR {LastName = AND IS NULL AND IS NULL) OR - } AND IS NULL AND IS OR = AND IS NULL AND IS NULL) OR (Apartment - AND IS NULL AND IS NULL) OR (City - AND IS NULL AND IS NULL) OR (State = AND IS NULL AND IS NULL) OR (Zip = AND IS NULL AND IS NULL) OR = AND IS NULL AND OR AND IS NULL AND IS NULL) OR (DOB AND Глава 8. Обновление базы данных Идентификация строк при оптимистическом параллелизме Реализация оптимистического параллелизма в отсоединенном режиме доступа к данным зависит от возможности строки, которые с момента их извлечения из базы несколько способов такой идентификации (табл. 8.1).

Таблица Методы идентификации строк, использующиеся при реализации оптимистического параллелизма Описание Создание Этот метод столбца, который будет использоваться времени созда- для проверки Обычно метка времени (метка при вставке и каждом обновлении этого метода возможна только при наличии контроля над схемой используемой базы данных Сравнение строк Этот подход используется классом CommandBuilder и предполагает проверку ства значений строки значениям, полученным после извлечения строки из базы данных Определение Этот метод представляет собой более сложную версию метода, предполагающего суммы метки времени Контрольная сумма определяется на основе всех строки полей строки, а сравнение производится между контрольной суммой строки, из базы данных, и таковой, хранящейся в базе данных на текущий момент. Код, ляющий контрольную сумму строки, достаточно сложен. Метод с вычислением контроль ной суммы более эффективен, чем метод, предполагающий сравнение строк, так как сравнение сумм возможно без фактического сравнения строк. Тем не менее себе суммы также требует Класс CommandBuilder Класс CommandBuilder отвечает за генерацию запросов по мере возникновения необходимости в них в объекте Создав объект CommandBuilder, его не обходимо передать в конструктор объекта DataAdapter. Как только объект об объекте DataAdapter, он использует свойство DataAdapter.

чтобы получить информацию о столбцах объекта и иметь возможность создать команды вставки, обновления и удаления данных. Если объект DataAdapter уже содержит некоторые команды (такие, как UPDATE и DELETE), объект CommandBuilder создает только отсутствующие команды. Для создания необходи мого SQL-оператора объект CommandBuilder регистрируется для получения уведом ления о событии Каждый управляемый содержит собственную реализацию класса CommandBuilder и Для того чтобы гарантировать нормальное функционирование объекта CommandBuilder, необхо димо учесть несколько моментов.

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

Объект CommandBuilder применяет SQL-оператор SELECT, для того чтобы иметь возможность создавать операторы вставки, обновления и удаления данных.

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

В противном случае объект CommandBuilder не сможет идентифицировать запись в таблице. В частности, при отсутствии уникального столбца не сможет быть создано предложение WHERE операторов UPDATE и DELETE.

182 Часть II. Класс = "30307";

// Добавление нового клиента.

newRow = "Chipper";

"10 Aaron Way";

"Atlanta";

"GA";

// Изменение информации о DataRow oldRow = "53 Peachtree Center";

= "30342";

// Удаление клиента.

DataRow delRow = // Создание объекта для генерирования // команд вставки, обновления и удаления данных.

= new (dataAdapter) ;

// Обновление базы данных.

Большая часть этого кода должна быть вам уже знакома. Мы создаем две новых строки в Customer, изменяем о существующем клиенте, после чего удаляем его из таблицы. При вызове метода DataAdapter О он анали зирует таблицу custTable, вставляет новые записи, удаляет запись, предназначенную для удаления, а также проводит обновление заданной строки. Команды INSERT, UPDATE и DELETE при этом не определяются, так как объект CommandBuilder создает их сам по мере необходимости.

Метод имеет множество переопределенных вариан тов. В этом примере в качестве параметра методу Update передавался объект Несмотря на то что это самый распространенный способ его использова ния, метод Update может принимать в качестве параметра массив объектов DataRow или же целый объект DataSet. Недостатком использования вари анта метода Update, принимающего в качестве параметра объект DataSet, является то, что он предполагает наличие в объекте DataSet таблицы с именем "Table". По идее можно было бы предположить, что вызов DataAdapter. Update О с объектом DataSet в качестве параметра приведет к обновлению всего объекта DataSet, однако на самом деле это приведет только к обновлению таблицы "Table". Альтернативный способ использования метода update заключается в передаче ему в качестве парамет ра объекта DataSet и имени таблицы, которую необходимо обновить.

Глава 8. Обновление базы данных // Создание DataAdapter.

SqlDataAdapter customerAdapter new * FROM CUSTOMER", SqlDataAdapter new * FROM INVOICE", // Создание объекта DataSet, dataSet - new // Использование // для заполнения объекта DataSet.

// Внесение изменений в объект DataSet.

// объекта DataSet // (каждая таблица должна быть обновлена Реализация оптимистического параллелизма Конечной оптимистического параллелизма является предотвра щение перезаписи последних обновлений базы данных. Предположим, что у нас есть система управления клиентами и мы извлекли из нее информацию об определенном клиенте. В это же время менеджер по работе с клиентами извлек из базы данных ин формацию о том же клиенте и изменил его демографические сведения. Если теперь вы попытаетесь внести некоторые в информацию о клиенте, должны ли эти изменения перезаписать информацию, внесенную менеджером по работе с клиен тами? Как разрешить подобную проблему? Оптимистический параллелизм позволяет изменять данные только в том случае, если они не были изменены с момента их за грузки из базы данных, В рассматриваемой ситуации вы получите сообщение о не возможности изменения данных.

В случае использования отсоединенных данных достичь оптимистического парал лелизма можно путем гарантирования что все записи в объекте DataSet иден тичны записям, извлеченным из базы данных, К счастью, позволяет осу ществлять такую проверку автоматически с помощью класса При мер оптимистического параллелизма показан в листинге 8.2.

Листинг 8.2. Оптимистический параллелизм, обеспечиваемый // Определение переменной для упрощения доступа к таблице DataTable = // Добавление нового клиента.

newRow = = "Mike";

= "10 Aaron Way";

= "Atlanta";

180 Часть II. Класс DataSet Параллелизм в Прежде всего необходимо определить тип который требуется в на шем конкретном случае. поддерживает три типа параллелизма.

• Оптимистический параллелизм (optimistic Все пользователи могут получить доступ к данным объекта DataSet до тех пор, пока кто-либо из них не начнет запись информации в базу Это самая распростра ненная модель параллелизма в • Пессимистический параллелизм concurrency). Нельзя получить доступ к данным объекта DataSet, пока кто-либо из пользователей владеет копией данных (в этом случае что данные блокируются).

• Деструктивный параллелизм (destructive concurrency). Все пользователи имеют доступ к объекту DataSet, но в базе данных зафиксированы только самые последние изменения. На самом деле это означает фактическое отсутствие кон троля параллелизма. Компания Microsoft называет такую модель параллелизма моделью "последний побеждает".

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

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

Обновление объекта DataSet с помощью DataAdapter Прежде чем рассмотреть аспекты различных моделей параллелизма в необходимо понять поведение объекта DataAdapter при вызове метода Update. Поскольку с помощью объекта DataAdapter можно обновить только один объект необходимо поддерживать взаимооднозначное соответствие объек тов DataAdapter объектам DataTable. При вызове метода объ ект DataAdapter анализирует таблицу и список изменившихся строк. Для каждой изменившейся строки объект DataAdapter выполняет соответствующую команду.

К примеру, для новой строки выполняется команда для изменив шейся а для При выполне нии каждой команды объект DataAdapter делает одно обращение к базе данных;

дру гими словами, он не поддерживает пакетные обновления.

Для того чтобы оптимизировать работу объекта DataAdapter, необходимо поддер живать его существование на протяжении всей жизни объекта DataSet. После за полнения объекта DataSet объект DataAdapter может быть использован повторно для обновления базы данных, как показано в листинге 8.1.

Листинг нескольких объектов // Создание и открытие соединения.

conn = new + + "Integrated Глава 8, Обновление базы данных Глава Обновление базы данных В этой главе...

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

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

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

Свойство интерфейса (поддерживается класса ми SqlTransaction, и определяет уровень изоляции транзакции. Несмотря на то что транзакции можно использовать для облегчения обработки параллельных обращений, они жестко свя заны с соединениями. Другими словами, их нельзя применять при работе с отсоеди ненными ADO.NET. К примеру, мы не можем начать транзакцию (открыть соединение) и оставлять ее открытой на протяжении всего времени использования объекта DataSet.

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

Часть Класс DataSet потому что параметры false и определяют поведение, принятое по умолчанию. С другой стороны, во втором вызове мы ожидаем идентичность схем объектов DataSet, а поэтому при нарушении этого ус ловия необходимо сгенерировать исключение.

Резюме Механизмы манипуляции и по данным, поддерживаемые объектом DataSet, предоставляют которая его чем-то большим, чем просто база данных, в памяти. Упрощенный способ изменения строк, а также способность объекта хранить "историю" данных, делает механизм соз дания, чтения, обновления информации в объекте DataSet и удаления информации из него намного более мощным. Кроме этого, по между объектами функциональность, прежде не свойственную уров ням доступа к данным Microsoft. Наконец, даже несмотря на отсутствие поддержки языка SQL, ADO.NET позволяет фильтровать, сортировать данные и проводить их поиск в объектах DataTable с помощью метода который при совместном применении с в состоянии удовлетворить запросы самых искушенных разработчиков.

7. Манипулирование объектом DataSet Метод Merge поддерживает все три сценария слияния объектов DataSet с помощью переопределенных вариантов, различные парамет ры. При вызове метода Merge помните, что передаваемые ему данные будут объеди с данными используемого объекта DataSet. В процессе слияния объектов DataSet первой объединяются схемы, затем — данные.

Ниже приведены примеры трех вариантов слияния объектов DataSet.

Слияние двух объектов DataSet.

// Слияние двух объектов DataSet.

В приведенных выше примерах с объектом DataSet объединяются исход ный объект DataSet и таблица Products исходного объекта DataSet. Если в схеме це левого объекта DataSet будет отсутствовать заданный объект DataTable, по умолчанию к схеме необходимый фрагмент.

// Слияние происшедших в одном DataSet, // с другом // DataSet имеют одинаковую // Слияние изменений, происшедших в одном объекте DataSet, происшедшими в другом // объекте DataSet (оба имеют одинаковую В приведенных выше примерах показано слияние одних лишь изменений исходного объекта DataSet с целевым объектом DataSet.

Слияние двух объектов DataSet с установкой // состояния строк в Unchanged.

// Слияние двух объектов DataSet с сохранением // состояния и версий строк.

В приведенном выше примере переопределенный вариант метода Merge позволяет указать, следует ли сохранять при слиянии объектов DataSet изменения в строках ис ходного объекта DataSet (состояние и версию строк). Если в качестве второго пара метра указать true, то при слиянии объектов DataSet сохраняется состояние и раз личные версии строк. Вызов метода с false в качестве второго параметра равносиль но его отсутствию.

// Слияние двух объектов DataSet с различными схемами.

// Слияние двух объектов DataSet с похожими схемами // (в случае различия схем будет сгенерировано false, Наконец, в приведенном выше примере переопределенный вариант метола Merge поддерживает определение действия на случай отсутствия нужной схемы в целевом объекте DataSet. Первый вызов метода Merge идентичен вызову метода 176 Часть II. Класс DataSet порядковый номер строки в объекте (а не в объекте DataTable), а метод — массив объектов DataRowView, которые представляют стро ки, критерию (листинг Листинг 7.18, Поиск с помощью объекта // Определение переменной для доступа к DataTable = // Определение порядка сортировки.

= "Vendor";

// Поиск по производителю int found // Поиск товара по производителю rows * } // Определение порядка = "Vendor, Price // Поиск товара по производителю и criteria - new object[] found = Vendor Search:

Последний тип критерия представляет собой пример составного ключа. При поиске по двум ключам необходимо использовать массив ключей. Составной ключ может использоваться как с DataView. Find так и с методом Слияние объектов DataSet Иногда может возникнуть необходимость в слиянии двух объектов DataSet. К при меру, ваше приложение должно провести анализ в нескольких объектах DataSet. Для того чтобы создать отношения между таблицами различных объектов DataSet, последние необходимо объединить. С другой стороны, при прове дении удаленного обновления объектов DataSet через Web-службу вам может понадо биться объединить изменения этих объектов в локальном объекте DataSet. Для под держки такой функциональности ADO.NET предоставляет три способа:

• копирование содержимого объекта DataSet с той же схемой с объедине ния всех строк объектов DataSet;

• обновление состояния записей из одного объекта DataSet в другой объект DataSet с использованием той же схемы;

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

Глава 7. объектом Item:

- Vendor:

- Price:

При работе с объектом DataSet, который содержит связанные объекты массив также содержит связанные объекты Это может ока заться полезным, так как результат проведения поиска в объекте DataTable, содержа в реляционном объекте DataSet, может быть использован для получения под множества объекта DataSet, соответствующего заданному критерию (листинг В этом примере демонстрируется слияние объектов DataSet, которое будет рассмотрено далее в этой главе.

Листинг 7.17. Выделение подмножества объекта DataSet с помощью методов и // Поиск клиентов, проживающих штате gaCusts // Создание нового DataSet.

DataSet newDataSet = new // схемы старого объекта DataSet в поток.

= new Помещение указателя на начало = 0;

// Считывание в новый объект DataSet.

// Копирование строк в новый объект DataSet.

Поиск с помощью объекта DataView Объект DataView может использоваться для проведения поиска в объекте DataTable с помощью свойства RowFilter. Следует отметить, что довольно часто установка зна чения свойства RowFilter полностью функционально эквивалента поиску. Напом ним: RowFilter используется для отбора строк объекта удовле творяющих определенному критерию. Несмотря на то что эта методика очень похожа на использование метода Select она может оказаться более удобной в том случае, если вы уже используете объекты DataView. Во-первых, это позволяет из бежать новых копий а во-вторых, получаемый в результате прямо угольный набор данных более удобен в использовании, чем массив объектов В отличие от этого, методы Find и FindRows класса DataView предназначены для поиска строк по заданному значению ключа. Для того чтобы провести поиск в объек те DataView, необходимо установить значение свойства DataView. Sort равным ключу (ключам), по которому будет производиться поиск, после чего вызвать метод DataView. или Метод возвращает 174 Класс DataSet которые были изменены с момента последнего вызова метода DataTable.

• None — не отображается ни одна строка.

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

• Unchanged — отображает только те строки, которые остались неизмененными с момента последнего вызова метода DataTable, AcceptChanges О.

Поиск в объекте DataSet К объект DataSet не поддерживает запросов SQL во всей его полноте. В то же время ADO.NET проведение простого поиска в пре делах объекта DataTable, в результате которого возвращаются строки, соответствую заданному критерию. Кроме этого, объект поддерживает поиск на ос нове индекса.

Поиск с помощью метода DataTable.Select Для поиска строк в объекте DataTable, удовлетворяющих определенному критерию, используется метод DataTable.Select О. Критерий поиска задается выражением, синтаксис которого совпадает с синтаксисом свойства В ре зультате вызова метода возвращается массив строк соответствующих заданному критерию. Если критерию не соответствует ни одна стро массив имеет нулевой размер, Пример использования метода показан в листинге 7.16, Листинг 7.16. Поиск в объекте DataTable с помощью метода // Определение переменной для упрощения доступа к DataTable // Поиск строк по заданному критерию.

DataRow[] rows = Select // Вывод строк, соответствующих // критерию, на (DataRow row in rows) Item:

Поиск строк по заданному составному критерию.

= = + "AND Price > 20. Select // соответствующих заданному на foreach (DataRow row in 7. DataSet которых начинаются с Microsoft, вне зависимости от того, сколько символов находится после первых девяти.

Родительские отношения dataView.RowFilter < Ссылаться на данные таблицы-родителя фильтруемых строк можно с префикса Parent.

Дочерние dataView.RowFilter dataView.RowFilter Точно так ссылаться на данные в дочерней таблице фильтруемых строк можно с помощью префикса Child, если у таблицы есть только одно дочернее отношение. В противном случае необходимо указывать имя дочернего Фильтрация по свойству RowState с помощью объекта DataView Объект DataView поддерживает фильтрацию по состоянию строк в объекте как показано в листинге Листинг 7.15. Фильтрация с помощью объекта DataView Определение переменной для упрощения доступа к DataTable dataTable // фильтра для данных // (для измененных будет отображено их // исходное — Для того чтобы провести на основе свойства RowState, необходимо воспользоваться перечислением DataViewRowState, которое поддерживает как фильтрацию на основе свойства так и отображение версий строки. Ниже приведено краткое описание каждого элемента этого перечисления.

• — отображает все версии строки, кроме удаленной. Это значение используется по умолчанию.

• Added — отображает строки, добавленные после последнего вызова метода • Deleted — отображает строки, удаленные с момента последнего вызова метода.

• отображает версию измененных строк. Строка считается измененной, если было изменено значение хотя бы одного ее столб При использовании этого значения объект DataView отобразит только те строки, которые были с момента последнего вызова метода.

• отображает первоначальную версию измененных строк.

При использовании этого значения объект DataView отобразит только те DataSet Фильтрация данных с помощью объекта DataView Класс DataView поддерживает фильтрацию данных с помощью свойства DataView.

RowFilter. Свойство представляет собой строку, критерий фильтрации. Пример фильтрации данных с объекта DataView показан в лис тинге 7.14.

Листинг 7.14. данных с помощью объекта DataView Определение переменной для упрощения доступа к DataTable dataTable // "Vendor = удовлетворяющих Синтаксис строки критерия фильтра идентичен синтаксису свойства и представляет собой синтаксис клиентского языка запросов (языка за просов к отсоединенным данным). В следующих разделах приведены некоторые при меры установки свойства RowFilter.

Текстовые фильтры = = Простые текстовые сравнения принимают форму точного соответствия. Тем, кто привык использовать оператор сравнения языка С#, стоит быть внимательными, так как в этом синтаксисе оператор сравнения имеет форму =. Кроме этого, оператор неравенства (о) отличается от оператора неравенства языка С# Фильтр по дате = В качестве критерия фильтрации можно использовать дату, которую необходимо окружить символами диеза (ft).

Операторы сравнения "Cost > 24.99";

Поддерживаются операторы сравнения <, >, и >=.

Составные выражения 24.99) AND (Cost > Для составных выражений поддерживаются булева конкатенация (AND, OR и NOT) и группировка с помощью скобок.

Групповые символы = "Company LIKE В рамках синтаксиса LIKE для текстового сравнения поддерживаются групповые * и Приведенный выше фильтр пропустит все компании, названия В первоначальной версии Visual Studio ссылка MSDN была такой:

7. объектом DataSet Объект DataView Класс Dataview используется для осуществления привязки данных к элементам управления Windows-форм и Web-форм ASP.NET. (Более подробно Windows- и Web формы рассматриваются в главе "Привязка в В этом разделе будет продемонстрировано использование объекта DataView для поиска, сортировки и содержимого объекта DataTable. Каждый объект Table содержит объ ект DataView. Объект DataView, используемый по умолчанию, называется Defaultview, но вы можете создать столько объектов DataView, сколько необходимо.

Это может оказаться полезным при отображении одних и тех же данных, отфильтро ванных или отсортированных различным образом (листинг Листинг Объект DataView, используемый по умолчанию переменной для упрощения доступа к DataTable dataTable // Подсчет количества строк в представлении, // используемом по умолчанию.

// нового DataView, DataView = new В этом примере используются два различных представления — представление, принятое по умолчанию, и представление sortedView. Они никак не влияют на дан ные;

можно сказать, что они содержат них различную "точку Сортировка данных с помощью объекта DataView Класс DataView поддерживает сортировку данных с свойства DataView.

Sort. Свойство Sort представляет собой строку, критерий сортировки.

Для того чтобы провести сортировку с объекта DataView, просто установите значение свойства Sort, использовав для этого имя одного или нескольких столбцов с модификатором или DESC, порядок сортировки (по возрастанию или по убыванию). На практике по умолчанию используется возрастающий порядок сортировки, поэтому его можно не указывать (листинг Листинг 7.13. данных с помощью объекта DataView Создание для упрощения доступа к DataTable dataTable // Сортировка по (по = // по LastName (по "LastName DESC";

// Сортировка по LastName (no // а по (по = "LastNarae, DESC" 170 Часть II. Класс DataSet Decimal - 0;

// Вывод счета.

DataRow[] = (DataRow in { DataRow productRow int quantity = Decimal total = * grandTotal += total;

quantity, } В результате выполнения этого фрагмента кода будет получен следующий результат:

Maddux, Greg : 3/21/ Basketball 2 $29.00 ea Baseball Mitt 3 $89.97 ea Total:

101 : 3/31/ Bat, Slugger 1 $19.99 ea Total: $19. Glavine, 102 : 3/24/ Baseball Mitt 1 $29.99 ea Total: $29. John 103 : 3/27/ Baseball Bat, Slugger 1 @ $19.99 ea Total:

: 4/1/ Baseball 1 $1.75 ea Total: $1. Вы видите, что мы получаем только непосредственно связанные строки. Так, для получения массива объектов DataRow, связанных с помощью отношения с дочерней таблицей, был использован метод GetChildRows последний раз при использова нии метода GetChildRows () мы что только одна строка таблицы Product со информацию о товаре, а поэтому достаточно было извлечь лишь первую стро ку. Если этот фрагмент будет присутствовать в конечном коде приложения, в него не обходимо добавить код, проверяющий, что массивы объектов DataRow не содержат значения null (это происходит, когда дочерняя таблица не содержит подходящих строк).

Для получения родительских строк класс DataRow предоставляет два практически идентичных метода: GetParentRows () и Единственная разница между ними заключается в том, что первый метод возвращает массив объектов DataRow, а второй — единственную (первую) строку.

Глава 7.

Address Apartment City State Zip DOB Discount Stamp PK InvoicelO Vendor InvoiceDate Cost Terms InStock FOB Price PO CustomerlD I FK Quantity Discount Рис. 7.2. Отношения между четырьмя таблицами invRow in I string invoiceDate :

168 Часть II. Класс Row В результате выполнения этого фрагмента кода на консоль будет выведен следую щий текст:

New Row State: Added Deleted Row State: Deleted Changed Row State: Modified Unchanged Row State: Unchanged В этом примере демонстрируется изменение свойства Rowstate, те кущее состояние строки. После обновления хранилища данных состояние строки ус танавливается равным Более подробно обновление базы дан ных рассматривается в главе 8, "Обновление базы данных".

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

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

В схеме базы данных объединение таблиц осуществляется в тех же которые используются при определении отношения как внешние ключи). Результат объединения двух таблиц базы данных может содержать повторяющуюся информа в том случае, когда одна строка таблицы-родителя соответствует нескольким дочерним строкам. В ADO.NET все по-другому — каждая таблица остается сама по себе, а объект DataSet позволяет перемещаться по отношению между таблицами на уровне строк.

На рис. 7.2 показаны отношения между таблицами Invoice, и Product, которые позволяют перемещаться по этим таблицам в обоих направлени ях. Для того чтобы переместиться вверх или вниз по отношению, необходимо вос пользоваться объектом DataRow, поддерживающим перемещение как к дочерним, так и к родительским строкам (листинг Листинг Перемещение по отношениям // Итерация по таблице клиентов // с соответствующих foreach in { // Вывод имени Console // Вывод счетов.

= Глава 7. Манипулирование объектом DataSet имеет ли строка Row добавочные версии, можно с помощью вызо ва метода Этот метод очень полезен, когда вам необходимо получить определенную версию строки. Версия всегда зависит от состояния строки. Если же версия строки не будет указана, то по умолча нию версия Состояние строки При каждом изменении строки (включая добавление и удаление) в объекте Row сохраняется ее состояние, на основании которого обновление хранилища данных (листинг 7.10).

Листинг 7.10. Получение строки // Определение переменной для упрощения доступа к DataTable dataTable // Добавление строки.

= = // Вывод на консоль состояния строки.

Row // Определение переменной для облегчения // доступа к первой DataRow deleteRow // Удаление строки.

// Вывод на консоль состояния строки.

Row // Определение переменной для облегчения // доступа ко второй строке.

DataRow = // Изменение строки.

// Вывод на консоль состояния строки.

Row // Определение переменной для облегчения // доступа к третьей строке.

DataRow unchangedRow = // Вывод на консоль состояния строки.

166 Часть Класс DataSet Accept Рис. 7. Жизненный цикл строки Конечно же, эти методы можно вызывать самостоятельно, однако обычно они ис пользуются объектом для изменения каждого объекта при об новлении базы данных. Важно понять, что если объект DataRow содержит изменения, которые не были приняты или отвергнуты, вы можете получить доступ к обеим вер сиям строки.

Для определения версии строки класс DataRow использует перечисление DataRowVersion. Ниже приведены элементы, входящие в это перечисление.

• Current значение строки. После внесения изменений это значение ста нет предложенным, а после вызова метода AcceptChanges — первоначальным.

• Default — значение строки по умолчанию в соответствии с ее текущим со стоянием.

• Original — значение строки сразу же после ее создания или последнего вызо ва метода AcceptChanges.

• Proposed — измененное значение строки перед вызовом метода Это значение становится доступным после завершения редактирования строки.

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

string = string newString Глава 7. Манипулирование // Завершение данных // уведомлений В этом примере демонстрируется единовременное добавление строки с метола Сигнатура этого метода очень похожа на сигнатуру метода, добавлять строку в виде массива значений. Если значение некоторого в строке генерируется автоматически или если необходимо исполь зовать значение по умолчанию, в качестве значения такого столбца следует передать объект Второй параметр метода определяет действие, которое должно выполняться после вызова данного метода. Если параметр равен true, то сразу же после вызова метода будет вызван метод сообщающий объекту о необходимости принятия всех внесенных изменений и дополнений. Это никак не влияет на саму базу данных, просто мы сообщаем объекту что эти данные необходимо рассматривать как принятые. Следует отметить, что при вызове метода О будут приняты все внесенные в объект DataTable изменения (а не только те из которые связаны с добавлением строки DataRow). Если в качестве второго параметра метода указать строка DataRow будет помечена как вставленная и может быть добавлена в базу данных при следующем обновлении последней.

Версия строки При работе с большинством старых уровней доступа к данным все действия по ре дактированию строк проходили одновременно в классе и в базе данных. Так как поддерживает отсоединенный режим доступа к ни одно измене ние не отразится на базе данных до тех пор, пока объект DataSet не получит об этом явное уведомление. Поскольку изменения не вносятся в объекте DataSet необходимо хранить информацию об изменившихся данных. По идее мы могли бы предположить, что для каждой строки или значения в строке существует флаг На самом деле такой флаг был бы бесполезен возможности возникно вения необходимости отмены изменений или получения информации о первоначаль ной версии заданной строки с целью осуществления проверки параллелизма. Для поддержки подобной в объекте DataRow хранится до двух копий а также флаг, описывающий ее состояние (сложный флаг При редактировании определенной строки поддерживаются две ее версии: перво начальная и предлагаемая (если строке присваивается новое значение) версии. Жиз ненный строки показан на рис.

Как следует из диаграммы, при редактировании строки DataRow она как измененная до тех пор, пока не будет вызван метод Acceptchanges {) или Выполнение этих методов заставляет объект DataRow одно из двух действий.

• AcceptChanges — первоначальное значение заменяется предложенным значе нием, при этом первоначальное значение удаляется. Строка DataRow помечает ся как неизмененная.

• RejectChanges — предложенное значение удаляется, а строка помечается как неизмененная.

164 Часть DataSet // в () // Начало внесения изменений в О ;

// Установка значения столбца типа // (выполняется успешно, гак как указанное // легко преобразуется в значение типа DateTime) = "04/24/1969";

для // вызывается метод Массовая загрузка данных в объект DataTable Необходимость отключения уведомлений и ограничений может потребоваться также при загрузке большого объема информации в объект DataTable. Класс DataTable реали зует эту возможность посредством методов и Кроме подавления уведомлений и ограничений, во загрузки данных не поддерживают ся индексы, что в целом приводит к ускорению загрузки. Для факти ческого добавления или изменения строк используется метод LoadDataRow класса DataTable, как показано в листинге 7.9.

Листинг 7.9. Массовая загрузка данных в DataTable // Определение переменной для упрощения доступа к DataTable dataTable = // Начало загрузки данных // ограничения, уведомления // и // Создание строки.

object[] row = new = row[l] "Kevin";

- "Millwood";

// строки без // внесения // изменений.

// Создание строки.

row - new = [1] = = "Moss";

// Добавление строки с подтверждением внесения // Глава 7. // Установить подобное // как неправилен.

= "04/31/1969";

// подобное значение также не удастся, // поскольку оно быть преобразовано в // типа float // В этом примере при попытке внесения двух последних изменений будут сгенери рованы исключения, о невозможности типа. Индексатор не проводит проверку его поведение больше похоже на вызов метода В большинстве случаев объект DataRow пытается самостоя тельно провести преобразование типа, что не всегда является приемлемым. Я бы по советовал вам самостоятельно проводить явное преобразование значений к нужному типу. Как правило, в результате этого мы получаем более устойчивый к ошибкам код, чем при проведении преобразования "за кулисами", Пакетные изменения строки ситуации, в которых изменения в конкретную строку DataRow необ ходимо параллельно. Обычно это делается тогда, когда одно изменение при водит к нарушению некоторого ограничения или когда вам необходима возможность отмены изменений перед их внесением в базу данных. Предположим, что у нас есть таблица с ограничением, чтобы один из двух столбцов не был равен null и чтобы оба столбца к тому же не были заполнены одновременно. Если попытаться создать код, это ограничение, то придется провести множество бессон ных ночей в поисках промежуточного состояния строки, все еще ограничению. Для того чтобы обойти эту проблему, класс DataRow предоставляет ме тоды и CancelEdit. Как только будет вызван метод изменения перестанут отражаться на объекте DataRow до тех пор, пока не будет вызван метод Если внесенные изменения окажутся ошибочны ми, можно вызвать метод который вернет строку в первоначальное состояние (состояние, в котором она находилась до вызова метода BeginEdit). Самое главное, что на протяжении всего процесса изменения строки не проверяются огра ничения и не срабатывают события. Пример внесения пакетных изменений приведен в листинге 7.8.

Листинг 7.8. Пакетные изменения строки DataRow Определение переменной для доступа к DataRow = // Начало внесения в // Установка нескольких корректных значений.

= - "Kevin";

// Установка значения =.15;

162 Часть //. Класс DataSet else ', :

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

Альтернативой проверке типа столбца на DBNull является опрос строки на пред мет равенства значения столбца null с помощью метода Приме няя этот метод, можно использовать те же самые переопределенные варианты, кото рые для индексатора (имя, порядковый номер или объект DataColumn), например:

if...

if...

if (row, IsNull ) Помимо чтения данных, нам необходимо иметь возможность манипулировать ими.

Как можно было ожидать, классы и DataRow под держивают выполнение простых Индексаторы класса DataRow позволяют установить новые значения столбцов строки. Будьте осторожны: при определении нового значения столбца объект DataRow сгенерирует исключение в том случае, если это значение бу дет конфликтовать со свойством DataType объекта DataColumn. Несмотря на то что точного соответствия типу, определенному в объекте не требуется, он должен поддаваться преобразованию в тип столбца (листинг 7.7).

Листинг 7.7. Добавление преобразованию // Определение переменной для упрощения доступа к DataRow row = // Установка корректных значений столбцов строки.

= "Kevin";

// Установка типа // Установка значения типа DateTime // (выполняется так как указанное // значение легко преобразуется в значение типа Глава 7. Манипулирование объектом foreach (DataRow row in { ) // Создание переменных для упрощения доступа к столбцам.

= = // Вывод имени каждого клиента на консоль // с использованием объекта foreach (DataRow row in { } Индексатор класса DataRow возвращает значение столбца строки в виде экземпляра класса Следует отметить, что этому значению известен его тип (как и всем типам Вы можете привести значение столбца к нужному типу, а можете использовать его без приведения. Кроме информации о типе, существует доступ к кол лекции Column объекта DataTable, содержащей информационную схему таблицы.

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

Листинг 7.6. Выполнение к Вывод имени каждого клиента на консоль использованием имени столбца.

foreach (DataRow row in dataTable.Rows) { for (int x = 0;

x < ++x) { object obj = if { : be } if { :

Часть II. Класс // Удаление строки на основе // для упрощения доступа к первой row = // ;

// Удаление строки на порядкового Чтение и запись значений столбцов строки Вывод данных, в объекте DataSet, может быть осуществлен путем по объектам DataRow заданного объекта DataTable, как показано в листинге 7.4.

Листинг 7.4. Чтение значений столбцов строки // Создание переменной для упрощения доступа к таблице.

DataTable dataTable // Вывод имени каждого клиента на foreach (DataRow row in { В этом примере продемонстрирован вывод данных, в одной таблице объекта DataSet. Ключевым моментом здесь является доступ к объектам DataRow за данного объекта DataTable. Класс DataRow поддерживает синтаксис строкового индек сатора (свойство Item в для получения значения столбца конкретной строки (например, Кроме этого, индексатор класса DataRow поддержива ет использование порядковых номеров и объектов представляющих стол бец таблицы DataTable, как показано в листинге 7.5.

Листинг 7.5. Индексаторы класса DataRow И имени каждого клиента на консоль // с использованием имени столбца.

WriteLine ( ;

foreach (DataRow row in { • Вывод имени каждого клиента на консоль // с использованием порядкового столбца.

Глава 7, Манипулирование DataSet Следует отметить, что метод сам по себе не добавляет стро ку в объект DataTable. Для этого необходимо вызвать метод передав ему в качестве параметра объект строки.

Новые строки не обязательно добавляются в конец объекта DataTable. Если вам необходимо вставить новую строку в середину воспользуйтесь методом InsertAt как показано в листинге 7.2.

Листинг 7.2. Вставка объекта в середину коллекции строк объекта DataTable Добавление новой строки создания объекта DataRow.

= = = Удаление строк При использовании отсоединенных данных к удалению строки из коллекции предъявляется особое требование: строка должна продолжать существовать до тех пока хранилище данных не будет обновлено с помощью объекта DataSet. Тем не ме нее сейчас нам это знать не обязательно. Удаление строки может быть одним из трех способов:

• с помощью метода на основе заданного экземпля ра класса DataRow;

• с метода 0 на основе заданного поряд кового номера строки;

• с помощью метода DataRow. Delete (строка удаляет себя сама).

Все три способа можно использовать для удаления заданных строк. При этом важ но понимать, что удаление строки может повлечь за собой изменение порядковых номеров строк. Если добавить и удалить строку перед как изменения будут при няты объектом DataTable, то система фактически удалит элемент из коллекции. При удалении элемента, который существует за пределами объекта DataTable, строка со храняется, но помечается как удаленная — хранящаяся в строке, еще понадобится для проведения обновления хранилища данных. Таким исполь зование порядкового номера строки очень так как он может изменяться каждый раз при удалении или добавлении элементов. Различные способы удаления строк показаны в листинге 7.3.

Листинг 7.3, Удаление строк // Создание переменной для упрощения доступа к DataTable dataTable // строки путем создания нового объекта DataRow.

DataRow newRow = — = "Kevin";

// <- Важно!

755 Часть Класс DataSet информацией и затем отправлять ее на хранение обратно в базу Set яв ляется единственно правильным выбором. А вот использование объекта для создания простого отчета (например, обычно является ошибкой. "Обычно", потому что из этого правила тоже существуют исключения.

Рассмотрим конкретную Web-узел электронной коммерции. Предполагая, что у нас есть много продаваемых товаров, я бы воспользовался объектом DataReader для предоставления списка товаров пользователю и объектом DataSet для поддержки "корзины" покупателя. Кэширование списка товаров привело бы к потере эффективности и разраста нию объема кэша. Обычно кэширование часто элементов стоит поручить базе данных. В большинстве случаев такое решение полностью оправдано. С другой стороны, объект DataSet, содержащий информацию о пользователе и его может быть кэ широван в Web-сеансе с целью сократить количество обращений к базе данных. Если посе титель так ничего и не купит, его "корзина" просто исчезнет (и не будет ни разу сохранена) при уничтожении сеанса.

Запомните простое эмпирическое правило: вам нужно что-то кэшировать, тесь объектом DataSet, в противном случае обратите внимание на объект DataReader.

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

Листинг Добавление нового объекта // Создание переменной для упрощения доступа к DataTable dataTable = ;

// Добавление новой строки путем создания объекта DataRow.

DataRow = = = // <- Важно!

Добавление путем создания // массива значений столбцов.

= new newValues[0] = [1] = = "Moss";

// <- Важно!

Метод поддерживает оба способа добавления новой строки. Какой из них лучше использовать в каждом конкретном случае? Я предпочи таю использовать метод так как он позволяет заполнить новую строку значениями, гарантируя их соответствие требованиям столбцов. Другими сло вами, значения в новом объекте DataRow удовлетворяют схеме объекта DataTable, что делает невозможным добавление некорректных значений. К тому же явное обращение к элементам строки сделает код более читабельным. Действительно, первый способ добавления нового объекта DataRow намного более прозрачен, можно ви деть, что нулевой столбец представляет собой идентификатор клиента, первый стол бец — его имя, а третий столбец — его фамилию.

Глава 7. Манипулирование DataSet Глава Манипулирование объектом DataSet В этой главе...

Изменение данных Перемещение по объекту DataSet Поиск в объекте DataSet Слияние объектов DataSet Прочтя главы 5 и 6, мы научились создавать объекты DataSet. Теперь возникает вопрос: что с ними делать? Данные, которые хранятся внутри объекта DataSet, содер жат не только информацию, необходимую для поддержки отсоединенного кэша базы данных, но также предоставляют возможность перемешаться по нему как по некото рой иерархической структуре.

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

Объекты DataRow являются основным данных внутри объекта DataSet. Объект DataRow содержит массив значений, представляющий собой строку объекта DataTable. Объекты DataRow доступны из объекта DataTablc через свойство Rows, являющееся коллекцией объектов DataRow (для этого используется класс DataRowCollection). Объект DataRow отличается от традиционных коллекций лей, поскольку он, помимо текущего значения каждого столбца в строке, содержит в себе некоторую дополнительную информацию. Она представляет собой следующее:

• текущее значение каждого столбца в строке;

• первоначальное значение каждого столбца в строке, если она была изменена;

• состояние столбца (добавлен, удален, изменен и т.д.);

• средство перехода к родительской или дочерней строке (при наличии отношений).

Pages:     || 2 | 3 | 4 |



© 2011 www.dissers.ru - «Бесплатная электронная библиотека»

Материалы этого сайта размещены для ознакомления, все права принадлежат их авторам.
Если Вы не согласны с тем, что Ваш материал размещён на этом сайте, пожалуйста, напишите нам, мы в течении 1-2 рабочих дней удалим его.