Технология ADO. Объекты ADO. Эквивалент из комплекта BDE

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

На сегодняшний день ADO.NET является наиболее развитой технологией доступа к данным среди технологий, разработанных корпорацией Microsoft. Она развивает те принципы, которые были заложены в таких технологиях, как DAO и ADO, делая их более простыми в применении, более мощными и универсальными. В то же время ADO.NET является уже иной технологией доступа к данным.

Рассмотрим архитектуру ADO.NET

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

На рис. 10.1 показаны классы, составляющие объектную модель ADO.NET

Рис. 10.1. Иерархия объектов ADO.NET

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

Одной из основных идей, лежащих в основе ADO.NET, является наличие поставщиков данных.

Поставщик данных - это набор классов, предназначенных для взаимодействия с хранилищем данных определенного типа. За счет этого модель ADO.NET является чрезвычайно гибкой и расширяемой. Рассмотрим уровни модели поставщиков ADO.NET (рис. 10.2).


Рис. 10.2. Уровни моделей поставщиков ADO.NET

Как видно из рис. 10.2, приложение.NET взаимодействует с базой данных посредством поставщиков данных. Каждый поставщик данных может обеспечивать доступ только к базе данных определенного формата. Так, для доступа к БД Microsoft SQL Server используется поставщик SQL Server для.NET, для доступа к БД Oracle - поставщик Oracle для.NET и т. д. Названные базы данных являются одними из самых распространенных во всем мире, поэтому поставщики данных для них выделены в отдельные элементы модели ADO.NET ; тем не менее существует множество баз данных другого формата, к которым необходимо осуществлять доступ из приложения.NET. Для этого могут применяться поставщики данных OleDb для.NET или ODBC для.NET, обеспечивающие доступ к любым данным, для которых существует драйвер OleDb либо ODBC соответственно.

При построении приложения, использующего доступ к данным, необходимо сперва попытаться найти "родного" поставщика данных для.NET. Когда же такового не существует, можно воспользоваться OleDb, если существует соответствующий драйвер для источника данных, к которому устанавливается подключение. Т. к. технология OleDb существует достаточно давно, создано много драйверов для различных источников данных, поддерживающих ее. Если в системе не установлен соответствующий драйвер OleDb, его можно попытаться найти на сайте разработчика той базы данных, к которой осуществляется доступ, либо на сайте Microsoft. То же самое справедливо и для технологии ODBC. В ряде случаев существует несколько альтернативных вариантов организации доступа к данным определенного формата. Например, доступ к источнику данных на основе SQL Server можно организовать используя либо поставщик SQL Server для.NET, либо поставщик OleDb. Тем не менее всегда предпочтительнее использовать тот поставщик данных, который специально предназначен для обеспечения доступа к конкретному источнику данных, т. к. он учитывает его особенности.

Каждый поставщик.NET реализует одинаковые базовые классы - Connection, Transaction, DataAdapter, Command, Parameter, DataReader имена которых зависят от поставщика. Например, у поставщика SQL Server существует объект SqlDataAdapter, у поставщика OleDb - OleDbDataAdapter и т. д.

У каждого поставщика данных существует собственное пространство имен. Хотя все поставщики относятся к пространству имен System.Data, каждый из них содержит свой подраздел этого пространства, который содержит объекты, специфичные для данного поставщика. Например, объект SqlDataAdapter находится в пространстве имен System.Data.SqlClient.

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

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

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

Объект Command представляет запрос к источнику данных, вызов хранимой процедуры или прямой запрос на возврат содержимого конкретной таблицы. Как известно, существует несколько типов запросов. Часть из них возвращают данные, извлекаемые из источника данных, другие изменяют записи, третьи - управляют структурой БД. С помощью объекта Command возможно выполнить любой из перечисленных типов запросов. Различия в поведении объекта Command начинают проявляться тогда, когда необходимо исполнить тот или иной запрос. Так, например, при необходимости исполнения запроса, не возвращающего записи, необходимо использовать метод ExecuteNonQuery объекта Command, а при извлечении данных из БД - метод ExecuteReader, который, в свою очередь, возвращает объект DataReader, позволяющий просматривать полученные в результате запроса записи.

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

Объект Transaction позволяет осуществлять группировку записей в логическую единицу работы, называемой транзакцией. Транзакция логически объединяет несколько различных действий, связанных с манипулированием данными, в единое целое. В процессе выполнения действий, осуществляемых в рамках транзакции, СУБД обычно кэширует изменения, вносимые этими действиями в данные до момента завершения транзакции. Это позволяет производить отмену любых изменений, выполненных в рамках транзакции, в случае, если хотя бы одно из действий транзакции завершилось неудачно.

Объект Parameter позволяет вводить в запрос элемент, значение которого может быть задано непосредственно перед исполнением запроса. За счет этого отпадает необходимость каждый раз изменять текст самого запроса.

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

Объект DataTable позволяет просматривать данные в виде наборов записей и столбцов. Фактически, он представляет собой аналог таблицы БД, размещенный в памяти. Достоинством такой организации является возможность автономной работы с данными. Это означает, что после установления соединения с базой данных, чтения данных и заполнения ими объекта DataTable можно отключиться от источника данных и продолжать работать с ним в автономном режиме. Такая возможность чрезвычайно полезна при организации Web-приложений, которые должны быть хорошо масштабируемыми и ориентированы на многопользовательский режим работы. При этом, однако, возникают и побочные эффекты, один из которых связан с тем, что человек, работающий с автономным набором данных, не может увидеть изменений, вносимых в данные в этот момент другими пользователями.

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

Объект DataRow представляет собой строку объекта DataTable. Набор всех строк этого объекта представляет собой коллекцию Rows. DataRow очень часто используется для доступа к значению конкретного поля определенной записи. При этом применяется свойство Item.

Объект DataSet представляет собой отсоединенный набор данных, который может рассматриваться как контейнер для объектов DataTable. DataSet позволяет организовывать внутри себя структуру, полностью соответствующую реальной структуре таблиц и связей между ними в БД. Это удобно в том случае, когда при работе с базой данных необходимы данные из разных таблиц. В этом случае вместо того, чтобы многократно обращаться к серверу и выбирать данные из одной таблицы за раз, можно поместить все данные в объект DataSet, а затем передать его клиентскому приложению. DataSet является очень мощным инструментом для работы с отсоединенными наборами данных. Все изменения, которые вносятся в данные, хранящиеся в DataSet кэшируются в объектах DataRow. Когда возникает необходимость передачи изменений из DataSet в БД, возможно осуществить передачу только измененных данных, вместо того чтобы передавать все данные объекта, и это значительно снижает объем данных, передаваемых между клиентским компьютером и сервером.

Объект DataRelation представляет собой описание связей между таблицами реляционной базы данных. Он предоставляется объектом DataSet и позволяет организовывать взаимосвязи между таблицами отсоединенного набора данных объекта DataSet. Объект DataRelation выполняет функцию, аналогичную той, которую выполняют связи, определяемые в СУБД между таблицами при создании структур данных. Это касается и соблюдения принципов ссылочной целостности информации. Например, DataRelation можно настроить таким образом, чтобы изменения значения первичного ключа родительской таблицы автоматически передавались (каскадировались) дочерним записям, а при удалении записи в родительской таблице автоматически удалялись записи в дочерних таблицах, связанных с ней.

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

Более подробно использование всех объектов модели ADO.NET будет рассмотрено ниже.

Глава из книги Кэнту М. (Marco Cantu): Delphi 7. Для профессионалов
Авторы благодарят издательский дом ПИТЕР за предоставленные к публикации материалы.

Аннотация издательства

Среда Delphi была и до сих пор является наилучшим сочетанием объектно-ориентированного и визуального программирования не только для Windows, но теперь уже и для Linux и в ближайшем будущем - и для.NET. В этой книге автор попытался практически полностью исключить справочный материал, сконцентрировавшись на технологиях эффективного использования Delphi. В книге приведено более 300 примеров. Как сказал один из подписчиков групп новостей, «книги Кэнту - это по сути „delphi.filtered“, только больше и лучше». Книга предназначена для программистов, разработчиков и всех, серьезно интересующихся программированием в среде Delphi.

Технология ADO (глава 15)

С середины 1980-х годов программисты RDBMS пытаются найти «волшебный ключик» от двери, которая ведет в страну независимости от конкретной базы данных. Проблема состоит в том, что данные могут поступать из самых разных источников, каждый из которых обладает своей спецификой. Однако разработка приложений существенно упростилась бы, если бы удалось создать унифицированный механизм взаимодействия с самыми разными источниками данных. Это мог бы быть универсальный программный интерфейс API, который позволил бы программистам разрабатывать приложения, одинаковым образом взаимодействующие с различными источниками данных. Такие приложения можно было бы использовать для взаимодействия с самими разными системами RDBMS, а также с другими источниками данных. За истекшее время различными компаниями было предложено множество решений в этой области. Наиболее значительными являются Microsoft ODBC (Open Database Connectivity) и Borland IDAPI (Integrated Database Application Programming Interface). Технология Borland IDAPI больше известна под именем BDE (Borland Database Engine).
В середине 1990-х годов, с развитием и распространением технологии COM (Component Object Model), компания Microsoft объявила о постепенном переходе от ODBC к использованию новой технологии OLE DB. Однако OLE DB, по мнению самой компании Microsoft, является интерфейсом системного уровня, этот интерфейс должен использоваться системными программистами. Технология OLE DB является тяжеловесной, сложной и очень чувствительной к ошибкам. Она требует от программиста слишком многого. Работать с OLE DB слишком сложно. Чтобы облегчить работу с OLE DB, был создан дополнительный прикладной уровень, который получил название ADO (ActiveX Data Objects). Работать с ADO существенно проще, чем с OLE DB. Технология ADO предназначена для прикладных программистов.
В главе 14 уже говорилось о том, что компания Borland также решила заменить технологию BDE новой технологией под названием dbExpress. Следует отметить, что ADO по своим возможностям и идеологии в большей степени напоминает BDE. Как BDE, так и ADO поддерживают навигацию, манипулирование наборами данных, обработку транзакций, кэшируемые обновления (в ADO они называются batch updates (пакетные обновления)). Иными словами,концептуально и идеологически ADO и BDE являются похожими технологиями.

ПРИМЕЧАНИЕ

Я хотел бы поблагодарить Гая Смита Ферриера (Guy Smith Ferrier) за то, что он написал данную главу для книги Mastering Delphi 6 (Русское издание: Delphi 6. Для профессионалов. - СПб.: Питер, 2002. - Примеч. перев.). Гай - программист, автор книг и статей, кроме того, он выступает на конференциях. Он является автором нескольких коммерческих программных продуктов и многочисленных внутренних систем как для небольших, так и для крупных компаний. Он написал множество статей для журнала The Delphi Magazine, а также для других изданий. Кроме того, он неоднократно выступал на различных конференциях в Северной Америке и в Европе. Гай живет в Англии вместе с женой, сыном и кошкой.

В данной главе мы рассмотрим работу с ADO. Мы также рассмотрим dbGo - набор компонентов Delphi, который изначально назывался ADOExpress, однако в Delphi 6 был переименован, так как компания Microsoft противится использованию обозначения ADO в продуктах, разработанных сторонними производителями. В среде Delphi вы можете работать с ADO без помощи dbGo. Вы можете импортировать библиотеку типов ADO и получить прямой доступ к интерфейсам ADO. Именно так приходилось работать с ADO в Delphi до появления версии Delphi 5. Однако такой подход не позволяет вам воспользоваться преимуществами встроенной в Delphi инфраструктуры взаимодействия с базами данных. В частности, вы не сможете воспользоваться элементами управления, специально предназначенными для работы с данными, кроме того, для вас будет недоступной технология DataSnap. Во всех примерах данной главы для взаимодействия с ADO используется dbGo. Во-первых, dbGo входит в стандартный комплект поставки Delphi, во-вторых, dbGo является очень удобной технологией. Вне зависимости от того, будете ли вы использовать dbGo или откажетесь от использования этой технологии, материал данной главы будет для вас полезным.

ПРИМЕЧАНИЕ

Помимо dbGo вы можете использовать для взаимодействия с ADO множество других продуктов, разработанных сторонними производителями, например Adonis, AdoSlutio, Diamond ADO и Kamiak.

В данной главе рассматриваются следующие вопросы:

    Microsoft Data Access Components (MDAC);

  • файлы связи с данными (Data link files);

    получение информации о схеме;

    использование механизма Jet;

    обработка транзакций;

    отключенные и хранимые на диске наборы записей;

    модель портфеля и установка MDAC.

MDAC (Microsoft Data Access Components)

На самом деле ADO является частью более крупномасштабной технологии под названием Microsoft Data Access Components (MDAC). Термин MDAC является общим обозначением для всех разработанных компанией Microsoft технологий, связанных с БД. К этому набору относятся ADO, OLE DB, ODBC и RDS (Remote Data Services). Часто приходится слышать, что люди используют термины MDAC и ADO как синонимы, однако это неправильно. На самом деле ADO является лишь одной из частей MDAC. Когда мы говорим о версиях ADO, мы имеем в виду версии MDAC. К основным версиям MDAC относятся версии 1.5, 2.0, 2.1, 2.5 и 2.6. Компания Microsoft распространяет MDAC в виде отдельного продукта. Этот продукт может быть загружен с веб-узла Microsoft бесплатно. Мало того, фактически его можно бесплатно включать в состав ваших собственных продуктов (существуют определенные ограничения, однако большинство разработчиков Delphi без каких-либо проблем удовлетворяют всем этим требованиям). Кроме того, MDAC входит в комплект поставки большинства продуктов Microsoft, имеющих отношение к базам данных. В состав Delphi 7 входит версия MDAC 2.6.
Необходимо принять во внимание два важных обстоятельства. Во-первых, с большой долей уверенности можно сказать, что технология MDAC уже установлена на клиентских компьютерах ваших пользователей. Во-вторых, вне зависимости от версии MDAC, которая была установлена на клиентских компьютерах ваших пользователей, можно с уверенностью сказать, что эта версия рано или поздно будет обновлена до самой свежей (текущей) версии MDAC. Обновление может быть выполнено вами, вашими пользователями или одним из устанавливаемых в системе приложений Microsoft. Подобное обновление фактически невозможно предотвратить, так как MDAC устанавливается в составе такого широко распространенного приложения, как Internet Explorer. К этому следует добавить, что компания Microsoft поддерживает лишь самую последнюю версию MDAC, а также версию, предшествующую самой последней. Исходя из всего этого, можно прийти к выводу: ваше приложение должно работать с самым свежим выпуском MDAC или с предшествующей ему версией.
Как разработчик ADO, вы должны регулярно просматривать страницы веб-узла Microsoft, посвященные MDAC. Для этого следует обратиться по адресу www. microsoft.com/data. Здесь вы сможете бесплатно загрузить самую свежую версию MDAC. Также рекомендуется загрузить MDAC SDK (13 Мбайт), если у вас еще нет этого пакета. На самом деле MDAC SDK входит в состав Platform SDK, так что, если у вас есть Platform SDK, значит, вы уже обладаете MDAC SDK. Пусть пакет MDAC SDK станет вашей библией. Вы должны загрузить его и регулярно обращаться к нему для получения необходимых сведений и ответов на любые вопросы, связанные с ADO. Если вы нуждаетесь в информации, связанной с MDAC, прежде всего вы должны обратиться к MDAC SDK.

Провайдеры OLE DB

Провайдеры OLE DB обеспечивают доступ к источникам данных. В dbExpress для этой цели используются драйверы, а в BDE - связи SQL Links. В процессе установки MDAC в системе автоматически устанавливаются провайдеры OLE DB, перечисленные в табл. 15.1.

Таблица 15.1. Провайдеры OLE DB, входящие в состав MDAC

Драйвер

Провайдер

Описание

Драйверы ODBC (по умолчанию)

Microsoft.Jet.OLEDB.3.5

Только базы данных MS Access 97

Microsoft.Jet.OLEDB.4.0

Базы данных MS Access и другие БД

Базы данных MS SQL Server

Базы данных Oracle

Online Analytical Processing

Пример провайдера OLE DB для файлов CSV

Для создания ваших собственных провайдеров для простых текстовых данных

Вот перечень этих провайдеров.

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

    Jet OLE DB - поддержка MS Access и других локальных баз данных. Мы вернемся к рассмотрению этих провайдеров далее.

    SQL Server обеспечивает взаимодействие с SQL Server 7, SQL Server 2000 и Microsoft Database Engine (MSDE). MSDE - это упрощенная версия SQL Server, в которой отсутствует большинство инструментов, а кроме того, добавлен специальный код, который намеренно снижает производительность в случае, если к базе данных одновременно подключаются более пяти пользователей. К преимуществам MSDE следует отнести то, что этот механизм распространяется бесплатно и полностью совместим с SQL Server.

    OLE DB для OLAP может использоваться напрямую, однако чаще обращение к нему осуществляется через ADO Multi-Dimentional (ADOMD). ADOMD - это дополнительная технология ADO, специально разработанная для Online Analytical Processing (OLAP). Если ранее вы работали с Delphi Decision Cube, Excel Pivot Tables или Access Cross Tabs, значит, вы работали с одной из форм OLAP. Помимо уже перечисленных здесь провайдеров, компания Microsoft осуществляет поддержку некоторых других провайдеров OLE DB, которые входят в состав других продуктов или в состав SDK.

    Active Directory Services OLE DB входит в состав ADSI SDK; AS/400 OLE DB и VSAM OLE DB входят в состав SNA Server; Exchange OLE DB входит в состав Microsoft Exchange 2000.

    Indexing Service OLE DB входит в состав Microsoft Indexing Service - внутренний механизм Windows, ускоряющий поиск информации в файлах при помощи построения каталога с файловой информацией. Служба индексирования Indexing Service интегрирована в IIS и часто используется для индексирования веб-узлов.

    Internet Publishing OLE DB позволяет разработчикам манипулировать каталогами и файлами с использованием HTTP.

    Существует также категория провайдеров OLE DB, которые называются провайдерами обслуживания (service providers). Как следует из имени, эти провайдеры обеспечивают обслуживание других провайдеров OLE DB и зачастую активизируются автоматически без участия программиста. Например, служба Cursor Service активизируется в случае, если вы создаете курсор на стороне клиента, а провайдер Persistent Recordset активизируется в случае, если вы собираетесь сохранить данные на локальном диске.

Помимо перечисленных, существует также огромное количество других провайдеров OLE DB для MDAC. Провайдеры OLE DB можно получить как от Microsoft, так и от независимых производителей. Список провайдеров OLE DB очень большой и постоянно меняется, поэтому его невозможно воспроизвести в данной книге. Кроме независимых производителей поставку и поддержку провайдеров OLE DB осуществляют многие производители систем RDBMS. Например, компания Oracle поддерживает собственный провайдер OLE DB под названием ORAOLEDB.

Вы уже, наверное, обратили внимание на то, что в списке отсутствует провайдер OLE DB для InterBase. Во-первых, вы можете воспользоваться драйвером ODBC, во-вторых, вы можете использовать провайдер IBProvider, разработанный Дмитрием Коваленко (www.lipetsk.ru/prog/eng/index.html). Наконец, вы можете попробовать разработать провайдер самостоятельно. Для этого удобно использовать комплект OLE DB Provider Development Toolkit, разработанный Бинхом Ли (Binh Ly) и доступный по адресу http://www.techvanguards.com/products/optk/install.htm .

Использование компонентов dbGo

Программисты, уже знакомые с BDE, dbExpess или IBExpress, без труда узнают компоненты, входящие в состав dbGo (табл. 15.2).

Таблица 15.2. Компоненты dbGo

Компонент dbGo

Описание

Эквивалент из комплекта BDE

Подключение к базе данных

База данных

Исполняет команду SQL

Нет эквивалента

Многоцелевой наследник TDataSet

Нет эквивалента

Инкапсулирует таблицу

Инкапсулирует SQL SELECT

Инкапсулирует сохраненную процедуру (stored procedure)

Подключение Remote Data Services

Нет эквивалента

Четыре компонента наборов данных (ADODataSet, ADOTable, ADOQuery и ADOStoredProc) фактически полностью реализованы общим для них базовым классом TCustomADODataSet. Этот компонент несет ответственность за выполнение большинства функций, присущих набору данных. Производные компоненты являются тонкими оболочками, которые делают доступными для внешнего мира те или иные возможности базового компонента. Таким образом, компоненты обладают множеством общих черт. Компоненты ADOTable, ADOQuery и ADOStoredProc предназначены для упрощения адаптации кода, ориентированного на BDE. Однако следует иметь в виду, что эти компоненты нельзя считать полностью идентичными эквивалентами аналогичных компонентов BDE. Различия обязательно проявят себя при разработке фактически любого приложения за исключением, может быть, самых тривиальных. В качестве основного компонента при разработке новых программ следует считать компонент ADODataSet, так как, во-первых, этот компонент является достаточно удобным, а во-вторых, его интерфейс сходен с интерфейсом ADO Recordset. В данной главе я продемонстрирую использование каждого из упомянутых компонентов.

Практический пример

Хватит теории, давайте перейдем к делу. Разместим на форме компонент ADOTable. Для индикации базы данных, к которой следует подключиться, в рамках ADO используются строки подключения (connection strings). Если вы знаете, что делаете, вы можете набрать строку подключения вручную. Однако в общем случае для создания строки подключения рекомендуется использовать специальный редактор (редактор свойства ConnectionString), рабочее окно которого показано на рис. 15.1.

Щелкните на Build (Сформировать), чтобы запустить разработанный компанией Microsoft редактор строк подключения. Его рабочее окно показано на рис. 15.2. Давайте рассмотрим этот инструмент подробнее, так как он является важным средством при работе с ADO. На первой вкладке показаны провайдеры OLE DB и провайдеры обслуживания, установленные на вашем компьютере. Перечень провайдеров может быть разным для разных версий MDAC, кроме того, новые провайдеры могут появиться в списке в результате установки на компьютере новых прикладных программ. Вернемся к нашему примеру. Выберите провайдер Jet 4.0 OLE DB - для этого сделайте двойной щелчок на надписи Jet 4.0 OLE DB Provider, на экране появится вкладка Connection (Подключение). Внешний вид этой страницы для разных провайдеров может быть разным. Для провайдера Jet редактор предложит вам ввести имя базы данных и аутентификационные данные. Вы можете выбрать MDB-файл базы данных Access, входящий в комплект поставки Delphi (например, C:\Program Files\Common Files\Borland Shared\Data\dbdemos.mdb). Щелкните на кнопке Test Connection (Протестировать соединение) для того, чтобы убедиться в правильности вашего выбора.
На вкладке Advanced (Дополнительно) вы можете контролировать режим доступа к базе данных. Здесь вы можете настроить эксклюзивный доступ или доступ только для чтения. На вкладке All (Все) перечисляются все параметры строки подключения. Этот список может быть разным для разных провайдеров OLE DB. Хорошо запомните эту страницу, так как с ее помощью можно решить множество разнообразных проблем. Закрыв редактор Microsoft, вы вернетесь к редуктору строк подключения Borland. В рабочем окне этого редактора будет показана строка, которая будет присвоена ConnectionString (здесь я разделил ее на несколько строчек, чтобы удобнее было читать):

Provider=Microsoft.Jet.OLEDB.4.0; Data Source=C:\Program Files\Common Files\Borland Shared\Data\dbdemos.mdb; Persist Security Info=False


Строка подключения - это обычная строка символов, в которой через точку с запятой перечисляются параметры и их значения. Такую строку можно редактировать вручную. Параметры и их значения можно перенастраивать в процессе выполнения программы, для этого вы должны написать собственный набор подпрограмм для выполнения поиска параметра в списке и внесения изменения в его значение. Существует также более простой способ: вы можете скопировать строку в список строк Delphi и воспользоваться механизмом обработки пар «имя-значение ». Этот прием будет продемонстрирован в примере JetText, о котором будет рассказано далее в разделе «Доступ к текстовым файлам через Jet» .
После того как вы сформировали строку подключения, вы можете выбрать таблицу. Раскройте список таблиц при помощи свойства TableName в окне Object Inspector. Выберите таблицу Customer. Добавьте компонент DataSource и элемент управления DBGrid, а затем соедините их вместе. В результате получилась реальная, хотя и примитивная программа, использующая ADO (полный исходный код оформлен в виде примера FirstAdoExample). Чтобы увидеть данные, занесите в свойство Active набора данных значение True или откройте набор данных внутри обработчика события FormCreate (как это сделано в примере). Второй способ позволяет избежать проблем, если на этапе проектирования база данных недоступна.

Если вы планируете использовать dbGo в качестве основной технологии доступа к БД, вам наверняка захочется переместить компонент DataSource на страницу ADO палитры компонентов, чтобы не перескакивать постоянно со страницы на страницу. Если вы используете ADO в комбинации с другой технологией, вы можете имитировать установку DataSource на нескольких страницах. Для этого необходимо создать шаблон (Component Template) компонента DataSource и поместить его на страницу ADO.

Компонент ADOConnection

Когда вы используете компонент ADOTable, он создает свой собственный компонент соединения с БД у вас за спиной. Однако вы вовсе не обязаны использовать именно это соединение. В общем случае вы должны создать свое собственное соединение при помощи компонента ADOConnection, который по сути является эквивалентом компонента SQLConnection из библиотеки dbExpress и компонента Database из библиотеки BDE. Компонент ADOConnection позволяет вам должным образом настроить процедуру аутентификации, контролировать транзакции, напрямую выполнять команды, адресованные БД, кроме того, он позволяет сократить количество подключений, существующих в рамках приложения.
Использовать ADOConnection достаточно просто. Разместите этот компонент на форме и настройте его свойство ConnectionString таким же образом, как вы делали это для компонента ADOTable. Кроме того, вы можете сделать двойной щелчок на компоненте ADOConnection (или выбрать пункт Component Editor в контекстном меню) для того, чтобы напрямую обратиться к редактору строки подключения. Если строка подключения (ConnectionString) указывает на необходимую вам базу данных, вы можете отключить диалоговое окно подключения к БД, для этого необходимо присвоить свойству LoginPrompt значение False. Чтобы в предыдущем примере воспользоваться новым соединением, присвойте значение ADOConnection1 свойству Connection компонента ADOTable1. Вы увидите, что значение свойства ConnectionString станет пустым, так как свойства Connection и ConnectionString исключают друг друга. Преимущество использования ADOConnection состоит в том, что строка подключения теперь хранится в одном месте, вместо того чтобы храниться в нескольких разных компонентах. Еще одно более важное преимущество заключается в том, что несколько разных компонентов могут использовать одно и то же соединение с сервером базы данных. Если вы не добавите в программу вручную сделанный вами компонент ADOConnection, каждый компонент ADO будет обладать собственным соединением с сервером.

Файлы связи с данными (Data Link Files)

Итак, компонент ADOConnection позволяет вам централизовать определение строки подключения в рамках формы или модуля данных. Однако у описанного подхода по-прежнему имеется один существенный недостаток: если вы идентифицируете базу данных при помощи некоторого имени файла, путь к этой базе будет жестко закодирован внутри исполняемого файла приложения. В результате возможности приложения будут существенно ограничены. Чтобы решить проблему, в ADO используются так называемые файлы связи с данными (Data Link Files). Файл связи с данными - это строка подключения, оформленная в виде INIфайла. Например, в рамках Delphi устанавливается файл dbdems.udl, в котором содержится следующий текст:

Файл связи с данными может обладать любым расширением, однако рекомендуется использовать расширение.UDL. Вы можете создать такой файл при помощи любого текстового редактора. Кроме того, чтобы создать такой файл, вы можете открыть окно проводника Windows, правой кнопкой мыши щелкнуть в одной из папок диска, выбрать New > Text Document (Создать > Текстовый документ), сменить расширение файла на.UDL (я предполагаю, что в вашей системе проводник отображает расширения файлов), затем сделать двойной щелчок на файле - в результате будет запущен редактор строки подключения Microsoft.
Если в редакторе свойства ConnectionString вы выберете Use Data Link File (Использовать файл связи с данными), в этом свойстве будет автоматически размещена строка "FILE NAME =", за которой будет указано имя файла связи с данными. Такой прием продемонстрирован в примере DataLinkFile. Файлы связи с данными можно разместить в любом месте диска, однако ADO использует для хранения таких файлов некоторый стандартный каталог. Узнать имя этого каталога можно при помощи функции DataLinkDir, которая определяется в модуле ADODB. Если конфигурация - по умолчанию используемая в MDAC, значит, эта функция вернет следующее:

C:\Program Files\Common Files\System\OLE DB\Data Links

Динамические свойства

Представьте, что вы занимаетесь разработкой среднего звена, расположенного между клиентами и несколькими базами данных. С одной стороны, вы должны сформировать единый унифицированный программный интерфейс для доступа к нескольким разным базам данных, с другой стороны, этот интерфейс должен обеспечивать доступ к специфическим возможностям каждой из баз данных. Чтобы решить обе эти задачи, вы можете разработать тяжеловесный интерфейс, который будет представлять собой сумму возможностей всех баз данных, для взаимодействия с которыми он предназначен. Каждый класс такого интерфейса должен включать в себя все возможные свойства и методы, однако для работы с конкретной БД можно будет использовать лишь подмножество свойств и методов класса. Надеюсь, не стоит доказывать вам, что это решение не является самым лучшим. Для решения проблемы в ADO используются динамические свойства (dynamic properties). Фактически все интерфейсы ADO, равно как и соответствующие им компоненты dbGo, обладают свойством под названием Properties. Это свойство является коллекцией свойств, специфичных для текущей базы данных. К этим свойствам можно обратиться, указав их порядковый номер, например:

ShowMessage(ADOTable1.Properties.Value);

Однако в большинстве случаев удобнее использовать имя: ShowMessage(ADOConnection1.Properties["DBMS Name"].Value); Набор динамических свойств определяется типом объекта и провайдером OLE DB. Чтобы вы получили представление о важности динамических свойств, я замечу, что такие компоненты, как ADOConnection или Recordset, поддерживают приблизительно 100 динамических свойств. Как будет показано в данной главе, динамические свойства активно используются в ADO для решения множества разнообразных задач.

Важным событием, имеющим отношение к использованию динамических свойств, является событие OnRecordsetCreate. Впервые это событие появилось в Delphi 6. Событие OnRecordsetCreate генерируется сразу же после создания Recordset, но перед тем как этот компонент будет открыт. Это событие полезно использовать для настройки тех динамических свойств, которые могут быть настроены только тогда, когда набор записей (Recordset) находится в закрытом состоянии.

Получение информации о схеме

В ADO для получения информации о схеме используется метод OpenSchema компонента ADOConnection. Этот метод принимает четыре параметра:

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

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

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

    Компонент ADODataSet, в составе которого будут возвращены данные. Этот параметр иллюстрирует распространенную в рамках ADO тему: если метод возвращает некоторое количество данных, он заносит эти данные в Recordset или, в терминологии Delphi, - в компонент ADODataSet.

Чтобы воспользоваться методом OpenSchema, вы должны открыть ADOConnection. Следующий код, который является частью примера OpenSchema, извлекает список первичных ключей для каждой таблицы и заносит их в компонент ADODataSet:

ADOConnection1.OpenSchema(siPrimaryKeys, EmptyParam, EmptyParam, ADODataSet1);

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


Если в качестве второго параметра вы передаете значение EmptyParam, в состав результирующего набора данных включается вся информация указанного типа для всей базы данных. Очень часто для удобства вы хотите выполнить фильтрацию информации. Конечно же, для этой цели можно применить к результирующему набору данных традиционный фильтр Delphi (для этого можно использовать свойства Filter и Filtered или событие OnFilterRecord). Однако в этом случае фильтрация будет выполняться на стороне клиента. Второй параметр позволяет выполнить фильтрацию более эффективно на стороне источника информации о схеме. Фильтр определяется как массив значений. Каждый элемент массива обладает специальным смыслом, имеющим отношение к типу возвращаемых данных. Например, массив фильтров для первичных ключей включает в себя три элемента: каталог (то есть базу данных), схему и имя таблицы. Этот пример возвращает перечень первичных ключей в таблице Customer:

var Filter: OLEVAriant; begin Filter:= VarArrayCreate(, varVariant); Filter := "CUSTOMER"; ADOConnection1.OpenSchema(siPrimaryKeys, Filter, EmptyParam, ADODataSet1); end ;

ПРИМЕЧАНИЕ

Ту же информацию можно получить при помощи ADOX. ADOX - это дополнительная технология ADO, которая позволяет вам получать и изменять информацию о схеме. В SQL эквивалентом ADOX является язык DDL (Data Definition Language), то есть выражения CREATE, ALTER, DROP и DCL (Data Control Language), то есть выражения GRANT, REVOKE. В рамках dbGo технология ADOX напрямую не поддерживается, однако вы можете импортировать библиотеку типов ADOX и использовать ее в приложениях Delphi. В отличие от метода OpenSchema, реализация ADOX в Delphi не универсальна, поэтому использовать ее не всегда удобно. Если вы хотите просто получить информацию о схеме, но не изменять ее, для этой цели, как правило, удобнее использовать метод OpenSchema.

Использование механизма Jet

Теперь, когда вы получили базовое представление об MDAC и ADO, мы можем перейти к рассмотрению механизма Jet. Для одних этот механизм представляет интерес, другим он совершенно не нужен. Если вы имеете дело с Access, Paradox, dBase, Excel, Lotus 1-2-3, HTML или данными, хранящимися в текстовых файлах, значит, рассматриваемый здесь материал будет для вас полезным. Если вы не заинтересованы в перечисленных здесь форматах, вы можете пропустить весь этот раздел.
Как правило, механизм Jet ассоциируется с базами данных Microsoft Access. Действительно, Access является основной системой, с которой взаимодействует Jet. Однако помимо Access механизм Jet позволяет работать с множеством других локальных источников данных. Многие не подозревают об этом, однако именно в этом заключается одно из основных преимуществ Jet. Взаимодействие с Access через Jet в стандартном режиме работы этого механизма выполняется относительно просто, поэтому здесь мы не будем рассматривать этот режим использования Jet. Вместо этого мы подробно рассмотрим взаимодействие Jet с другими форматами.

ПРИМЕЧАНИЕ

Механизм Jet входит в состав некоторых (но не всех) версий MDAC. В частности, он отсутствует в версии 2.6. В свое время было много споров относительно того, могут ли программисты, использующие средства разработки, не принадлежащие Microsoft, включать в комплект поставки своих программных продуктов механизм Jet. Официально считается, что такое возможно. Механизм Jet можно загрузить бесплатно с веб-узла компании Microsoft, кроме того, этот механизм входит в комплект поставки многих продуктов компании Microsoft.

Существует два провайдера OLE DB для механизма Jet: Jet 3.51 OLE DB и Jet 4.0 OLE DB. Провайдер Jet 3.51 OLE DB использует Jet 3.51 и поддерживает работу только с Access 97. Если вы будете применять только Access 97 и не собираетесь переходить на Access 2000, то Jet 3.51 в большинстве случаев даст более высокую производительность по сравнению с провайдером Jet 4.0 OLE DB.
Провайдер Jet 4.0 OLE DB поддерживает работу с Access 97, Access 2000 и с драйверами IISAM (Installable Indexed Sequential Access Method). Устанавливаемые драйверы ISAM специально написаны для механизма Jet и обеспечивают доступ к таким форматам, как Paradox, dBase и текстовые файлы. Именно возможность использования этих драйверов делает Jet полезным и удобным инструментом. Полный список драйверов ISAM, установленных на вашем компьютере, определяется набором установленного в системе программного обеспечения. Этот список располагается в реестре по адресу:

HKEY_LOCAL_MACHINE\Software\Microsoft\Jet\4.0\ISAM Formats

В состав комплекта поставки Jet входят драйверы для Paradox, dBase, Excel, текстовых файлов и HTML.

Импорт и экспорт

Механизм Jet удобно использовать для импорта и экспорта данных. Процесс экспортирования данных одинаков для каждого экспортированного формата и состоит из исполнения выражения SELECT в специальном формате. Рассмотрим пример экспортирования данных из базы данных Access в примере DBDemos в таблицу Paradox. Для этого добавим в программу JetImportExport активное соединение ADOConnection с названием ADOConnection1. Это соединение использует механизм Jet для того, чтобы открыть базу данных. Следующий код экспортирует таблицу Customer в файл Customer.db формата Paradox:

SELECT * INTO Customer IN "C:\tmp" "Paradox 7.x;" FROM CUSTOMER

Рассмотрим составные части этого SQL-выражения. После ключевого слова INTO указывается новая таблица, которая будет создана в результате выполнения оператора SELECT. До выполнения этого кода таблица с этим именем должна отсутствовать в базе. После ключевого слова IN указывается база данных, в которую добавляется новая таблица. В Paradox это должен быть каталог, который уже существует на диске. Сразу же после имени базы данных указывается имя драйвера IISAM, который будет использоваться для экспорта данных. В конце имени драйвера обязательно нужно добавить символ точки с запятой (;). Ключевое слово FROM является стандартным компонентом любого выражения SELECT. В рассматриваемом примере эта операция выполняется при помощи компонента ADOConnection1, вместо фиксированного имени каталога используется текущий каталог программы:

Новый файл Excel с именем dbdemos.xls создается в текущем каталоге программы. В этот документ Excel добавляется рабочая книга с именем Customer, в которую заносятся все данные из таблицы Customer базы данных Access с именем dbdemo. mdb.
Вот еще одно выражение, которое экспортирует те же самые данные в HTMLфайл:

ADOConnection1.Execute ("SELECT * INTO IN "" + CurrentFolder + "" "HTML Export;" FROM CUSTOMER");

В данном случае база данных - это каталог (как и в Paradox). Имя таблицы включает в себя расширение.htm, поэтому имя таблицы необходимо заключить в квадратные скобки. Обратите внимание, что драйвер IISAM называется не просто HTML, а HTML Export. Как следует из названия, драйвер позволяет только экспортировать данные, но не позволяет импортировать их.
Наконец, давайте рассмотрим входящий в состав Jet драйвер HTML Import, который является полезным дополнением к HTML Export. Добавьте на форму компонент ADOTable. Настройте строку подключения ConnectionString на использование провайдера Jet 4.0 OLE DB. Присвойте параметру Extended Properties строки подключения значение HTML Import. В качестве имени базы данных укажите имя HTML-файла, который был создан в результате экспорта (чуть ранее), точнее говоря, Customer.htm. Теперь присвойте свойству TableName значение Customer. Откройте таблицу - вы только что импортировали данные из HTML-файла. Имейте в виду, что если вы попытаетесь обновить данные, система выдаст ошибку, так как драйвер предназначен только для импорта. Если вы создали собственный HTMLфайл, в котором содержатся таблицы, и хотите открыть эти таблицы с использованием данного драйвера, вы должны помнить, что имя таблицы - это значение тега caption в HTML-разделе table.

Работа с курсорами

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

Положение курсора (свойство CursorLocation)

Свойство CursorLocation определяет, каким образом осуществляется извлечение и модификация данных. Этому свойству можно присвоить одно из двух значений: clUseClient (курсор на стороне клиента) или clUseServer (курсор на стороне сервера). Выбор значения в большой степени влияет на функциональность, производительность и масштабируемость базы данных.
Клиентский курсор обслуживается механизмом ADO Cursor Engine. Этот механизм является превосходным примером провайдера обслуживания OLE DB: он обеспечивает обслуживание для других провайдеров OLDE DB. Механизм ADO Cursor Engine управляет обработкой данных на стороне клиента. При открытии набора данных все данные результирующего набора перекачиваются с сервера на клиентский компьютер. После этого данные хранятся в памяти, их обновление и обработка осуществляется с использованием ADO Cursor Engine. Этот подход напоминает использование ClientDataSet в приложениях dbExpress. Преимущество состоит в том, что после передачи данных на сторону клиента любые манипуляции с этими данными выполняются значительно быстрее. Кроме того, так как манипуляции выполняются в памяти, механизм ADO Cursor Engine обладает более широкими возможностями, чем любой из курсоров, работающих на стороне сервера. Далее я подробнее рассмотрю эти преимущества, а также другие технологии, основанные на клиентских курсорах (в частности, отключенные и постоянные наборы записей). Курсор на стороне сервера управляется самой системой RDBMS. В клиент-серверной архитектуре, основанной на таких продуктах, как SQL Server, Oracle или InterBase, это означает, что управление курсором осуществляется на удаленном серверном компьютере. Если речь идет о настольной базе данных, такой как Access или Paradox, серверный курсор управляется программным продуктом, обслуживающим базу данных. То есть логически курсор расположен на «сервере», однако физически база данных вместе с курсором располагается на клиентском компьютере. Как правило, серверные курсоры загружаются быстрее, чем клиентские курсоры, так как при открытии набора данных с серверным курсором нет необходимости перемещать все данные на сторону клиента. Благодаря этому серверные курсоры лучше подходят для обслуживания больших наборов данных, то есть тогда, когда клиентский компьютер не обладает объемом памяти, достаточным для хранения всего набора данных. Чтобы понять возможности курсоров обоих типов, лучше всего посмотреть, как они функционируют в той или иной ситуации. Например, можно взять ситуацию блокирования записей. Чуть позднее я более подробно расскажу о блокировании. (Если вы хотите заблокировать запись, вам потребуется серверный курсор, так как система RDBMS должна знать о том, что запись заблокирована.)
Еще одной характеристикой, на которую следует обратить внимание при выборе местоположения курсора, является масштабируемость. Серверные курсоры располагаются на стороне сервера. Чем больше пользователей подключается к базе, тем больше курсоров создается на сервере. С каждым новым курсором нагрузка на сервер возрастает. Таким образом, при увеличении количества пользователей общая производительность системы может существенно понизиться. Используя курсоры на стороне клиента, вы можете существенно повысить масштабируемость вашего приложения. Открытие клиентского курсора обойдется вам дороже, так как в процессе открытия все данные передаются на сторону клиента, однако обслуживание клиентского курсора менее обременительно для сервера, ведь основная связанная с этим нагрузка возлагается на клиентский компьютер.

Тип курсора (свойство CursorType)

Тип курсора во многом определяется местом расположения курсора. Существует пять типов курсоров, один из которых не используется. Неиспользуемый тип называется unspecified (неуказанный). В ADO существует много значений, которые соответствуют неуказанному значению. В Delphi эти значения фактически никогда не используются. Эти значения присутствуют в Delphi только потому, что они присутствуют в ADO. Дело в том, что технология ADO изначально разрабатывалась для таких языков, как Visual Basic и C. В этих языках вы работаете с объектами напрямую, без поддержки вспомогательных механизмов, таких как dbGo. В результате вы можете создать открытый набор записей (в терминологии ADO - recordset), не указывая при этом значения для каждого из свойств. Таким образом, значения некоторых свойств будут не определены. В этом случае свойству присваивается значение unspecified (не указано). Однако в рамках dbGo вы имеете дело с компонентами. Компоненты обладают конструкторами. Конструктор - это функция, которая в обязательном порядке инициализирует каждое из свойств компонента. Когда вы создаете компонент dbGo, каждое из его свойств обладает определенным значением. В итоге отпадает надобность в использовании значения unspecified (не указано).
Тип курсора влияет на то, каким образом происходит чтение и обновление данных. Можно использовать один из четырех типов курсора: Forward-Only (только вперед), Static (статический), Keyset (набор ключей) и Dynamic (динамический). Прежде чем переходить к обсуждению разнообразных комбинаций типов и местоположения курсора, отмечу одно важное обстоятельство: для курсоров, работающих на стороне клиента, можно использовать только один тип: статический курсор. Все остальные типы курсоров могут использоваться только на стороне сервера. Давайте подробнее рассмотрим типы курсоров в порядке возрастания затрат, связанных с их обслуживанием.

    Forward-only (только вперед). Курсоры этого типа обходятся дешевле всего в смысле затрат. Иными словами, такие курсоры обеспечивают самую высокую производительность. Как следует из имени, курсор Forward-only (только вперед) позволяет вам перемещаться по набору данных в направлении от начала к концу. Курсор читает с сервера количество записей, указанное в свойстве CacheSize (по умолчанию 1), каждый раз, как только он покидает последнюю запись в локальном кэше, он читает с сервера следующую порцию записей. Любая попытка переместиться по направлению к началу набора записей за пределы локального кэша приводит к возникновению ошибки. Это поведение напоминает поведение набора данных в библиотеке dbExpress. Курсор Forwardonly (только вперед) плохо подходит для формирования пользовательского интерфейса, в котором пользователь обладает возможностью контролировать направление перемещения. Вместе с тем, такой курсор вполне подходит для выполнения пакетных операций, формирования отчетов, при построении вебприложений, не сохраняющих информацию о состоянии, - в любой из этих ситуаций вы начинаете с начала набора данных и перемещаетесь по направлению к концу набора данных. По достижении конца набор данных закрывается.

    Static (статический). При использовании статического курсора набор данных полностью перемещается на сторону клиента, обращение к нему осуществляется при помощи окна размером CacheSize. В результате пользователь получает возможность перемещаться по набору данных в обоих направлениях. Недостаток заключается в том, что данные являются статическими - обновления, добавления и удаления записей, выполняемые другими пользователями, не видны для статического курсора, так как данные курсора уже прочитаны.

    Keyset (набор ключей). Чтобы понять принцип функционирования этого курсора, разделите слово Keyset на две части: key и set. Key - это ключ, то есть в данном контексте - идентификатор записи. Зачастую имеется в виду первичный ключ. Set - это множество или набор. Получается «набор ключей». При открытии набора данных с сервера читается полный список всех ключей. Например, если набор данных формируется при помощи выражения SELECT * FROM CUSTOMER, значит, список ключей можно сформировать при помощи выражения SELECT CUSTID FROM CUSTOMER. Набор ключей хранится на стороне клиента до закрытия курсора. Когда приложение нуждается в данных, провайдер OLE DB читает строки таблицы, используя для этой цели имеющийся у него набор ключей. В результате клиент всегда имеет дело с обновленными данными. Однако набор ключей является статическим в том смысле, что после открытия курсора в этот набор нельзя добавить новые ключи, также ключи нельзя удалить из набора. Иными словами, если другой пользователь добавляет в таблицу новые записи, эти изменения не будут видны для клиента. Удаленные записи становятся недоступными, а любые изменения в первичных ключах (как правило, пользователям запрещается менять первичные ключи) также становятся недоступными.

    Dynamic (динамический). Это наиболее дорогостоящий курсор. Динамический курсор функционирует приблизительно так же, как курсор набора ключей. Разница заключается в том, что набор ключей заново читается с сервера каждый раз, когда приложение нуждается в данных, отсутствующих в кэше. Так как значение свойства ADODataSet.CacheSize по умолчанию равно 1, запросы на чтение данных возникают достаточно часто. Можно себе представить дополнительную нагрузку, которую данный курсор создает на сервер DBMS и на сеть. Однако при использовании этого курсора клиент знает не только об изменениях данных, но и о добавлениях и удалениях, выполняемых другими клиентами.

Вы не всегда получаете то, о чем просите

Теперь, когда вы знаете о типах и местоположении курсора, я должен предупредить вас о том, что допускается использование далеко не всех комбинаций типов и местоположений курсора. Как правило, это ограничение связано с типом RDBMS и/или провайдером OLE DB. Например, если курсор располагается на стороне клиента, тип курсора может быть только статическим. Вы можете понаблюдать подобное поведение самостоятельно. Добавьте на форму компонент ADODataSet, настройте свойство ConnectionString для подключения к любой базе данных, после этого присвойте свойству ClientLocation значение clUseCursor, а свойству CursorType - значение ctDynamic. Теперь измените значение свойства Active на True и понаблюдайте за свойством CursorType. Значение этого свойства немедленно изменится на ctStatic. Следует сделать важный вывод: вы далеко не всегда получаете именно то, о чем просите. Открыв набор данных, всегда проверяйте значения свойств - некоторые из них могут самопроизвольно изменить свои значения. Для различных провайдеров OLE DB характерны разные изменения свойств. Приведу лишь несколько примеров:

    провайдер Jet 4.0 OLE DB изменяет большинство типов курсоров на Keyset (набор ключей);

    провайдер SQL Server OLE DB часто меняет Keyset (набор ключей) и Static (статический) на Dynamic (динамический);

    провайдер Oracle OLE DB меняет все типы курсоров на Forward-only (только вперед);

    провайдер ODBC OLE DB может выполнить самые разные изменения типа курсора в зависимости от используемого драйвера ODBC.

Отсутствие счетчика

Когда вы пытаетесь прочитать свойство RecordCount какого-либо набора данных ADO, иногда вы обнаруживаете, что это свойство равно –1. Курсор типа Forwardonly не знает, какое количество записей входит в состав набора данных, пока он не достигнет конца набора. По этой причине свойство RecordCount равно значению – 1. Статический курсор всегда знает, какое количество записей входит в набор данных, так как статический курсор читает все данные набора в момент открытия. Курсор типа Keyset (набор ключей) тоже знает количество записей в наборе, так как в момент открытия набора данных он извлекает из базы данных фиксированный набор ключевых значений. Таким образом, для курсоров Static и Keyset вы можете обратиться к свойству RecordCount и получить точное количество записей в наборе. Динамический курсор не может достоверно знать количество записей, так как каждый раз при чтении данных он заново читает набор ключей, поэтому свойство RecordCount для этого курсора всегда равно –1. Вы можете вообще отказаться от использования свойства RecordCount и вместо этого использовать выражение SELECT COUNT(*) FROM имя_таблицы. Однако в результате вы получите неточное значение количества записей в таблице базы данных - это значение далеко не всегда совпадает с количеством записей в наборе данных.

Клиентские индексы

Одним из преимуществ курсоров, работающих на стороне клиента, является возможность создания локальных, или клиентских, индексов. Представьте, что у вас есть набор данных ADO с клиентским курсором и что этот набор соединен с таблицей Customer из примера DBDemos. Представьте, что к этому набору подключена сетка DBGrid. Присвойте свойству IndexFieldNames значение CompanyName. Сетка немедленно отобразит записи, упорядочив их в соответствии со значением поля CompanyName. Важно отметить, что для формирования индекса ADO не читает заново данные из источника. Индекс формируется на основе данных, хранящихся в памяти. Благодаря этому, во-первых, индекс формируется достаточно быстро, во-вторых, не создается никакой дополнительной нагрузки на сеть и DBMS. В противном случае одни и те же данные пришлось бы раз за разом передавать через сеть в различном порядке сортировки.
Свойство IndexFieldNames обладает еще кое-какими интересными возможностями. Например, присвойте этому свойству значение Country;CompanyName - вы увидите, что записи сначала отсортированы в соответствии с именем страны, а затем - в соответствии с именем компании. Теперь присвойте свойству IndexField- Names значение CompanyName DESC (ключевое слово DESC должно быть написано заглавными буквами, но не desc или Desc). В результате записи будут отсортированы в порядке убывания значений.
Эта простая, но весьма мощная возможность позволяет вам решить одну из наиболее актуальных проблем, связанных с программированием БД. Пользователи любят задавать неизбежный и неприятный для программистов, но совершенно оправданный вопрос: «Могу ли я щелкнуть на заголовке столбца сетки для того, чтобы отсортировать мои данные?» Существует несколько способов решения этой проблемы. Например, вы можете воспользоваться стандартным (не поддерживающим работу с данными) элементом управления, таким как ListView, который поддерживает встроенный механизм сортировки. Кроме того, вы можете выполнить обработку события OnTitleClick компонента DBGrid и в рамках обработчика заново исполнять SQL-выражение SELECT, добавляя к нему подходящую команду ORDER BY. Однако любое из этих решений нельзя назвать в полной мере удовлетворительным. Если данные кэшируются на стороне клиента (мы уже обсуждали этот подход, когда говорили о компоненте ClientDataSet), вы можете воспользоваться индексом, сформированным в памяти клиентского компьютера. Добавьте следующий обработчик события OnTitleClick для сетки (полный исходный код входит в состав примера ClientIndexes):

procedure Tfrom1.DBGrid1TitleClick(Column: Tcolumn); begin if ADODataSet1.IndexFieldNames = Column.Field.FieldName then ADODataSet1.IndexFieldNames:= Column.Field.FieldName + " DESC" else ADODataSet1.IndexFieldNames:= Column.Field.FieldName end ;

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

ПРИМЕЧАНИЕ

Все то же самое можно реализовать с использованием компонента ClientDataSet, однако этот компонент не поддерживает ключевого слова DESC, поэтому для сортировки в порядке уменьшения значений вам потребуется написать дополнительный код. Более того, при смене порядка сортировки компонент ClientDataSet будет заново формировать индекс - это ненужная и, возможно, медленная операция.

Клонирование

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

ПРИМЕЧАНИЕ

Компонент ClientDataSet также поддерживает клонирование, однако я не упомянул об этой возможности в главе 13.

Для клонирования набора данных (в ADO - набора записей) используется метод Clone. Клонировать можно любой набор данных ADO, однако в данном примере мы будет использовать компонент ADOTable. В программе DataClone (рис. 15.6) присутствуют два компонента ADOTable - один из них подключен к данным, а второй пуст. Оба набора данных подключены к источнику данных DataSource и сетке. Когда пользователь щелкает на кнопке Clone Dataset (клонировать набор данных), выполняется всего одна строка кода, которая клонирует набор данных:

ADOTable2.Clone(ADOTable1);



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

Набор данных можно клонировать только в случае, если он поддерживает закладки (bookmarks). По этой причине курсоры типа «только вперед» и динамические курсоры не могут быть клонированы. Чтобы определить, поддерживает ли набор записей закладки, вы можете воспользоваться методом Supports (например, ADOTable1.Supports()). Побочный эффект клонирования заключается в том, что закладки, созданные одним из клонов, могут использоваться всеми остальными клонами.

Обработка транзакций

В разделе «Использование транзакций» главы 14 мы с вами говорили о том, что механизм транзакций позволяет разработчикам группировать отдельные операции в отношении БД в единую логически неразрывную процедуру.
Обработка транзакций в ADO осуществляется при помощи компонента ADOConnection, для этого используются методы BeginTrans, CommitTrans и RollbackTrans. Действие этих методов сходно с аналогичными методами dbExpress и BDE. Для изучения механизма транзакций, встроенного в ADO, воспользуемся программой TransProcessing. В состав программы входит компонент ADOConnection, строка подключения которого (свойство ConnectionString) настроена на использование провайдера Jet 4.0 OLE DB и на обращение к файлу dbdemos.mdb. В программе присутствует компонент ADOTable, подключенный к таблице Customer и связанный с компонентами DataSource и DBGrid для отображения данных. Наконец, в программе присутствуют три кнопки, предназначенные для выполнения следующих команд:

ADOConnection1.BeginTrans; ADOConnection1.CommitTrans; ADOConnection1.RollbackTrans;


Используя эту программу, вы можете вносить в базу данных изменения, а затем выполнять откат транзакции, то есть отмену этих изменений. В результате база данных будет восстановлена в состояние, в котором она находилась до начала транзакции. Следует отметить, что обработка транзакций выполняется по-разному в зависимости от базы данных и провайдера OLE DB. Например, если вы подключитесь к Paradox с использованием провайдера ODBC OLE DB, вы получите сообщение об ошибке, указывающее на то, что база данных или провайдер OLE DB не могут начать транзакцию. Чтобы определить уровень поддержки транзакций, можно воспользоваться динамическим свойством Transaction DDL соединения:

if ADOConnection1.Properties["Transaction DDL"].Value > DBPROPVAL_TC_NONE then ADOConnection1.BeginTrans;


Если вы попытаетесь обратиться к этой же базе данных Paradox при помощи провайдера Jet 4.0 OLE DB, никакой ошибки не возникнет, однако из-за ограничений провайдера вы не сможете выполнить откат транзакции.
Еще одно странное отличие проявляет себя при работе с Access: если вы используете провайдер ODBC OLE DB, вы сможете использовать транзакции, однако не сможете использовать вложенные транзакции. Попытка открыть новую транзакцию параллельно с уже существующей активной транзакцией приведет к возникновению ошибки. Однако при использовании механизма Jet вы сможете без проблем использовать вложенные транзакции.

Вложенные транзакции

Используя программу TransProcessing, попробуйте выполнить следующий тест.

    Активизируйте транзакцию.

    Измените значение поля ContactName записи Around The Horn: вместо Thomas Hardy поставьте Dick Solomon.

    Активизируйте еще одну, вложенную транзакцию.

    Измените значение поля ContactName записи Bottom-Dollar Markets: вместо Elizabeth Lincoln поставьте Sally Solomon.

    Выполните откат внутренней транзакции.

    Подтвердите внешнюю транзакцию.

В результате модификации должны быть внесены только в запись Around The Horn. Если же внутренняя транзакция будет подтверждена, а в отношении внешней транзакции вы выполните откат, в результате в базу данных вообще не будет внесено ни одного изменения (даже изменения, сделанные в рамках внутренней транзакции). Именно так работают вложенные транзакции. Существует ограничение: Access поддерживает только пять уровней вложения транзакций.
ODBC не поддерживает вложенных транзакций, а провайдер Jet OLE DB поддерживает до пяти уровней вложения. Провайдер SQL Server OLE DB вообще не поддерживает вложения транзакций. Вы должны иметь в виду, что вложение транзакций может обрабатываться по-разному в зависимости от версии SQL-сервера или драйвера. Необходимую информацию можно получить в документации и при помощи экспериментов. Судя по всему, в большинстве случаев внешняя транзакция определяет, будут ли внесены в базу данных изменения, сделанные в рамках внутренней транзакции.

Атрибуты компонента ADOConnection

Если вы планируете использовать вложенные транзакции, существует еще одно обстоятельство, которое вы должны принимать во внимание. Компонент ADOConnection обладает свойством Attributes, которое определяет, каким образом ведет себя соединение в момент, когда транзакция подтверждается или выполняется ее откат. В свойстве Attributes хранится множество значений TXActAttributes, которое изначально пусто. Перечисление TXActAttributes включает в себя только два значения: xaCommitRetaining и xaAbortRetaining (иногда это значение ошибочно записывают как xaRollbackRetaining, так как с логической точки зрения это более правильное название). Если в свойстве Attributes присутствует атрибут xaCommitRetaining, в момент подтверждения транзакции автоматически открывается новая транзакция. Если в свойстве Attributes присутствует атрибут xaAbortRetaining, в момент отката транзакции автоматически открывается новая транзакция. Таким образом, если вы добавите в свойство Attributes оба этих атрибута, любые действия будут выполняться в рамках транзакции: в момент завершения очередной транзакции будет автоматически активизироваться следующая.
В большинстве случаев программисты предпочитают отказаться от работы в таком режиме и самостоятельно контролировать открытие транзакций, поэтомуданные атрибуты используются нечасто. Следует принимать во внимание особенности использования этих атрибутов совместно с вложенными транзакциями. Если вы создаете вложенную транзакцию и присваиваете свойству Attributes значение , внешняя транзакция никогда не будет завершена. Рассмотрим такую последовательность событий.

    Начинается внешняя транзакция.

    Начинается внутренняя транзакция.

    Выполняется подтверждение или откат внутренней транзакции.

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

Типы блокировки

Технология ADO поддерживает четыре различных подхода к блокированию данных для обновления: ltReadOnly, ltPessimistic, ltOptimistic и ltBatchOptimistic (существует также тип ltUnspecified, однако по изложенным ранее причинам этот тип в Delphi не используется). Для настройки режима блокировки используется свойство LockType. В данном разделе я коротко расскажу обо всех четырех способах блокировки. В последующих разделах о каждом из этих способов будет рассказано подробнее.
Значение ltReadOnly указывает на то, что данные предназначены только для чтения, - обновление невозможно. Так как клиент не может выполнить модификацию данных, никакой блокировки не требуется.
Значения ltPessimistic и ltOptimistic обеспечивают пессимистическую и оптимистическую блокировку соответственно. Эти режимы являются эквивалентами аналогичных режимов BDE. Однако по сравнению с BDE технология ADO обеспечивает большую гибкость: выбор режима блокировки остается за вами. Если вы используете BDE, решение об использовании пессимистической или оптимистической блокировки выполняет за вас драйвер BDE. Если вы используете настольную базу данных, такую как Paradox или dBase, значит, драйвер BDE использует пессимистическую блокировку. Если вы используете клиент-серверную базу данных, такую как InterBase, SQLServer или Oracle, драйвер BDE использует оптимистическую блокировку.

Пессимистическая блокировка

В данном контексте терминами «оптимистическая» и «пессимистическая» характеризуется ожидание программиста относительно возможности возникновения конфликтов при обновлении содержащихся в БД данных одновременно несколькими пользователями. Пессимистическая блокировка предполагает, что вероятность возникновения конфликта велика. Иными словами, пользователи в одно и то же время модифицируют содержащиеся в БД данные, и высока вероятность того, что два пользователя в одно и то же время попытаются модифицировать одну и туже запись базы. Чтобы предотвратить подобный конфликт, запись блокируется в момент, когда начинается редактирование. Запись остается заблокированной до тех пор, пока редактирование завершается или отменяется. Если какой-либо другой пользователь попытается отредактировать ту же самую (заблокированную) запись, он не сможет этого сделать: возникнет исключение «Обновление невозможно, запись заблокирована».
Этот подход хорошо знаком программистам, которые ранее работали с настольными базами данных, такими как dBase и Paradox. Преимущество состоит в том, что пользователь знает, что если он начал редактировать запись, то сможет успешно завершить редактирование и внести модификации в базу. Недостаток - в том, что пользователь полностью контролирует блокирование записи. Если пользователь хорошо освоил работу с приложением, редактирование одной записи может занять всего пару секунд, однако в клиент-серверной среде с множеством пользователей даже пара секунд может показаться вечностью. С другой стороны, ничего не подозревающий пользователь может начать редактирование записи и уйти на обед. В этом случае запись останется заблокированной до тех пор, пока он не вернется на свое рабочее место. Если не предпринять каких-либо специальных мер, все это время никто не сможет отредактировать заблокированную запись. Чтобы избежать подобного, зачастую используют таймер: если клавиатура и мышь длительное время остаются в бездействии, программа автоматически разблокирует запись.
Еще одна проблема, связанная с пессимистическим блокированием, заключается в том, что для пессимистического блокирования требуется курсор, работающий на стороне сервера. Ранее мы уже говорили о том, что местоположение курсора влияет на типы доступных курсоров. Сейчас мы видим, что местоположение курсора влияет также на способы блокирования. Позднее в данной главе мы подробнее обсудим преимущества использования курсоров, работающих на стороне клиента. Если вы примете решение воспользоваться этими преимуществами, значит, вы не сможете воспользоваться пессимистической блокировкой.

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

Поддержка обновляемых соединений (updatable joins) является одной из основных причин, по которым программисты используют компонент ClientDataSet (или кэшируемые обновления в BDE). Запрос, подразумевающий соединение двух таблиц, возвращает пользователю единую таблицу, и пользователь хочет обладать возможностью модифицировать записи этой таблицы. Рассмотрим следующее SQL-выражение:

SELECT * FROM Products, Suppliers WHERE Suppliers.SupNo=Products.SupNo

Этот запрос возвращает список продуктов с указанием поставщиков, которые выполняют поставку этих продуктов. Механизм BDE рассматривает любое SQLсоединение так таблицу, предназначенную только для чтения. Дело в том, что добавление, обновление и удаление записей в объединенной таблице неоднозначно. Например, если пользователь добавляет в объединенную таблицу новую запись, надо ли добавлять в таблицу нового поставщика и новый продукт или можно ограничиться только добавлением продукта? Архитектура ClientDataSet/Provider позволяет вам указать первичную обновляемую таблицу (в этой книге об этом не рассказывается) и выполнить дополнительную настройку SQL-обновлений. Об этом частично рассказано в главе 14, а кроме того, я расскажу об этом в главе 16. Технология ADO поддерживает механизм кэширования обновлений, который называется пакетными обновлениями (batch updates) и функционирует приблизительно так же, как аналогичный механизм BDE. В следующем разделе я более подробно рассмотрю механизм пакетных обновлений ADO. Однако для того, чтобы решить проблему обновления SQL-соединений вы можете обойтись и без помощи этого механизма. Дело в том, что ADO поддерживает обновление SQL-соединений. В программу JoinData добавлен компонент ADODataset, основанный на приведенном ранее SQL-выражении. Если вы запустите программу, вы сможете отредактировать одно из полей и сохранить изменения в базе (для этого достаточно переместиться на другую запись). Никаких ошибок не возникнет, так как ADO успешно выполнит обновление БД. Дело в том, что в отличие от BDE в ADO используется более практический подход. В рамках ADO, когда выполняется соединение нескольких таблиц, каждое поле знает, к какой таблице оно принадлежит. Если вы обновляете поле в таблице Products и публикуете изменения в базе, для обновления формируется SQL-выражение UPDATE, которое обновляет значение поля в таблице Products базы данных. Если помимо поля таблицы Products вы изменяете также поле таблицы Suppliers, значит, генерируются два SQL-выражения UPDATE - по одному для каждой таблицы.
При добавлении новой строки в SQL-соединение механизм ADO ведет себя подобным же образом. Если вы вставляете строку и добавляете значения только для полей таблицы Products, значит, генерируется только одно SQL-выражение INSERT, которое добавляет новую запись в таблицу Products. Если вы вводите значения для полей обеих таблиц, генерируется два SQL-выражения INSERT - по одному для каждой таблицы. Важен порядок, в котором выполняются эти выражения, так как новый продукт может ссылаться на нового поставщика, поэтому информация о поставщике должна добавляться в таблицу Suppliers в первую очередь. Серьезная проблема возникает, если выполняется удаление строки из объединенной таблицы. При попытке выполнить удаление строки объединенной таблицы вы увидите сообщение об ошибке. Конкретный текст сообщения зависит от версии ADO, а также от используемой базы данных. Это сообщение может сбить вас с толку, так как, скорее всего, оно не будет иметь отношения к истинной причине проблемы. Проблема связана с тем, что невозможно удалить запись, на которую ссылаются другие записи. В нашем примере вы, скорее всего, увидите сообщение о том, что невозможно удалить запись о продукте, потому что существуют другие записи, ссылающиеся на эту запись. Однако если вы проведете пару экспериментов, вы обнаружите, что ошибка возникает вне зависимости от того, существуют ли в базе другие записи, ссылающиеся на удаляемый продукт, или таких записей нет. Чтобы понять причину проблемы, необходимо воспользоваться таким же подходом, какой используется при добавлении новых записей в объединенную таблицу. В случае удаления строки объединенной таблицы механизм ADO генерирует два SQL-выражения DELETE: одно для таблицы Suppliers, а второе - для таблицы Products. Выражение DELETE для таблицы Products выполняется успешно, а вот выражение DELETE для таблицы Suppliers дает сбой - поставщик продуктов не может быть удален из таблицы, так как с ним, как правило, связано несколько ссылающихся на него записей о продуктах.

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

Даже если вы понимаете, как именно работает этот процесс, полезно взглянуть на проблему глазами пользователя. Я могу поспорить, что когда пользователь удаляет запись из объединенной таблицы, в 99 процентах случаев он намерен удалить только запись о продукте, оставив запись о поставщике без изменений. К счастью, вы можете добиться такого результата, если воспользуетесь специальным динамическим свойством Unique Table. С его помощью вы можете указать, что удаление строки имеет отношение только к таблице Products, но не к таблице Suppliers. Для этого используется следующий код:

ADOQuery1.Properties["Unique Table"].Value:= "Products";

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

Пакетные обновления (Batch updates)

При использовании механизма пакетных обновлений любые изменения, вносимые пользователем, накапливаются в локальной памяти. Позже полный пакет этих изменений за одну операцию может быть внесен в базу данных. Очевидно, что подобный подход обеспечивает выигрыш в производительности, однако существуют и другие преимущества, делающие его удобным. В частности, при использовании механизма пакетных обновлений пользователь может выполнять изменения даже тогда, когда он отключен от базы данных. Эта возможность может употребляться для обеспечения работы пользователя в автономном режиме, при применении технологий наподобие Briefcase (Портфель), а также в веб-приложениях, основанных еще на одном механизме ADO, который называется RDS (Remote Data Services). Вы можете включить пакетные обновления для любого набора данных ADO. Для этого необходимо присвоить свойству LockType значение ltBatchOptimistic перед тем, как набор данных будет открыт. Кроме того, вы должны присвоить свойству CursorLocation значение clUseClient, так как пакетные обновления обрабатываются механизмом ADO Cursor Engine. В результате любые изменения, вносимые пользователем в набор данных, будут сохраняться в области delta (в этой области хранится список изменений). Набор данных будет выглядеть так, как будто данные были изменены, однако на самом деле информация об изменениях хранится в памяти - эти изменения не внесены в базу данных. Чтобы опубликовать изменения в БД (перенести их из памяти в базу данных), необходимо обратиться к методу ApplyBatch (этот метод эквивалентен методу ApplyUpdates механизма BDE):

ADODataSet1.UpdateBatch;

Если вы хотите отменить все накопленное в памяти, воспользуйтесь методом CancelBatch - это аналог метода CancelUpdates. В рамках механизмов пакетных обОбновление новлений ADO, кэшируемых обновлений BDE и механизма кэширования Client- DataSet используется много других методов и свойств со схожими именами. Например, как и в BDE, свойство UpdateStatus набора данных ADO может использоваться для того, чтобы узнать состояние некоторой записи: была ли запись добавлена, модифицирована, удалена или она не подвергалась каким-либо изменениям. Это свойство весьма удобно, если вы хотите выделить модифицированные записи цветом или отобразить их состояние в строке состояния. Существуют некоторые различия в синтаксисе, например вместо вызова RevertRecord в ADO используется вызов CancelBatch(arCurrent);. Одна весьма полезная возможность механизма кэшированных обновлений BDE отсутствует в ADO: механизм слежения за тем, существуют ли неопубликованные в БД обновления. В BDE для этой цели используется свойство UpdatesPending. Это свойство содержит в себе значение True в случае, если в набор данных были внесены изменения, которые еще не опубликованы в базе данных. Это свойство удобно использовать в обработчике события OnCloseQuery:

Однако, обладая необходимыми знаниями и изобретательностью, вы можете написать свою собственную функцию ADOUpdatesPending. Чтобы написать такую функцию, вы должны знать, что наборы данных ADO поддерживают свойство FilterGroup, которое функционирует примерно так же, как фильтр. В отличие от свойства Filter стандартного набора данных, которое фильтрует данные, исходя из некоторого условия, свойство FilterGroup может выполнять фильтрацию, исходя из состояния записи. Существует несколько состояний фильтрации, и одно из них соответствует значению fgPendingRecords. Это состояние соответствует записям, которые были модифицированы, но информация об изменении которых еще не была опубликована в базе данных. Таким образом, чтобы взглянуть на все изменения, которые были сделаны, но не опубликованы, достаточно выполнить две строки кода:

ADODataSet1.FilterGroup:= fgPendingRecords; ADODataSet1.Filtered:= True;

Естественно, после выполнения этих команд в наборе данных будут присутствовать записи, которые были удалены, при этом поля этих записей будут пустыми, - это не очень удобно, так как вы не сможете понять, какая именно запись была удалена. (Первая версия ADOExpress функционировала иначе: для удаленных записей отображались все значения полей.)
Чтобы решить проблему UpdatesPending, вам потребуется использовать клонирование наборов данных (об этой возможности рассказывалось ранее). Функция ADOUpdatesPending настраивает свойство FilterGroup таким образом, чтобы в наборе данных присутствовала только информация о сделанных, но не опубликованных изменениях. Теперь надо проверить, присутствует ли в наборе данных хотя бы одна запись. Если хотя бы одна запись присутствует, значит, некоторые изменения еще не опубликованы в базе данных. Если после фильтрации набор данных оказался пустым, значит, все сделанные ранее изменения уже опубликованы в БД. Однако если вы попытаетесь проверить количество записей в реальном наборе данных, перенастройка свойства FilterGroup приведет к смещению указателя на текущую запись - пользовательский интерфейс немедленно отреагирует на это. Во избежании этого, воспользуйтесь клоном набора данных:

function ADOUpdatePending(ADODataSet: TCustomADODataSet): boolean; var Clone: TADODataSet; begin Clone:= TADODataSet.Create(nil); try Clone.Clone(ADODataSet); Clone.FilterGroup:= fgPendingRecords; Clone.Filtered:= True; Result:= not (Clone.BOF and Clone.EOF); Clone.Close; finally Clone.Free; end; end;

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

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

Ранее мы с вами рассмотрели использование свойства LockType и обсудили принцип функционирования пессимистической блокировки. В данном разделе мы рассмотрим оптимистическую блокировку. Оптимистическая блокировка не только является предпочтительной для высокопроизводительных систем, помимо этого именно такой тип блокировки используется при выполнении пакетных обновлений.
Оптимистическая блокировка предполагает, что возникновение конфликта между двумя пользователями, пытающимися отредактировать одну и ту же запись одновременно, маловероятно. Отсюда следует, что любому пользователю разрешается редактировать любую из записей в любое время. Последствия конфликтов обрабатываются в момент сохранения изменений в базе данных. Таким образом, конфликты рассматриваются как исключение из правила. Если два пользователя попытаются сохранить изменения одной и той же записи, выполнить это удастся только первому пользователю, второму пользователю будет отказано. Подобное поведение реализуется в приложениях, работающих в рамках модели Briefcase (Портфель), а также в веб-приложениях, в которых отсутствует постоянное соединение с базой данных и поэтому невозможно реализовать пессимистическую блокировку. В отличие от пессимистической, оптимистическая блокировка не требует длительного блокирования ресурсов: запись блокируется только в момент обновления. Таким образом, в среднем потребление ресурсов ниже, а база данных более масштабируема.
Давайте рассмотрим пример. Представьте, что у вас есть компонент ADODataSet, соединенный с таблицей Customer из примера dbdemos.mdb; при этом свойство LockType обладает значением ltBatchOptimistic, а содержимое набора данных отображается в сетке DBGrid. Предположим также, что у вас есть кнопка, при щелчке на которой происходит обращение к методу UpdateBatch. Запустите две копии этой программы (это программа BatchUpdates). Начните редактирование записи в первой копии программы. Для простоты в процессе демонстрации я использую один компьютер, однако все то же самое произойдет и в случае, если редактирование одной и той же записи будет выполняться на двух компьютерах.

    Выберите компанию Bottom-Dollar Markets в Канаде и измените ее имя на Bottom-Franc Markets.

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

    Во второй копии программы найдите ту же самую запись и измените имя компании на Bottom-Pound Markets.

    Перейдите к другой записи и щелкните на кнопке пакетного обновления. Обновление не сработает.

Как и в случае с другими сообщениями об ошибках ADO, текст сообщения будет зависеть не только от версии ADO, но также от того, насколько точно вы повторили описанную последовательность действий. В ADO 2.6 на экране появится следующее сообщение об ошибке: Row cannot be located for updating. Some values may have been changed since it was last read (Не удается обнаружить строку для обновления. Некоторые значения строки могли быть изменены с момента последнего чтения).
В этом заключается вся суть оптимистической блокировки. Для обновления записи выполняется следующее SQL-выражение:

UPDATE CUSTOMER SET CompanyName="Bottom-Pound Markets" WHERE CustomerID="BOTTM" AND CompanyName="Bottom-Dollar Markets"

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

UPDATE CUSTOMER SET ContactName="Liz Lincoln" WHERE CustomerID="BOTTM" AND ContactName="Elizabeth Lincoln"

В нашем случае обновление было бы выполнено успешно, так как другой пользователь не изменит ни первичный ключ, ни контактное имя. Это поведение напоминает режим Update Where Changed механизма BDE. Вместо свойства UpdateMode, используемого в BDE, в рамках ADO используется динамическое свойство Update Criteria набора данных. В следующем списке перечислены допустимые значения этого динамического свойства.

Не следует думать, что один из этих режимов будет предпочтительным для всего вашего приложения. На практике выбор режима определяется тем, информация какого характера содержится в таблице. Предположим, в таблице Customer присутствуют только три поля: CustomerID (идентификатор), Name (имя) и City (город). Изменение любого из этих полей никак не влияет на значения других полей таблицы, поэтому в подобной ситуации вполне можно использовать режим adCriteriaUpdCols (этот режим используется по умолчанию). Однако если помимо перечисленных полей в таблицу входит еще поле PostalCode (почтовый индекс), тогда обновление этого поля должно быть в обязательном порядке согласовано с обновлением поля City (город). Иными словами, нельзя допустить, чтобы один пользователь модифицировал поле PostalCode и в то же самое время другой пользователь модифицировал поле City без всякого согласования с первым пользователем. В этом случае более безопасным будет режим обновления adCriteriaAllCols. Следует также рассказать о том, как ADO осуществляет обработку ошибок в процессе обновления нескольких записей. В BDE и ClientDataSet для этой цели вы можете воспользоваться событием OnUpdateError, которое позволит вам отреагировать на ошибку, связанную с обновлением записи, и разрешить проблему перед тем, как перейти к следующей записи. В ADO такой возможности нет. Вы можете следить за прогрессом, успехом или неудачей пакетного обновления при помощи событий OnWillChangeRecord и OnRecordChangeComplete, однако вы не можете изменить содержимое обновляемой записи и попытаться заново внести ее в базу, как это возможно в рамках BDE и ClientDataSet. Проблема состоит также в том, что если ошибка возникает в ходе пакетного обновления, процедура обновления не останавливается, а продолжает выполняться. Пакетное обновление выполняется до самого конца, то есть до тех пор, пока все изменения не будут внесены в базу (при этом могут возникнуть другие ошибки). В результате вы можете получить сбивающее с толку или неправильное сообщение об ошибке. Если в ходе пакетного обновления возникло несколько ошибок (не удалось обновить несколько записей), ADO 2.6 отобразит на экране следующее сообщение: Multistep OLE DB operation generated errors. Check each OLE DB status value, if available. No work was done (В ходе выполнения многошаговой операции OLE DB возникли ошибки. Если возможно, проверьте каждое из состояний OLE DB. Никакой работы не было сделано). Проблема заключается в последнем предложении: No work was done. ADO сообщает нам, что ничего не было сделано, однако это не так. Запись, ставшая причиной ошибки, действительно не была обновлена, однако все остальные записи успешно опубликованы в базе данных.

Разрешение конфликтов, связанных с обновлением данных

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

ADODataSet1.FilterGroup:= fgConflictingRecords; ADODataSet1.Filtered:= True;

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

Те, кто работает с компонентом ClienDataSet, знают о существовании удобного диалогового окна ReconcileErrorForm. Это диалоговое окно формируется автоматически, оно показывает пользователю старые, новые и текущие значения полей проблемной записи и позволяет пользователю выбрать метод решения конфликта. К сожалению, в ADO аналог этого диалогового окна отсутствует. Класс TReconcileErrorForm был разработан специально для компонента ClientDataSet, поэтому его очень сложно адаптировать для использования с наборами данных ADO. Следует также сказать о порядке функционирования упомянутых свойств класса TField. Эти свойства основаны на объектах ADO Fields, на которые они ссылаются. Это означает, что порядок функционирования этих свойств целиком и полностью определяется провайдером OLE DB, который вы используете. Остается надеяться, что используемый вами провайдер корректно поддерживает возможности, в которых вы нуждаетесь. Большинство провайдеров хорошо справляются с этой задачей, однако провайдер Jet OLE DB возвращает одно и то же значение для свойств CurValue и OldValue. Говоря точнее, в качестве текущего значения поля возвращается значение, которое содержалось в поле в момент первого чтения записи из базы данных. Иными словами, Jet не позволяет определить значение, присвоенное полю другим пользователем (если только вы сами не предпримете каких-либо дополнительных действий, чтобы осуществить это). Если вы используете провайдер SQL Server OLEDB, вы сможете обратиться к свойству CurValue только после выполнения метода Resync набора данных, при этом параметр AffectRecords должен быть равен значению adAffectGroup, а параметр ResyncValues - содержать значение adResyncUnderlyingValues. Вот соответствующий код:

adoCustomers.FilterGroup:= fgConflictingRecords; adoCustomers.Filtered:= true; adoCustomers.Recordset.Resync(adAffectGroup, adResyncUnderlyingValues);

Отключенные наборы записей

Теперь, когда вы знаете о пакетных обновлениях, мы можем приступить к изучению еще одной возможности ADO: отключенных наборах записей. Отключенный набор записей (disconnected recordset) - это набор записей, который отключен от подключения к базе данных. Пользователь может работать с таким набором записей в точности так же, как он работает с обычным, подключенным набором записей. Это весьма впечатляющая возможность: между подключенным и отключенным наборами записей фактически не существует различий. Свойства и возможности фактически идентичны. Чтобы отключить набор записей от подключения, необходимо присвоить свойству CursorLocation значение clUseClient, а свойству LockType - значение ltBatchOptimistic. После этого вы присваиваете свойству Connection значение nil, в результате набор записей становится отключенным:

Эта возможность также поддерживается в BDE и в других технологиях работы с данными, однако для этого вы должны переключиться на использование Client- DataSet. Прелесть ADO состоит в том, что вы с самого начала можете разработать приложение так, будто бы вы понятия не имеете о возможности отключения набора записей от БД. Вы используете обычные компоненты dbGo самым обычным образом. После этого вы можете без лишних сложностей добавить в вашу программу возможность отключения от БД так, как будто вы только что узнали о существовании этой возможности. При этом вам не потребуется менять основной код вашего приложения.
Отключение набора записей может потребоваться для того, чтобы:

    минимизировать общее количество параллельных подключений к БД;

    обеспечить работу приложения в автономном режиме (в соответствии с моделью Briefcase).

О приложениях, работающих в рамках модели Briefcase (Портфель), мы поговорим в одном из следующих разделов.
Большинство бизнес-приложений, работающих в рамках концепции «клиентсервер», открывают таблицы базы данных и поддерживают постоянное подключение к БД в течение всего времени, пока таблица находится в открытом состоянии.
Однако на самом деле подключение необходимо только во время выполнения двух операций: извлечение данных из БД и обновление данных в БД. Представьте, что вы модернизируете вашу программу таким образом, чтобы она выполняла отключение от базы данных сразу же после того, как происходит открытие таблицы и извлечение данных. Как только необходимые данные передаются на клиентский компьютер, соединение разрывается. Вот соответствующий код:

Если вы будете использовать подобный подход в вашем приложении, среднее количество соединений с БД, открытых в одно и то же время, будет минимальным - соединения будут открываться только на короткое время, когда они действительно нужны. Очевидно, что подобную клиент-серверную структуру существенно проще масштабировать. Сервер сможет обслуживать значительно большее количество клиентов. Существует также недостаток: формирование соединения с некоторыми (но не со всеми) БД требует значительного времени, в подобной ситуации пакетное обновление будет выполняться медленнее.

Накопление соединений (Connection Pooling)

Разговоры о разрыве и повторном открытии соединений наводят нас на мысль, нельзя ли использовать соединения повторно? Этот механизм называется Connection Pooling. При его использовании, когда приложение завершает работу с подключением, подключение не уничтожается, вместо этого оно откладывается в область накопления (пул) и затем может использоваться повторно. Этот процесс выполняется автоматически, конечно, при условии, что используемый вами провайдер OLE DB поддерживает его и этот механизм включен. В этом случае никаких дополнительных действий предпринимать не надо.
Производительность является основной причиной применения подобного механизма. Чтобы установить соединение с базой данных, зачастую требуется время. Если вы имеете дело с настольной базой данных, такой как Access, соединение устанавливается фактически мгновенно. Однако в клиент-серверной среде, например при использовании сервера Oracle, доступ к которому осуществляется через сеть, для формирования соединения может потребоваться несколько секунд. Таким образом, имеет смысл подумать о повторном использовании такого ценного ресурса, как соединение с БД.
При использовании механизма накопления ADO каждый раз, когда приложение «уничтожает» объект ADOConnection, этот объект добавляется в специальный пул (место временного хранения). Если в дальнейшем возникает необходимость создать новое соединение, система автоматически выполняет поиск подходящего соединения в пуле. Если обнаруживается существующее соединение, обладающее строкой подключения, совпадающей с той, в которой нуждается пользователь, это соединение используется повторно. Если подходящего соединения не обнаруживается, происходит его создание. Соединения остаются в пуле до тех пор, пока либо они не будут востребованы, либо приложение не завершит работу, либо не истечет время тайм-аута. По умолчанию продолжительность тайм-аута составляет 60 секунд, однако начиная с версии MDAC 2.5 это значение можно изменить. Для этого необходимо указать продолжительность тайм-аута в ключе реестра HKEY_CLASSES_ROOT\ CLSID\\SPTimeout. Процесс накопления и повторного использования подключений выполняется в прозрачном режиме без каких-либо дополнительных действий со стороны разработчика. Аналогичный механизм накопления соединений функционирует в BDE при использовании MTS (Microsoft Transaction Server) и COM+, однако в отличие от BDE, механизм ADO выполняет накопление соединений самостоятельно, без помощи MTS или COM+.
По умолчанию механизм накопления соединений активизирован для всех провайдеров MDAC OLE DB реляционных баз данных (включая SQL Server и Oracle). Важным исключением является провайдер Jet OLE DB. Если вы используете ODBC, вы можете выбрать между накоплением соединений ODBC и накоплением соединений ADO, однако не рекомендуется использовать оба этих механизма одновременно. Начиная с версии MDAC 2.1 по умолчанию накопление соединений ADO включено, а накопление соединений ODBC отключено.

ПРИМЕЧАНИЕ

Вне зависимости от провайдера OLE DB накопление соединений не осуществляется в среде Windows 95.

К сожалению, в составе ADO отсутствуют инструменты, позволяющие следить за содержимым пула соединений. Однако для этой цели вы можете использовать инструмент SQL Server Performance Monitor, который снабдит вас информацией о подключениях к базе данных SQL Server.
Включить или отключить накопление соединений можно либо при помощи реестра, либо при помощи строки подключения. В реестре для этой цели используется параметр OLEDB_SERVICES, который расположен в разделе HKEY_CLASSES_ROOT\ CLSID\. В этом параметре хранится битовая маска, при помощи которой вы можете отключить некоторые службы OLE DB, в том числе накопление соединений, журналирование транзакций и другие. Чтобы отключить накопление соединений с использованием строки подключения, добавьте в конце строки подключения последовательность символов;OLE DB Services=-2. Чтобы включить накопление соединений для провайдера Jet OLE DB, добавьте в конце строки подключения последовательность;OLE DB Services=-1. В результате будут включены все службы OLE DB.

Сохранение набора записей в постоянной памяти (Persistent Recordset)

Возможность сохранения набора записей на жестком диске является важной составной частью приложений Briefcase (Портфель). Используя эту возможность, вы можете сохранить содержимое набора записей в файл на локальном жестком диске. Позже вы можете загрузить данные из этого файла в набор записей. Помимо прочих преимуществ, эта возможность позволяет разработчикам создавать реальные однозвенные приложения - вы можете установить приложение базы данных, но при этом не устанавливать саму базу данных. Благодаря этому для установки программы требуется очень небольшое пространство на клиентском жестком диске. Чтобы сохранить набор данных на жестком диске, используется метод SaveToFile:

Следует иметь в виду, что (в отличие от ClientDataSet) ADO не содержит в себе собственной поддержки формата XML - для генерации XML-кода используется механизм MSXML. Следовательно, пользователь вашего приложения должен будет установить Internet Explorer 5.0 или загрузить MSXML с веб-узла компании Microsoft.
Если вы планируете сохранять данные локально в формате XML, помните о некоторых недостатках этого подхода:

    XML-файлы (создаваемые механизмом ADO, впрочем, как и любые другие XML-файлы) обладают значительным размером, их размер существенно превышает размер ADTG-файла, содержащего такие же данные (как правило, размер XML-файла в два раза превышает размер аналогичного ADTG-файла);

    формат XML, генерируемый ADO, является специфическим форматом Microsoft (это характерно для многих других компаний, поддерживающих собственный порядок генерации XML), это означает, что XML-код, генерируемый ADO, не может быть прочитан компонентом ClientDataSet и наоборот (к счастью, эту проблему можно решить, воспользовавшись входящим в состав Delphi компонентом XML Transform, который позволяет выполнять преобразование между различными структурами XML).

Если вы собираетесь использовать механизм сохранения набора записей только в рамках однозвенного приложения и не предполагаете использовать модель Briefcase (Портфель), тогда вы можете воспользоваться компонентом ADODataSet, присвоить его свойству CommandType значение cmdFile, а свойству CommandText - имя файла. Благодаря этому вы избавляетесь от необходимости обращаться к методу LoadFromFile вручную. Однако вам по-прежнему придется обратиться к методу SaveToFile. В приложении Briefcase (Портфель) этот подход будет слишком ограничивающим, так как в таких приложениях набор данных используется в двух режимах.

Модель Briefcase (Портфель)

Теперь, когда вы знакомы с пакетными обновлениями, отключенными наборами записей и сохранением наборов записей в локальных файлах, вы можете приступить к изучению модели Briefcase (Портфель). Основная идея состоит в том, чтобы обеспечить пользователю возможность работать с вашим приложением даже тогда, когда он не имеет возможности подключиться к базе данных, то есть когда пользователь путешествует или находится в офисе у клиента компании. Проблема заключается в том, что в офисе у клиента пользователь не может подключиться к корпоративной сети вашей компании, а значит, не может подключиться к серверу базы данных. Отсюда следует, что данные не могут попасть на пользовательский портативный компьютер и быть обновлены.
Чтобы обеспечить автономную работу пользователя, вам потребуются такие технологии, как отключенные наборы данных и сохранение наборов данных на локальном диске. Представьте себе, что вы разработали клиент-серверное приложение, которое обеспечивает доступ к серверу базы данных и полностью удовлетворяет запросам пользователя. Теперь пользователь требует, чтобы вы обеспечили работу этого приложения в автономном режиме, то есть даже тогда, когда нет возможности подключиться к серверу БД. Для этого вы должны добавить в приложение возможность сохранения данных на пользовательском локальном жестком диске. Иными словами, прежде чем пускаться в путешествие, пользователь должен отдать команду подготовки приложения к работе в автономном режиме. По этой команде каждая таблица сохраняется в локальном файле при помощи метода SaveToFile. В результате на пользовательском жестком диске возникает коллекция файлов ATDG или XML, которые являются отражением содержимого базы данных. После этого пользователь может отключить свой портативный компьютер от сети и продолжить работу с привычным для него приложением в автономном режиме.
Приложение должно автоматически определять, работает ли оно автономно или существует возможность подключения к сети. Для того чтобы определить это, вы можете попытаться подключиться к базе данных и проверить, открылось ли соединение. Также вы можете проверить присутствие локального файла портфеля или воспользоваться собственным специальным флагом. Если приложение работает в автономном режиме, вместо того чтобы подключаться к базе данных, оно должно извлечь данные из локального файла при помощи метода LoadFromFile. Когда требуется опубликовать данные в базе, вместо метода UpdateBatches приложение должно обращаться к методу SaveToFile для каждой из таблиц. Если возможна связь с базой данных через сеть, необходимо присвоить значение True свойству Connected компонента ADOConnection, а также свойству Active каждого из компонентов ADODataSet. Когда пользователь возвращается из путешествия, он должен внести информацию о сделанных им изменениях в базу данных. Для этого необходимо загрузить данные из локальных файлов, подключить наборы данных к базе данных и обратиться к методу UpdateBatch.

Пара слов об ADO.NET

ADO.NET - это часть новой архитектуры.NET, разработанной компанией Microsoft. Архитектура.NET является попыткой компании Microsoft заново перепроектировать средства и инструменты разработки программного обеспечения для того, чтобы сделать их более удобными для создания веб-приложений. ADO.NET является новым развитием ADO, ориентированным на решение проблем, связанных с разработкой веб-систем и устраняющим многие недостатки устаревшей технологии ADO. Проблема ADO состоит в том, что эта технология основана на COM. Для одно- и двухзвенных приложений COM является вполне приемлемой платформой, однако в мире Веб использовать COM в качестве транспортного механизма фактически невозможно. Для COM характерны три основные проблемы, которые ограничивают использование этой технологии в Веб: во-первых, COM функционирует только в среде Windows, во-вторых, передача наборов записей требует маршализации COM, в-третьих, вызовы COM не могут проникать через корпоративные брандмауэры. Технология ADO.NET решает все три проблемы благодаря использованию XML.
Еще одной особенностью ADO.NET является разделение традиционного набора записей ADO на несколько отдельных классов. Вместо того чтобы решать множество проблем, каждый из классов предназначается для решения одной конкретной проблемы. Например, в ADO.NET присутствует класс DataSetReader, который обеспечивает доступ к данным только для чтения в режиме Forward-only (только вперед), при этом данные располагаются на стороне сервера. Благодаря всем этим ограничениям данный класс обеспечивает быстрое чтение результирующего набора данных. Класс DataTable функционирует как отключенный набор записей на стороне клиента. Класс DataRelation обладает общими чертами с провайдером MSDataShape OLE DB. В любом случае знание традиционной технологии ADO окажется чрезвычайно полезным при изучении новой технологии ADO.NET.

В данной главе мы рассмотрели технологию ADO (ActiveX Data Objects) и dbGo - набор компонентов Delphi, предназначенных для доступа к интерфейсам ADO. Вы узнали о том, как работать с MDAC (Microsoft Data Access Components) и различными механизмами обращения к серверам баз данных. Я рассказал вам о преимуществах и недостатках технологии ADO.
В главе 16 речь пойдет о встроенной в Delphi архитектуре DataSnap, которая позволяет разрабатывать трехзвенные клиентские и серверные приложения. Эту задачу можно решить и при помощи ADO, однако данная книга в первую очередь посвящена Delphi, поэтому я расскажу вам о решении проблемы, которое является естественным для среды Delphi. После того как мы с вами завершим рассмотрение DataSnap, мы продолжим изучать встроенную в Delphi архитектуру для работы с данными. В главе 17 мы рассмотрим процесс разработки собственных компонентов, предназначенных для работы с данными.

5. 5 Технология доступа к данным ADO

Технология доступа к данным ADO

Основные особенности технологии доступа к данным ADO

Технологии ODBC и OLE DB считаются хорошими интерфейсами передачи данных, но как программные интерфейсы имеют много ограничений, т.к. являются низкоуровневыми.

Для снятия этих ограничений была предложена технология ADO (Delphi 5).

Технология ADO представляет иерархическую модель объектов для доступа к различным OLE DB-провайдерам данных. Объектная модель ADO включает объекты, обеспечивающие соединение с провайдером данных, создание SQL-запросов к данным и т.д.

Модель объекта не содержит таблиц, среды. Здесь основными объектами являются:

    объект Набор данных;

    объект Соединение, создающий связь с провайдером данных;

    объект Команда –выполнение процедуры.

Особенностью технологии ADO является возможность ее использования в Интернет-приложениях для доступа к различным источникам данных.

Технология ADO и интерфейсы OLE DB обеспечивают для приложений единый способ доступа к источникам данных различных типов (рис.1).

Рис. 1. Схема доступа к данным через ADO

В целом технологию ADO можно охарактеризовать как наиболее современную технологию разработки приложений для работы с распределенными БД по технологии клиент-сервер.

Компоненты ADO

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

Все использующие ADO компоненты доступа к данным размещены на странице ADO палитры компонентов (рис.2).

Рис. 2. Компоненты ADO

TADOConnection аналогичен компоненту BDE TDatabase и используется для указания базы данных и работы транзакциями. С одним компонентом TADOConnection может быть связанно несколько компонентов TADOTable и TADOQuery.

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

TADOQuery позволяет выполнять SQL-запросы при работе с данны­ми через ADO. Соединение с хранилищем данных осуществляется стандарт­ным методом. Текст запроса содержится в свойстве SQL.

TADOStoredProc позволяет обращаться к хранимым процедурам, содержащимся в базах данных. Имя хранимой процедуры определяется свойством ProcedureName.

TADODataSet предназначен для представления набора данных из хранилища ADO.

TADOCommand и TADODataSet являются наиболее общими компонентами для работы с ADO, но и наиболее сложными в работе. Оба компонента позволяют выполнять команды на языке провайдера данных (так в ADO называется драйвер базы данных).

Установление связи с объектом ADO

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

Первый шаг задания свойства ConnectionString, это выбор способа определения подключения. База данных может быть указана двумя способами: через файл формата Microsoft Data Link (расширение UDL) либо прямым заданием параметров соединения (рис. 3).

Рис. 3. Выбор способа определения строки подключения

При прямом задании соединения значение свойства ConnectionString может быть задано напрямую в поле ввода окна диалога ConnectionString. Однако обычно проще использовать специальный мастер, вызываемый щелчком на кнопке с многоточием в конце поля ввода. При этом открывается окно диалога Data Link Properties, в котором выбираются тип базы данных, ее местоположение и параметры соединения (см. рисунок 4). Данное окно диалога содержит четыре вкладки:

    на вкладке Provider (Поставщик данных) выбирается тип БД. Базы данных Microsoft Access доступны через драйверы Microsoft Jet OLE DB Provider и Microsoft OLE DB Provider for ODBC. Базы данных dBase, Paradox и FoxPro доступны только через ODBC;

Рис. 4. Диалоговое окно настройки параметров соединения: выбор провайдера

    состав элементов управления вкладки Connection (Подключение) зависит от выбранного типа БД (рис. 5). Рассмотрим вариант выбора соединений Microsoft Jet 4.0 OLE DB Provider:

    флажок Blank password подавляет отображение окна диалога для ввода идентификатора и пароля пользователя при установлении соединения, если поле пароля пустое;

    флажок Allow saving password сохраняет пароль в строке параметров соединения. Если флажок не установлен, то введенный пароль будет использоваться только при выполнении тестового соединения;

    кнопка Test Connection позволяет проверить правильность и полноту параметров;

    переключатель Use data source name позволяет ввести предустановленный псевдоним ODBC, а в положении Use connection string вводится как псевдоним, так и тип ODBC драйвера и параметры соединения;

    путь к базе данных задается в поле ввода Select or enter a database name для соединения Microsoft Jet OLE DB Provider или в поле ввода Use data Source name для соединения Microsoft OLE DB Provider for ODBC. Причем в первом случае при щелчке на кнопке с многоточием, расположенной справа от поля ввода, появится окно диалога открытия файла, с помощью которого выбирается необходимый файл базы данных. Во втором случае путь к базе данных приходится вводить вручную;

Рис. 5 – Окно диалога DataLinkProperties(вкладка Подключение)

    на вкладке Advanced (Дополнительно) (рис.6) расположены дополнительные параметры, с помощью которых устанавливаются уровень доступа к файлу базы данных, время ожидания сетевого соединения (то есть время, через которое связь будет считаться потерянной, если сервер не отвечает) и уровень защиты соединения;

Рис. 6 – Окно диалога DataLinkProperties(вкладка Дополнительно)

    на вкладке All (Все) (рис. 7) можно отредактировать все параметры с предыдущих страниц и параметры, зависящие от выбранного соединения, не помещенные на вкладку Connection. Редактирование осуществляется путем ввода значений параметров в текстовой форме. Описание этих параметров можно найти в следующих документах: MSDN Data Access Services, Microsoft Data Access Components (MDAC) SDK, Microsoft ActiveX Data Objects (ADO), Microsoft ADO Programmers Reference, Using Providers with ADO.

Рис. 7 – Окно диалога DataLinkProperties(вкладка Все)

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

Компоненты для работы с Microsoft ActiveX Data Objects (ADO) впервые появились в среде Delphi версии 5.

ADO - это технология стандартного обращения к реляционным структурам данных от Microsoft. Она аналогична BDE по назначению и сродни ему по возможностям.

В основе архитектуры ADO лежит объектная модель компонентов COM (Component Object Model). Все объекты и интерфейсы ADO представляют собой интерфейсы и объекты СОМ.

Модель СОМ является базовой для технологий ActiveX и OLE. Проиллюстрировать это отношение можно на примере объекта TObject, как базового объекта для VC L Delphi.

Технология СОМ работаете так называемыми СОМ-обьектами. Во многом СОМ-объекты похожи на обычные объекты визуальной библиотеки компонентов Delphi. Но, в отличие от последних, СОМ-объекты не содержат поля, в них находятся лишь свойства и методы, а также интерфейсы.

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

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

Технология СОМ имеет два явных плюса:

· создание СОМ-объектов не зависит от языка программирования. Таким образом, СОМ-объекты могут быть написаны на различных языках;

· СОМ-объекты пригодны для использования в любой среде программирования под Windows. В число этих сред входятDelphi, Visual С++, С++ Builder, Visual Basic и многие другие.

ПРИМЕЧАНИЕ: Хотя технология СОМ имеет очевидные достоинства, ей свойственны также и минусы, среди которых можно выделить зависимость от платформы. Эта технология применима только в операционной системе Windows и на платформе Intel.

Технология СОМ реализуется с помощью СОМ-библиотек (такие файлы операционной системы, как OLE32.DLL и OLEAUT32.DLL). СОМ-библиотеки содержат набор стандартных интерфейсов, которые отвечают за функциональность СОМ-объекта, а также небольшой набор функций API, обеспечивающих создание СОМ-объектов и управление ими.

В Delphi воплощение и поддержка технологии СОМ называется Delphi ActiveX framework, DAX. Реализация DAX описана в модуле AxCtrls.

Рассмотрим особенности архитектуры ADO (Рисунок 1).

Рисунок 1 - Архитектура ADO

Перечислим основные интерфейсы ADO и кратко поясним их назначение:

· интерфейс IConnection выполняет следующие функции:

· осуществляет связь с сервером;

· управляет транзакциями;

· получает информацию о произошедших ошибках (свойство Errors);

· получает информацию о схеме данных (таблицы, поля и т. д.);

· интерфейс IRecordset (на нижнем уровне ADO это IRowset) является аналогом TDataSet в Delphi и поддерживает текущее положение и перемещение курсора, закладки (Bookmarks), чтение, изменение и удаление записей и т. п.;

· интерфейс IReld позволяет получать значение поля, его тип, длину и другие сведения о поле данных;

· интерфейсы ICommand и IParameter обеспечивают работу с командами источника данных. Синтаксис команд для каждого из источников свой собственный;

· интерфейс IProperty позволяет получать и устанавливать параметры, специфические для провайдера данных.

Для работы с механизмом ADO в Delphi 7 предназначены семь стандартных компонентов, расположенных на закладке ADO палитры компонентов (рисунок 2).

Рисунок 2 – палитра компонентов ADO

Первый компонент называется ADOConnection. Функционально он аналогичен компоненту Database закладки BDE. С помощью компонента ADOConnection можно указывать местоположение базы данных и работать с транзакциями. Рассмотрим основные свойства компонента ADOConnection, отображаемые в окне инспектора объектов (Таблица 1).

Таблица 1 – Основные свойства ADOConnection

Следующий компонент закладки ADO называется ADOCommand. Он предназначен для выполнения SQL-команды без возврата результирующего набора данных (Таблица 2).

Таблица 2 - Основные свойства компонента ADOCommand

Третий компонент закладки ADO – ADODataSet. Его назначение - получение набора данных из одной или нескольких таблиц базы данных. Кроме того, он также позволяет работать с возвращенным набором данных визуальным компонентам, предназначенным для отображения данных. Это наиболее общий компонент для работы с набором данных, который может использоваться вместо компонентов ADOTable, ADOQuery или ADOStoredProc. В таблице 3 приведены основные свойства указанного компонента.

Таблица 3 - Основные свойства компонента ADODataSet

Следующий компонент закладки ADO называется ADOTable. Он является аналогом компонента Table, расположенного на закладке BDE, и предназначен для доступа к таблице с помощью механизма ADO. Компонент ADOTable имеет свойства, которые мы уже рассматривали ранее в компонентах Table и ADODataSet, поэтому мы не будем возвращаться к ним еще раз.

Компонент ADOQuery представляет собой запрос к базе данных. Это может быть как запрос, в результате которого возвращаются данные из базы (например, SELECT), так и запрос, не формирующий результирующего набора данных (например, INSERT). Компонент аналогичен компоненту Query из BDE. Все основные его свойства мы уже рассматривали в описании свойств компонентов Query н ADODataSet



Компонент ADOStoredProc предназначен для вызова процедуры, хранимой на сервере базы данных. В отличие от BDE и InterBase хранимые процедуры в ADO могут возвращать набор данных, поэтому компонент такого типа является потомком DataSet и может выступать источником данных в компонентах типа DataSource.

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

ПРИМЕЧАНИЕ: Маршалингом данных называется механизм, который позволяет клиенту получать доступ к объектам, расположенным в другом адресном пространстве или на другом компьютере.

На события в ADO возлагаются те же задачи, что и на события в VCL. Многие из событий ADO имеют аналоги в VCL, и соответствующие компоненты вызывают из событий ADO события VCL.

Все события в ADO условно разделим на несколько групп: события соединения, события транзакции, события выполнения команд. События соединения:

· OnWillConnect - вызывается перед установкой соединения;

· OnConnectComplete - вызывается после установки соединения;

· OnDisconnect - активируется при разрыве соединения. Эти события инкапсулированы в компоненте ADOConnection.События транзакции:

· OnBeginTransComplete - происходят при выполнении BeginTrans;

· OnCommitTransComplete - происходят при выполнении CommitTrans;

· OnRollbackTransComplete - вызываются при выполнении RollbackTrans.

События данной группы инкапсулированы в компоненте ADOConnection. События выполнения команд OnWillExecute и OnExecuteComplete вызываются перед выполнением команды и после него соответственно.

Эти события «зашиты» в компоненте ADOConnection, а не в ADOCommand, как можно было бы предположить. Указанная «странность» связана с тем, что в ADO объекта команды как такового нет, и по этой причине он не может получать сообщения.

В компонент ADOConnection также встроено событие OnlnfoMessage, которое вызывается по факту прихода с сервера дополнительной информации. Формат и назначение зависят от сервера, с которым вы работаете.

В ADO есть и события, связанные с набором данных, а не с соединением, как вышеописанные. Они инкапсулированы в компоненты, представляющие наборы данных: ADODataSet, ADOTable, ADOQuery и ADOStoredProc.

Эти события можно условно разбить на три группы.

События выборки данных.

· OnFetchProgress - многократно вызывается в процессе выборки данных;

· OnFetchComplete - завершение выборки.

· Уведомления об изменении положения текущей записи в наборе:

· OnWillMove - вызывается до изменения положения текущей записи. Позволяет отменить действие;

· OnMoveComplete - вызывается после изменения положения текущей записи;

· OnEndOfRecordset - генерируется при достижении конца набора данных. Позволяет добавить новую запись.

Уведомления об изменении набора данных.

· OnWillChangeField, OnFieldChangeComplete - вызываются до и после изменения текущей записи набора;

· OnWillChangeRecord, OnRecordChangeComplete - вызываются до и после изменения, добавления, удаления строки набора и при отмене этих действий;

· OnWillChangeRecordset, OnRecordsetChangeComplete - вызываются до и после открытия, закрытия, повторного запроса и синхронизации набора данных.

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

В ADO есть возможность, не имеющая аналогов ни в BDE, ни в InterBase. Это асинхронное выполнение операций с сервером. Асинхронно могут выполняться:

· установка соединения с сервером (Connection);

· выполнение команды (Execute);

· выборка данных (Fetch).

Для включения этого режима необходимо присвоить свойству ConnectOptions компонента ADOConnection значение coAsyncConnect.

При установлении соединения происходит следующее:

· вызывается обработчик события OnWillConnect;

· управление передается программе.

· После завершения соединения, как успешного, так и ошибочного, вызывается обработчик события OnConnectComplete.

Надо заметить, что многие компоненты ADO при активизации или выполнении отрабатывают команду на языке SQL. Это такие компоненты, как ADOCommand, ADODataSet, ADOTable, ADOQuery и ADOStoredProc. Для асинхронного выполнения команды установите в свойстве ExecuteOptions значение coAsyncExecute. При выполнении команды будет происходить следующее:

· вызовется обработчик события OnWillExecute;

· управление передастся программе.

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

57

Работа с базами данных в.NET Framework --- ADO.NET --- Модель EDM

Понимая предназначение платформы ADO.NET Entity Framewrok, а также имея общее представление о ее работе, можно приступать к рассмотрению первого примера. Чтобы пока не усложнять картину, построим модель EDM, которая обеспечит доступ только к таблице Inventory базы данных AutoLot, созданной ранее - ADO_NET/base/level3/3_2.php . Разобравшись с основами, мы затем построим новую модель EDM, которая будет рассчитана на всю базу данных AutoLot.

Генерация файла *.edmx

Начнем с создания консольного приложения. Когда планируется использование Entity Framework, первый шаг состоит в генерации необходимой концептуальной, логической и физической модели данных, определенной в файле *.edmx. Один из способов предусматривает применение для этого утилиты командной строки EdmGen.exe из комплекта.NET 4.0 SDK. Откройте окно командной строки Visual Studio 2010 и введите следующую команду:

На консоль выводится список опций, которые можно указывать утилите для генерации необходимых файлов, основываясь на существующей базе данных; кроме того, доступны опции для генерации совершенно новой базы данных на основе имеющихся сущностных файлов. Ниже описаны некоторые общие опции EdmGen.exe:

/mode:FullGeneration

Генерировать файлы *.ssdl, *.msl, *.csdl и клиентские сущности из указанной базы данных

/project:

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

/connectionstring:

Строка соединения, используемая для взаимодействия с базой данных (допускается сокращенная форма - /с:)

/language:

Позволяет указать, какой синтаксис должен использоваться для сгенерированного кода - C# или VB

/pluralize

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

Как и платформа.NET 4.0 в целом, программная модель EF поддерживает программирование в стиле сначала домен, что позволяет создавать свойства (с применением типичных объектно-ориентированных приемов) и использовать их для генерации новой базы данных. В этом вводном обзоре ADO.NET EF ни подход "сначала модель", ни генерация сущностной модели клиентской стороны с помощью утилиты EdmGen.exe применяться не будут. Вместо этого будут использоваться визуальные конструкторы EDM из среды Visual Studio 2010.

Выберите пункт меню Project --> Add New Item (Проект --> Добавить новый элемент) и вставьте новый элемент ADO.NET Entity Data Model по имени InventoryEDM.edmx:

Щелчок на кнопке Add (Добавить) приводит к запуску мастера создания модели сущностных данных (Entity Data Model Wizard) . На первом шаге мастер позволяет выбрать, нужно генерировать EDM из существующей базы данных либо определить пустую модель (для разработки в стиле "сначала модель"). Выберите опцию Generate from database (Генерировать из базы данных) и щелкните на кнопке Next (Далее).

На втором шаге мастера выбирается база данных. Если соединение с базой данных внутри проводника сервера Visual Studio 2010 уже существует, оно будет присутствовать в раскрывающемся списке. Если же нет, щелкните на кнопке New Connection (Создать соединение). В любом случае выберите базу данных AutoLot и отметьте флажок Save entity connection settings in App.config as (Сохранить настройки соединения в файле App.config как):

Прежде чем щелкать на кнопке Next, взгляните на формат строки соединения:

Metadata=res://*/InventoryEDM.csdl|res://*/InventoryEDM.ssdl|res://*/InventoryEDM.msl; provider=System.Data.SqlClient;provider connection string="Data Source=MICROSOF-1EA29E\SQLEXPRESS;Initial Catalog=AutoLot; Integrated Security=True;Pooling=False"

Основной интерес в ней представляет флаг metadata , который используется для указания имен встроенных данных XML-ресурсов концептуального, физического и файла отображений (вспомните, что во время компиляции файл *.edmx будет разделен на отдельные файлы, и данные этих файлов примут вид двоичных ресурсов, встраиваемых в сборку).

На последнем шаге мастера можно выбрать элементы из базы данных, для которой необходимо сгенерировать модель EDM. В рассматриваемом примере ограничимся только таблицей Inventory. Щелкните на кнопке Finish для генерации модели EDM.

Изменение формы сущностных данных

После завершения работы с мастером откроется визуальный конструктор EDM в IDE-среде с одной сущностью по имени Inventory. Просмотреть композицию любой сущности в визуальном конструкторе можно в окне Model Browser (Браузер моделей), которое открывается через пункт меню View --> Other Windows (Вид --> Другие окна).

Теперь взгляните на формат концептуальной модели для таблицы базы данных Inventory, представленный в папке Entity Types (Типы сущности). В узле хранилища, имя которого совпадает с именем базы данных (AutoLotModel.Store), находится физическая модель базы данных:

По умолчанию имена сущностей будут основаны на именах исходных объектов баз данных; однако, вспомните, что имена сущностей в концептуальной модели могут быть любыми. Чтобы изменить имя сущности либо имена свойств сущности, необходимо выбрать нужный элемент в визуальном конструкторе и установить соответствующим образом свойство Name в окне свойств (Properties). Переименуйте сущность Inventory в Car и свойство PetName в CarNickname:

Теперь выберите сущность Car в визуальном конструкторе и снова загляните в окно Properties. Вы должны увидеть поле Entity Set Name (Имя набора сущностей), также переименованное из Inventories в Cars. Значение Entity Set Name важно, потому что оно соответствует имени свойства в классе контекста данных, который используется для модификации базы данных. Вспомните, что это свойство инкапсулирует переменную-член ObjectSet класса-наследника ObjectContext.

Просмотр отображений

Имея данные в измененной форме, можно просматривать отображения между концептуальным уровнем и физическим уровнем в окне Mapping Details (Сведения об отображениях), которое открывается через пункт меню View --> Other Windows --> Mapping Details. Взгляните на рисунок ниже и обратите внимание, что узлы в левой части дерева представляют имена данных из физического уровня, в то время как узлы справа представляют имена концептуальной модели:

Просмотр данных сгенерированного файла *.edmx

Теперь давайте посмотрим, что именно мастер EDM Wizard сгенерировал. Щелкните правой кнопкой мыши на файле InventoryEDM.edmx в проводнике решения и выберите в контекстном меню пункт Open With... (Открыть с помощью). В открывшемся диалоговом окне выберите опцию XML Editor (Редактор XML). Это позволит просмотреть XML-данные, лежащие в основе представления в визуальном конструкторе EDM. Структура этого XML-документа разделена на четыре части: все они находятся в корневом элементе .

Подэлемент определяет XML-данные для концептуальной, физической и модели уровня отображения. Ниже показано определение физической таблицы базы данных Inventory:

Обратите внимание, что узел определяет имя поставщика данных ADO.NET, который использует эту информацию при взаимодействии с базой данных (System.Data.SqlClient). Узлами помечается имя физической таблицы базы данных, а также каждый столбец в таблице.

Следующая важная часть файла *.edmx - элемент . который определяет измененные сущности клиентской стороны. Как видно, сущность Cars определяет свойство CarNickname, которое изменяется в визуальном конструкторе:

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