Продолжается подписка на наши издания! Вы не забыли подписаться?

Технический обзор MIDAS

(Сервисы многоуровневых распределенных приложений)


Цель этой статьи - дать читателю представление о многоуровневом программировании в реализации корпорации Inprise (Корпорация Borland International ныне называется Inprise Corporation) - технологии Midas. Многоуровневое программирование позволяет разделить приложение и получать доступ к данным, расположенным физически на другом компьютере, не имея на локальной машине полного набора инструментов управления базой данных. Кроме того, это позволяет централизовать бизнес-правила и бизнес-процессы, а также распределить загрузку сети при работе процесса.

Эта статья разделена на четыре части:

  • Введение в многоуровневое программирование
  • Обзор многоуровневой технологии программирования Inprise
  • Подробное рассмотрение создания серверов и клиентов
  • Подробное рассмотрение удаленных соединений
  • Термины “многоуровневое программирование”, “распределенный набор данных”, “удаленный набор данных” и “Midas” используются в качестве синонимов. Ниже вы найдете раздел, посвященный терминологии. Уточнения и новые версии этой статьи можно обнаружить на www.borland.com и на web-сайте автора: users.aol.com/charliecal.

    Обзор многоуровневой технологии программирования Inprise

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

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

    Однако, в отличие от реализации распределенных наборов данных Inprise, функциональность браузеров весьма ограничена. К примеру, отсутствие инструментов от сторонних разработчиков, подобных IntraBuilder, значительно осложняет работу с базами данных. Но сложные задачи просто выполняются с помощью многоуровневых Delphi-приложений. Кроме того, приложения, откомпилированные в Delphi, гораздо быстрее и, по сравнению с HTML-приложениями, лучше работают.

    Распределенные наборы данных позволяют использовать в клиентских приложениях все стандартные Delphi-компоненты, включая инструменты баз данных. При этом со стороны клиента необязательно иметь Inprise Database Engine, ODBC или любую клиентскую библиотеку базы данных (напр., Oracle SQL*NET, Sybase CT-Lib и т. д.), хотя где-то в сети BDE или подобный процессор (один, со стороны сервера), разумеется, должен присутствовать.

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

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

    Такой подход позволяет не подключаться к серверу каждый раз, когда нужно работать с данными. Это очень удобно для мобильных пользователей, как, впрочем, и в других случаях, где нужно свести сетевой траффик к минимуму. Все, что нужно иметь со стороны клиента - это маленький (около 150 КБ) файл, чрезвычайно мало по сравнению со многими мегабайтами, необходимыми для работы BDE или другого программного обеспечения среднего уровня баз данных.

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

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

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

    Терминология

    Некоторое внимание следует уделить терминологии. Эта технология появилась относительно недавно, и различные компании по-разному трактуют смысл терминов. Речь идет о предмете, который называют:

    распределенные вычисления

    технология клиент/сервер

    многоуровневые вычисления

    n-уровневое программирование

    удаленные наборы данных

    распределенные наборы данных

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

    Особенности определения многоуровневых вычислительных систем, использованного в этой статье обозначены в предыдущем разделе “Обзор многоуровневой Технологии Программирования Inprise”. Это определение будет развито в данном разделе более точно и емко.

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

    Inprise поддерживает трехуровневую технологию, которая в классическом случае состоит из:

    Сервера базы данных на одной машине

    Сервера приложения на второй машине

    И простого клиента на третьей машине.

    Сервер - это инструмент, подобный InterBase, Oracle, Sybase, MS SQL Server и т. д. Сервер приложения и простой клиент создаются в Delphi. В своем традиционном варианте программное обеспечение для доступа к базам данных (напр. BDE, SQL*NET и т. д.) должно работать на той же машине, что и сервер приложения. Это, впрочем, не исключает множества других возможных вариантов.

    Разработчики Delphi реализовали в своем инструментарии эту технологию в виде набора компонентов, назвав ее распределенными наборами данных. Так мы и будем называть ее в этой статье.

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

    У распределенного набора данных Inprise есть, как минимум, две характерных особенности:

    Встроенные компоненты Delphi, делающие возможной реализацию системы.

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

    И компоненты Delphi и OLEnterprise включены в Midas.

    OLEnterprise вошел в состав Midas после того как Inprise приобрел Open Environment Corporation. Приобретение ОЕС также дало Inprise доступ к Object Broker, который позволяет случайным образом распределять выполнение задачи между несколькими серверами. Серверная часть приложения может быть установлена на несколько машин, и брокер при каждом подключении клиентов будет выбирать одну из них. К примеру, если вы имеете 100 клиентов и 3 сервера, брокер объекта случайным образом разобьет загрузку сети между тремя серверами так, что каждый будет иметь по 33 клиента (приблизительно).

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

    Подробное рассмотрение технологии: Использование распределенных наборов данных

    Рис. 1

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

    Для начала рассмотрим ядро компонентов Delphi. Позже в статье будут рассмотрены дополнительные инструменты OLEnterprise, полученные при покупке ОЕС.

    В Delphi имеются 4 основных инструмента, позволяющие работать с распределенными данными. Первые 2 из них - со стороны сервера.

    Удаленные модули данных (Remote Data Modules) - это почти то же самое, что и стандартные модули данных, с той разницей, что они помогают вам передавать данные не только вашему приложению, но и в микрорайоны сети. В целом, они преобразуют простой модуль данных в СОМ-объект, что позволяет вам получать доступ к модулю данных удаленного сервера посредством DCOM.

    Компонент TProvider находится в удаленном модуле данных точно так же, как объект TTable может находиться в стандартном модуле данных. Разница заключается в том, что TProvider предусматривает передачу таблицы по сети. Объекты Provider также включены в качестве свойств в объекты TTable и TQuery. Однако, если вы получаете доступ к ним как к независимым компонентам, вы сможете воспользоваться более гибкими и мощными средствами. В общем, вы подключаете компонент TProvider к TTable и TQuery так, что другие программы сети могут с помощью DCOM получать доступ к данным из TTable и TQuery. Задачей удаленного модуля данных является предоставление клиенту доступа к определенным провайдерам, доступным на сервере. Сначала клиент подключается к удаленному модулю данных, после этого запрашивает у него список доступных провайдеров его сервера.

    Со стороны клиента находятся два компонента, используемых для доступа к данным, поставляемым сервером:

    Компонент TRemoteServer дает клиенту возможность подключаться к серверу, а именно к удаленному модулю данных сервера. Более того, он подключается к интерфейсу СОМ, поддерживаемому удаленным модулем данных. Несмотря на то, что он так называется, TRemoteServer находится со стороны клиента, а не сервера. TRemoteServer – это компонент, которому известно, как просмотреть реестр при поиске доступных серверов. После того, как сервер будет найден, TRemoteServer соединяется с ним.

    Компонент TClientDataSet подключается к компоненту TRemoteServer, а затем присоединяется к определенному провайдеру сервера. Это предоставляет приложению клиента возможность подключиться к удаленному набору данных. Короче говоря, TClientDataSet играет ту же роль, что и TQuery или TTable, только он обслуживает данные с удаленного сайта. Представьте себе конфигурацию обычных TDatabase, TTable, TDataSource и TGrid, которые можно увидеть во многих стандартных Delphi-приложениях. В случае с удаленным набором данных вам нужно внести небольшие изменения в их конфигурацию, используя TRemoteServer, TClientDataSet, TDataSource и TBGrid. В этом новом сценарии TRemoteServer приблизительно играет роль TDatabase, а TClientDataSet делает то, что в обычном случае выполняется TQuery и TTable. Я не имею в виду абсолютное соответствие TDatabase и TRemoteServer, но определенное сходство между функциями, выполняемыми этими двумя компонентами, увидеть можно.

    Схема, приведенная в Рисунке 1, представляет архитектуру приложения, работающего с удаленным набором данных. В верхней половине расположена схема серверной части, состоящей из удаленного модуля данных, трех таблиц и трех провайдеров. Со стороны клиента вы видите TRemoteServer и три TClientDataSets. Присоединившись к набору данных клиента, вы получаете ряд источников данных и визуализированных средств управления. Учтите, что вам необходимо иметь по одному провайдеру и по одному набору данных клиента для каждой таблицы, которую вы хотите переслать. Нет необходимости говорить, что число таблиц, также как и множество других объектов, форм и т. п., можно изменять с любой стороны системы - целью в данном случае является обсуждение основных элементов предложенного сценария, состоящего из трех таблиц, необходимого для объяснения концепции работы удаленных наборов данных.

    Формирование стороны сервера в удаленном наборе данных

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

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

    - Для создания удаленного модуля данных используйте File|New.

    - Поместите в удаленный модуль данных один или более компонентов TProvider.

    - Привяжите TProvider к объекту TTable или TQuery.

    - Для создания методов интерфейса доступа к провайдеру удаленной машины, щелкните правой кнопкой мыши на TProvider.

    - Сохраните свою работу и один раз запустите сервер, чтобы его зарегистрировать.

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

    Я начал с одного модуля данных и одного удаленного модуля данных просто потому что это даст вам возможность начать с известного. Вы начинаете с хорошо известного приложения Delphi, после чего разместите все новые тексты программы в одном месте, в удаленном модуле данных. Наконец, вам нужно будет разместить объекты TTable и TQuery прямо в удаленном модуле данных - иначе у вас возникнут проблемы с синхронизацией.

    Теперь, когда общая последовательность действий намечена, углубимся в детали. Для начала создадим стандартное приложение базы данных, соединяющее таблицы Заказов и Заказчиков из базы данных DBDEMOS. Поместите таблицы в модуль данных и проделайте обычную последовательность действий. В клиентской части объедините эти таблицы системой связей “один-ко-многим”, как показано на рис.2. А вот со стороны сервера эти таблицы нужно просто поместить в форму и в модуль данных, не производя никаких других действий.

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

    Примечание: У вас есть выбор – какую часть логики разместить на сервере, среднем уровне своего приложения. Например, вы решаете, объединять таблицы по схеме “один-ко-многим” или нет - поскольку в большинстве случаев это делать необязательно. Если вы решите сделать это, то в дальнейшем, при запросе клиентом сервера, получите только данные из подчиненной таблицы, которая будет в этот момент видна на сервере. Возможно, этого вы и хотели, тем более, если подчиненная таблица очень велика. Однако, если и главная, и подчиненная таблица невелики, вы можете предпочесть получить данные из обеих таблиц, для чего лучше оставить их в разделенном виде на сервере и одновременно вызывать их только со стороны клиента. Даже если вы не объединили их в “один-ко-многим”-таблицу на сервере, по-прежнему имеется возможность контролировать количество одновременно поступающих записей, так что никаких существенных преимуществ объединение таблиц в схему “один-ко-многим” на сервере не дает.

    Рис.2. Интерфейс для простого приложения со связью “один-ко-многим” между таблицами Заказчиков и Заказов из базы данных DBDEMOS.

    Inprise может разместить на своем web-сайте (www.borland.com) дополнительную программу, которая будет нацелена на размещение большего объема логики на уровне сервера приложения, даже при использовании первой версии Midas.

    Создав собственно приложение, перейдем к удаленному модулю данных. Скомандуйте File|New и создайте удаленный модуль. У вас будет запрошено имя класса. Удаленным модулям данных лучше давать содержательные имена, например, CustOrdersRemoteData. Помните, что удаленный модуль данных - это, за исключением содержащегося в нем СОМ-интерфейса, обычный модуль данных.

    Поместите в модуль CustOrdersRemoteData два элемента управления Tprovider. Они находятся на той же странице Component Palette (Палитры Компонентов), что и TTable и TQuery. Для того, чтобы добавить модуль данных программы к используемым операторам удаленного модуля данных, выберите File | Use Unit. Теперь воспользуйтесь свойством DataSet объектов TProvider для присоединения провайдеров к объектам TTable. В окончательной же версии программы удаленный модуль непременно должен содержать запросы и таблицы. В случае, если у вас есть два удаленных модуля данных, имеющих доступ к одной таблице стандартного модуля данных, могут возникнуть проблемы с синхронизацией.

    Примечание: Не забывайте создавать узнаваемые имена для используемых компонентов. К примеру, я назвал таблицу, которая указывает на таблицу Заказчиков CustomerTable. Ее источник данных называется CustomerSource. Таблица с подчиненными данными названа OrderTable, а ее источник данных - OrderSource. Провайдер, который подключен к таблице Заказчиков, называется CustomerProvider и т. д.

    Итак, остановимся и посмотрим на дело рук своих.

    У нас есть основная форма с двумя таблицами.

    Также присутствует модуль данных с таблицами Customer и Orders. Дополнительно можно разместить таблицы прямо в удаленном модуле данных.

    Удаленный модуль данных с двумя TProvider-объектами. Один из них соединен с CustomerTable, а второй - с OrdersTable.

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

    TCustOrdersRemoteData = class(TDataModule, ICustOrdersRemoteData)
        CustomerProvider: TProvider;
        OrdersProvider: TProvider;
      private
        { Private declarations }
      public
        { Public declarations }
      end;

    Объявление класса такого типа предусматривает внедрение СОМ-объекта. СОМ объект называется ICustOrdersRemoteData. Вы должны заметить, что ICustOrdersRemoteData - это СОМ-объект с двойным интерфейсом. Это означает, что он доступен другим приложениям с помощью OLE-автоматизации, а также удаленным машинам с помощью DСОМ. ICustOrdersRemoteData имеет библиотеку соответствующего типа и объявляется в отдельном модуле, что объясняется позже в этом же разделе статьи. (Не стоит отчаиваться, если в данный момент вы не знакомы с COM и OLE. Разумеется, лучше разбираться и в том и в другом, но Delphi автоматизирует процесс их использования до такой степени, что вы сможете пользоваться удаленными модулями данных, не зная СОМ или DCOM.)

    Итак, сейчас вы можете получить доступ к объекту TCustOrdersRemoteData из второго приложения или второй машины. Однако, доступ к двум объектам TProvider, присутствующим в удаленном модуле данных, не автоматизирован. Чтобы предоставить клиентам доступ к провайдерам, вам нужно добавить свойства к СОМ-объекту ICustOrdersRemoteData.

    Чтобы двинуться дальше, нужно только щелкнуть правой кнопкой мыши по объекту TProvider и выбрать “Export XXX from data module” (“Экспортировать ХХХ из модуля данных”) из меню, где ХХХ - это имя вашего компонента TProvider. Код, сгенерированный этими действиями, объясняется ниже. Если в меню нет такого пункта, значит нужное действие уже произведено. Другими словами, после того, как вы выбрали опцию, она исчезает из меню. Те же действия доступны и для объектов TTable и TQuery, расположенных в удаленном модуле данных.

    Можно сделать по-другому. Войдите в меню Edit | Add to Interface, добавьте к созданному СОМ-объекту два свойства. Если вас коробит от примитивности собственных действий, и душа требует усложнения собственной жизни, воспользуйтесь Редактором Библиотеки Типов (Type Library Editor). Мышью щелкнуть проще, поэтому такой образ действий считается действиями по умолчанию.

    Если вы выберете Edit | Add to Interface, появится небольшое окно диалога, показанное на Рис. 3. Заполните поле Declaration (Объявление) строкой, которая выглядит:

    property CustomerDataSet: IProvider;

    Очевидно, что эта строка добавляет СОМ-объекту IcustOrdersRemoteData свойство, называемое CustomerDataSet. Это свойство предоставляет удаленным клиентам доступ к объекту CustomerProvider. Тот же результат получается щелчком правой кнопки мыши и выбором соответствующего пункта меню.

    Нажатием ОК в диалоговом окне Add to Interface вы автоматически получите вновь созданный в удаленном модуле данных метод. Этот же метод может быть автоматически создан щелчком правой кнопки мыши и выбором соответствующей опции из меню. Сам метод выглядит следующим образом:

    function TCustOrdersRemoteData.Get_CustomerDataSet: IProvider;
    begin
      Result := CustomerProvider.Provider;
    end;

    Объявление, пара begin...end и оператор присвоения в этом методе автоматически создаются IDE. Если для создания метода вы воспользовались Библиотекой Типов, вам нужно будет написать одну строку кода, который будет возвращать интерфейс IProvider от объекта CustomerProvider. После того, как вы добавили к СОМ-объекту CustomerDataSet, вы должны добавить OrdersDataSet, щелкнув правой кнопкой на компоненте. Метод, созданный после выполнения этих действий выглядит следующим образом:

    function TCustOrdersRemoteData.Get_OrdersDataSet: IProvider;
    begin
      Result :=  OrdersProvider.Provider;
    end;

    При непосредственном использовании для создания этого метода библиотеки типов, помните, что наряду с Get methods (Получить методы) есть и Set methods (Задать методы) для свойств. Вы можете оставить эту графу пустой. Другими словами, не будет ошибкой иметь для объекта TCustOrdersRemoteData пустой метод, выглядящий таким образом:

    function TCustOrdersRemoteData.Set_OrdersDataSet: IProvider;
    begin
    
    end;

    Примечание: В Delphi принято, что все классы, начинающиеся с буквы I, являются интерфейсами, т. е., СОМ-объекты. В приложении CustOrdersServer есть модуль, автоматически добавляемый к нему и называемый CustOrdersServer_TLB. Он содержит объявление ключевого СОМ-объекта, созданного классом TCustOrdersRemoteData:

    ICustOrdersRemoteData = interface(IDataBroker)
        [‘{EE5A6C61-BD6E-11D0-A356-0080C751528B}’]
        function Get_CustomerDataSet: IProvider; safecall;
        function Get_OrdersDataSet: IProvider; safecall;
        property CustomerDataSet: IProvider read Get_CustomerDataSet;
        property OrdersDataSet: IProvider read Get_OrdersDataSet;
      end;

    Этот интерфейс содержит два только что созданных для компонентов TProvider свойства. В данном случае свойства называются CustomerDataSet и OrdersDataSet. Удаленный клиент получает доступ к этим свойствам посредством удаленного модуля данных.

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

    Одновременно с тем, как в фоновом режиме компонуется этот СОМ-объект, конструируется и библиотека типов. Вы можете получить доступ к библиотеке типов, выбрав в меню View|Type Library.

    Рис. 3. Добавление интерфейса к СОМ-объекту.

    У объектов TTable и TQuery также есть провайдеры, присоединенные к ним, как обычные свойства. Вы можете пропустить оба объекта TProvider и написать следующий код:

    function TCustOrdersRemoteData.Get_CustomerDataSet: IProvider;
    begin
      Result := DMod.CustomerTable.Provider;
    end;
    function TCustOrdersRemoteData.Get_OrdersDataSet: IProvider;
    begin
      Result :=  DMod.OrdersTable.Provider;
    end;

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

    Причина, по которой вы можете захотеть использовать объект TProvider, описана в диалоговой справке таким образом: “Когда вы напрямую используете свойства провайдера компонента набора данных, Delphi создает IProvider и управляет им в фоновом режиме. При этом вы жертвуете управлением. Использование TProvider обеспечивает приложению более непосредственное управление по сравнению с использованием интерфейсом провайдера.” В целом необходимо знать о событиях, связанных с компонентом TProvider.

    Если теперь вы сохраните свою работу и один раз запустите программу для ее занесения системой в реестр, вы создадите сервер удаленных данных. Этот объект можно переслать на NT-сервер, где установлены BDE и некоторые библиотеки Delphi. Получить к нему доступ по сети можно используя OLEnterprise или в своем домене, если вы используете DCOM. (Реализации подключения будут обсуждаться в последнем разделе этой статьи.) При переносе на другую машину для регистрации объекта в системе он должен быть запущен с этой машины. Чтобы получить доступ к этому серверу через клиента, требуется только загрузочный файл клиента Delphi. Никакие инструменты баз данных, кроме 151 kb файла DBClient.dll, не нужны. (Со стороны сервера должен быть STDVCL32.DLL. Он будет установлен автоматически, если на сервере имеется Delphi.)

    Компоновка клиентского приложения удаленных данных

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

    Здесь дан краткий обзор действий, выполняемых при создании клиента:

    Откройте меню TRemoteServer и для подключения к серверу воспользуйтесь свойством ServerName. Эти серверы перечислены в раскрывающемся списке. Присвойте опции Connected значение True.

    Откройте окно управления TclientDataSet, и в окне свойства RemoteServer установите значение сервера, который вы только что определили. Задайте как свойство провайдера имя провайдера набора данных с сервера.

    Подключите к TClientDataSet элементы управления базой данных так же, как для TTable или TQuery.

    Запустите приложение.

    Теперь подробности. Для начала создайте новое приложение, поместите в него объект TRemoteServer и заполните его свойства так, как показано на рисунке 4.

    При подключении компонента TRemoteServer, ключевое свойство называется ServerName. Вы можете изменить ServerName, выбрав нужный пункт из раскрывающегося списка. Если этот список пуст или не содержит того, что вам нужно, значит, сервер в вашей системе в данный момент не зарегистрирован. Чтобы исправить положение запустите сервер с машины клиента один раз.

    Запуск сервера регистрирует этот элемент в Реестре Windows. Для того, чтобы увидеть запись реестра, запустите RegEdit.exe из меню Start | Run. Откройте HKEY_CLASSES_ROOT и найдите имя исполняемого файла, содержащего этот сервер. К примеру, ключ в данном случае называется CustOrdersServer.CustOrdersRemoteData. Первая половина ключа состоит из имени исполняемого файла сервера, а вторая половина - из имени, которое вы присвоили удаленному модулю данных и которое, кроме того, по умолчанию является именем экспортированного СОМ-интерфейса.

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

    Примечание: Выполняя этот пример, вы можете захотеть просто подключиться к удаленному серверу, расположенному на текущей машине. Чтобы сделать это, просто запустите сервер. Он зарегистрируется и появится в раскрывающемся списке свойства ServerName. Чтобы получить доступ к серверу на удаленной машине, запустите сервер по одному разу - сначала на локальной, а затем на удаленной машине, после чего обратитесь к машине через свойство ComputerName. Если вы установили сервисы DCOM на обеих машинах должным образом, то система начнет работать автоматически. При желании во время работы вашего сервера можно указать DCOM удаленную машину с помощью DComCfg.exe (можно использовать и OLEnterprise). Вы можете по своему желанию использовать и другие средства - не только запуск сервера, - для того, чтобы его зарегистрировать. Более подробно о возможностях соединения DCOM и OLEnterprise рассказано в этой статье позже.

    Теперь поместите в форму два TClientDatasets. Для того, чтобы подключить их к RemoteServer1, используйте раскрывающийся список свойств RemoteServer. С помощью раскрывающегося списка подсоедините один из TClientDataSet к CustomerDataSet на сервере через свойство ProviderName. Используйте свойство ProviderName для подключения второго набора данных к OrdersDataSet на сервере. Когда вы используете свойство ProviderName, свойство Connected объекта TRemoteServer приобретает значение True и удаленный сервер загружается в память.

    Присвойте свойству Active наборов данных клиента значение True, после чего подсоедините к ним источники данных и таблицы. После этого, для того, чтобы организовать одну связь (или более) между двумя таблицами, можно воспользоваться свойствами MasterSource и MasterFields. Приложение-клиент завершено. Можете запустить его и посмотреть на свой продукт в работе.

    Рис. 4. Свойства компонента TRemoteServer.

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

    CustomerClientDataSet.Active := True;
      OrdersClientDataSet.Active := True;

    Или, если вы предпочитаете:

      CustomerClientDataSet.Open;
      OrdersClientDataSet.Open;

    Присвоение значения True свойству Active набора данных приводит к автоматическому присвоению значения True свойству Connected TRemoteServer.

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

    Модель “портфеля”

    Модель портфеля основывается на двух методах TClientDataSet, называемых LoadFromFile и SaveToFile:

    CustomerClientDataSet.SaveToFile(‘Customer.dta’);

    CustomerClientDataSet.LoadFromFile(‘Customer.dta’);

    Две таблицы, которые нужно сохранить, следует сохранять (и считывать) в отдельных файлах

    procedure TForm1.BriefcaseSave1Click(Sender: TObject);
    begin
      CustomerClientDataSet.SaveToFile(CustomerFile);
      OrdersClientDataSet.SaveToFile(OrdersFile);
    end;

    Сам процесс записи на диск и чтения файлов чрезвычайно прост. Однако, для того, чтобы правильно использовать модель “портфеля” вам следует принять во внимание некоторые дополнительные моменты. Большинство этих моментов разъясняются в следующем разделе, PacketRecords.

    PacketRecords

    PacketRecords - это очень важное свойство TClientDataSet, рассмотрению которого следует посвятить некоторое время.

    Для того, чтобы модель “портфеля” работала корректно, лучше всего удостовериться, что файлы на сервере не объединены в систему со связями “один-ко-многим” и что вы присвоили свойству PacketRecords обоих ClientDataSet значение -1. Присвоение значения 0 приводит к работе с метаданными, -1 - со всеми данными, а значения некоторого положительного значения n - к работе с n записями за 1 запрос.

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

    Для работы с моделью “портфеля” обычно следует работать с целым набором данных. Это значит, что вам следует присвоить PacketRecords значение -1. (Разумеется, запрос сервера сможет отфильтровать для вас большую часть таблицы, но присвоить PacketRecords -1 все равно нужно, чтобы при запросе получить все данные.)

    Если вы хотите просто установить связи “один-ко-многим”, и вас не интересует модель “портфеля”, то для подчиненной таблицы присвойте значение PacketRecords 0, а для главной - -1. Эти установки вызывают все записи из главной таблицы и только метаданные - для подчиненной. После этого Delphi вызовет TClientDataSet.AppendData для получения только тех подчиненных записей, которые нужны при просмотре одной отдельной главной записи. Это удобно во многих случаях, но не то, что вам нужно, если вы используете модель “портфеля”. Вместо этого, при использовании модели “портфеля”, обычно нужно будет присваивать PacketRecords значение -1. При этом все останется по-прежнему и, кроме того, полный набор подчиненных данных на сервере будет доступен все время. Очевидно, что это непрактично при работе с очень большими наборами данных.

    Поскольку это очень важный вопрос, я покажу, как написать код, позволяющий вам отладить этот процесс. Следующий код представляет собой бессмысленный случай, когда вы сначала получаете метаданные, а затем – записи. Я назвал его бессмысленным, поскольку метаданные автоматически будут получены после вашего первого обращения к данным. Однако, представим себе, что у вас есть какие-то причины сначала получить метаданные, а затем - все записи. Вы должны написать программу, выглядящую таким образом:

    with ClientDataSet1 do begin
      Close;
      PacketRecords := 0;
      Open;
      PacketRecords := -1;
      GetNextPacket;
    end;

    Либо, если вы предпочитаете изыски, можно воспользоваться кодом признанного гуру Delphi Джоша Дэлби (Josh Dahlby):

    var
      RecsOut: Integer;
      V: OleVariant;
    begin
      CustomerClientDataSet.Close;
      V := CustomerClientDataSet.Provider.GetMetaData;
      CustomerClientDataSet.AppendData(V, False)
      V := CustomerClientDataSet.Provider.GetRecords(-1,RecsOut);
      CustomerClientDataSet.AppendData(V, True);
    end;

    В этом случае вы сначала закрываете набор данных клиента, а после этого получаете метаданные в OleVariant, используя функцию SaveToFile:. Теперь у вас не так много вариантов действий - результат работы этой функции можно направить напрямую к AppendData, иначе – С результатом работы этой функции вам больше нечего делать, кроме как направить его напрямую к AppendData, либо применить несколько низкоуровневых функций. В будущем — это стоит учесть — Inprise может предложить функции для облегчения доступа к информации пакетов данных, сохраненных в OleVariant.

    AppendData имеет два параметра. Первый - для получения данных с сервера, а второй - для того, чтобы поставить или не ставить EOF после получения данных. Помните, что использовать GetRecords или AppendData не надо, обычно вы будете пользоваться GetNextPacket. Более того, простейшим способом выполнить это действие является простой вызов Open или присвоение Active значения True. Я показал вам GetNextPacket и AppendData, чтобы у вас было больше гибкости в управлении процессом.

    Обновление и
    регенерация данных

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

    CustomerClientDataSet.ApplyUpdates(-1);

    Присвоение -1 этому методу означает, что вы хотите остановить процесс обновления при возникновении ошибки. В случае возникновения ошибок, ApplyUpdates возвращает значение, равное их количеству.

    Чтобы понять, что здесь происходит, вам следует разобраться, как Delphi кеширует все внесенные в набор данных изменения. Другими словами, сохраняются как оригинальные, так и обновленные записи. Если при вызове ApplyUpdates возникает сообщение об ошибке, имеется выбор - вернуть оригинальную запись или попытаться сохранить изменения. Обработка ошибок описывается в следующем разделе.

    Если вы успешно обновили данные на сервере, скорее всего, вам захочется обновить и свой набор данных - для учета изменений, внесённых другими пользователями. Здесь приведен типичный пример того, как выглядит в работе процесс в целом:

    if CustomerClientDataSet.ApplyUpdates(-1) = 0 then
        CustomerClientDataSet.Refresh;
      if OrdersClientDataSet.ApplyUpdates(-1) = 0 then
        OrdersClientDataSet.Refresh;

    Обработка ошибок

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

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

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

    Для того, чтобы найти эту форму, выберите из меню File | New, перейдите в странице Dialogs и выберите Copy the Reconcile Error Dialog. Сохраните диалог в том же каталоге, что и ваш текущий проект и удалите его из файлов, которые автоматически создались при запуске. Чтобы сделать это выберите Project | Options | Forms из меню Delphi.

    Рис. 5. Диалог cогласования ошибки (OnReconcile Error Dialog) из банка объектов Delphi во время работы.

    Добавьте Reconcile Error Dialog к списку используемых форм вашего проекта. Во многих случаях это будет основная форма вашего проекта. Теперь добавьте следующее на случай возникновения события OnReconcileError:

    procedure TForm1.CustomerClientDataSetReconcileError(
      DataSet: TClientDataSet; E: EReconcileError; UpdateKind: TUpdateKind;
      var Action: TReconcileAction);
    begin
      HandleReconcileError(DataSet, UpdateKind, E);
    end;

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

    Таблица в центре формы показывает имя поля, в котором возникла ошибка. Modified Value (Измененное Значение) – значение, которое приложение-клиент намеревалось вставить в запись. Conflicting value (Конфликтующее значение) - это то значение, которое “другой парень”, опередивший вас, уже вставил в запись. Original Value – это значение записи до обоих обновлений. Как вы видите, пользователь имеет возможность Пропустить (Skip), Прервать (Cancel), Исправить (Correct), Обновить (Refresh) или Объединить (Merge) данные.

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

    На этом позвольте закончить разговор о процессе создания клиентской и серверной частей приложения. Дальше речь пойдет об организации подключения. Как вы помните, статью сопровождает пара программ — CustOrdersClient и CustOrdersServer. Если у вас нет этих примеров, их можно найти на web-сайте: users.aol.com/charliecal.

    Удаленное соединение

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

    DLL, необходимые для каждой стороны

    Когда вы устанавливаете сервер или клиента, каждая сторона должна содержать несколько файлов.

    Со стороны сервера нужны:

    Все DLL из BDE4.

    DBCLIENT.DLL

    IDPROV32.DLL

    STDVCL32.DLL

    DBCLIENT и IDPROV32 включены только в клиент/сервер версию Delphi, и, чтобы иметь их в своей системе, вам необходимо ответить на вопрос о лицензионном соглашении “Да”.

    DBCLIENT и STDVCL32 должны быть зарегистрированы, после чего они будут внесены в реестр. Чтобы зарегистрировать их, вы можете использовать Microsoft REGSVR32.EXE или TRegServ из демо-версии Delphi.

    DBCLIENT.DLL – это DLL, необходимая со стороны клиента. STDVCL32.DLL не обязательна, но лучше ее иметь. Это библиотека типов для IProvider и IDataBroker. Файл DBCLIENT должен быть зарегистрирован со стороны клиента. Если он содержится в path, то он автоматически зарегистрируется при запуске приложения.

    Использование DCOM

    Если Windows NT устанавливалась со стандартными параметрами, то для установки DCOM вам не нужно ничего делать. Я думаю, что следующая версия Windows, называемая Memphis, предоставит вам такой же сервис.

    Если вы работаете с Windows 95, то вам нужно переписать DCOM для Windows 95 с web-сайта Microsoft. На момент написания статьи вы можете найти его на web-сайте Microsoft (www.microsoft.com) в разделе “Free Downloads”. Не забудьте захватить оттуда же DCOMCfg для Windows 95.

    Настройка DCOM для Windows 95 проста; все, что вам нужно будет сделать - запустить полученную с web-сайта программу инсталляции. Однако сделать так, чтобы DCOM корректно работал под Windows 95 или Windows NT более сложно.

    Использование OLEnterprise

    OLEnterprise является альтернативой DCOM. Если вы используете OLEnterprise, вам не нужно иметь в своей системе DCOM.

    По сравнению с DCOM OLEnterprise имеет ряд преимуществ.

    Это приложение позволяет осуществлять соединение между двумя машинами Windows 95, даже если сервер NT недоступен. В случае с DCOM соединения без сервера невозможны. Более того, в случае DCOM соединения между двумя машинами Windows 95 даже при наличии сервера невозможны или весьма затруднительны. В общем, DCOM работает надежно только когда вы размещаете серверы на машинах NT. С помощью DCOM вы можете подключать машину Windows 95 к машине NT, но не наоборот, и не получите надежной связи между двумя машинами Windows 95. (Я сказал, что невозможно выполнить удаленное соединение сервера с машиной Windows 95, но я немного сомневаюсь, поскольку эта область очень слабо документирована, так что я могу и ошибаться в некоторых местах. Кроме того, я думаю, что такое соотношение продуктов изменится после доработки DCOM. В любом случае, я никогда не видел того, кто смог бы осуществить соединение, зато пробовало сделать это множество талантливых программистов.)

    OLEnterprise имеет Object Broker (Брокер Объекта), который позволит вам распределять загрузку соединений между многими машинами. Вообще, каждый раз при подключении пользователя к базе данных, он может быть перенаправлен по случайному алгоритму на доступный сервер, чем и достигается распределение загрузки между многими серверами. Копирование данных между серверами OLEnterprise не предусматривает по своей природе, но это входит в обязанности самих серверов.

    OLEnterprise имеет возможность защитного резервирования, которое реализуется написанием нескольких строк кода.

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

    procedure TForm1.Button2Click(Sender: TObject);
    begin
     try
      ClientDataset1.ApplyUpdates(-1);
     except
      on eOle : EOleException do begin
        Case eOle.Errorcode of
          -2147023169 : // I dont know the const name
            begin
              // Handle RPC failure by resetting server
              RemoteServer1.Connected := False;
              RemoteServer1.connected := TRUE;
              ClientDataset1.ApplyUpdates(-1);
            end;
          else showMessage(intToStr(eOle.errorCode));
        end;
      end;
     end;
    end;

    Под Windows 95 не стоит устанавливать OLEnterprise в каталог по умолчанию. Вместо этого установите его в корневой каталог и постарайтесь не применять длинные имена файлов в пути (path) каталога. Все проблемы, связанные с OLEnterprise и длинными именами файлов, используемыми на машинах Windows 95 будут устранены в будущих версиях продукта. Для того, чтобы избежать возможных проблем, просто установите в каталог, в котором нет длинных имен файлов.

    В инструментарии OLEnterprise есть четыре ключевых элемента:

    Брокер объектов (Object Broker), называемый Broker.exe, и запускаемый на машине сервера перед загрузкой Фабрики Объектов (Object Factory). Если вы запускаете его с опцией -D, он будет выдавать отладочные данные, которые помогут вам понять процесс соединений, и удостовериться, что соединения производятся в правильной последовательности и в нужное время.

    Конфигурационная утилита OLEnterprise называется OLECFG.exe. Эта утилита используется для выбора - использовать Object Broker или нет, и на какой машине запускать Object Broker. Случайное распределение соединений входит именно в обязанности брокера, так что эта утилита необязательна при выполнении соединений. Полезная, но не необходимая утилита.

    Фабрика Объектов (Object Factory), называемая ObjFact.exe. Эта утилита является ядром системы и вы должны запустить ее на сервере, иначе OLEnterprise работать не будет. Однако, для того, чтобы выполнять соединения, вам запускать эту утилиту не нужно.

    Проводник Объектов (Object Explorer), называемый OLEntExp.exe. Эта программа играет роль, подобную DCOMCFG.exe. Она позволяет загружать доступные системе объекты, экспортировать объекты с сервера, импортировать объекты из удаленной системы. Вы должны помнить, что проводник использует реестр как основной источник информации об объектах системы. Если вы хотя бы один раз запустили на машине сервер, проводник найдет объект в реестре и, после этого, можно будет использовать меню для импорта или экспорта объектов. При работе с машины клиента и использовании проводника для поиска программ на удаленных машинах, вы должны быть уверены, что воспользовались конфигурационной утилитой OLEnterprise для указания местоположения используемого в данный момент брокера объекта. Если вы запустили проводник и он очень долго не откликается или все время “подвисает”, то, скорее всего, машина ищет брокера, но найти его не может. Работа программы может занять несколько минут, прежде чем истечет время, выделенное на поиск, и управление будет передано пользователю. То же самое произойдет, если вы щелкнете в левой панели программы на опции Object Broker. Один щелчок — и поиск брокера начат. Если брокер не найден, используйте для указания его местоположения конфигурационную утилиту, либо перейдите на удаленную машину и удостоверьтесь, что брокер запущен. Если вы запустите брокер с опцией -D, то вы увидите его отчетную информацию, при запуске на той же машине Object Factory или при попытке подключиться к нему с удаленной машины.

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


    Copyright © 1994-2016 ООО "К-Пресс"