Logo Море(!) аналитической информации!
IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware
VPS/VDS серверы. 30 локаций на выбор

Серверы VPS/VDS с большим диском

Хорошие условия для реселлеров

4VPS.SU - VPS в 17-ти странах

2Gbit/s безлимит

Современное железо!

Бесплатный конструктор сайтов и Landing Page

Хостинг с DDoS защитой от 2.5$ + Бесплатный SSL и Домен

SSD VPS в Нидерландах под различные задачи от 2.6$

✅ Дешевый VPS-хостинг на AMD EPYC: 1vCore, 3GB DDR4, 15GB NVMe всего за €3,50!

🔥 Anti-DDoS защита 12 Тбит/с!

4. Пример 1: создание простейшего серверного объекта

Предварительная подготовка

Прежде чем приступить к созданию серверных объектов, предназначенных для работы под управлением MTS, следует убедиться в том, что сам MTS и Delphi 4 установлены корректно. Во-первых, NT Option Pack, содержащий MTS, следует обязательно установить до установки Delphi. Тогда в процессе установки Delphi при обнаружении инсталляционной программой установленной копии MTS в него будет добавлен специальный "пакет" (package) BDE-MTS, содержащий объект BdeMTSDispenser (его можно обнаружить с помощью MTS Explorer, рис. 2).

Рис. 2. BdeMTSDispenser, зарегистрированный в Microsoft Transaction Server

Следует также убедиться, что данный объект поддерживает транзакции. C этой целью нужно из контекстного меню объекта BDEDispenser выбрать опцию Properties и в появившейся диалоговой панели выбрать закладку Transaction (рис. 3):

Рис. 3. Установка поддержки транзакций объектом MTS.

Далее следует запустить BDE Administrator, открыть страницу Configuration, выбрать раздел System/Init и установить значение параметра MTS POOLING равным TRUE. Только при этом значении данного параметра возможна поддержка транзакций и коллективное использование соединений с базами данных, доступных с помощью BDE (рис. 4).

Рис. 4. Установка опции MTS POOLING для коллективного использования соединений с базами данных

Для выполнения описанных ниже примеров следует создать три таблицы в трех разных базах данных. Первая из них требует наличия сервера IB Database (он входит в комплект поставки Delphi 4) и должна быть создана в базе данных IBLOCAL с помощью следующего скрипта:


CREATE TABLE STOCKTABLE (
  GOODSNAME CHAR(30),
  PRICE FLOAT,
  GOODSNUMBER INTEGER NOT NULL)

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

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


CREATE GENERATOR GEN1;
SET GENERATOR GEN1 TO 5

Можно ввести в таблицу какие-либо данные (рис. 5):

Рис. 5. Таблица STOCKTABLE, созданная на сервере IB Database

Следующую таблицу создадим в формате Paradox (например, с помощью Database Desktop) и поместим в базу данных DBDEMOS, поставляемую вместе с Delphi. Структура этой таблицы приведена на рис. 6.

Рис. 6. Структура таблицы delivery.db

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

И, наконец, третья таблица формата dBase должна быть создана в произвольном каталоге, и этот каталог должен быть описан как псевдоним PAYDB (рис. 7):

Рис. 7. структура таблицы ord.dbf

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

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

4.2. Создание серверного объекта

Для создания серверного объекта следует со страницы Multitier репозитария объектов выбрать пиктограмму MTS Data Module (рис. 8).

Рис. 8. Выбор MTS Data Module из репозитария объектов.

Далее в появившейся диалоговой панели MTS Data Module Wizard следует ввести имя класса и выбрать способ работы с транзакциями (рис. 9).

Рис. 9. MTS Data Module Wizard

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

В созданный модуль данных поместим один компонент TSession, один компонент TDatabase, один компонент TProvider, один компонент TTable, два компонента TQuery (рис. 10):

Рис. 10. Модуль данных серверного объекта StockDM1, управляющего таблицей STOCKTABLE

Свойство AutoSessionName компонента TSession установим равным True. Свойство SessionName компонента TDatabase установим равным имени компонента TSession (это делается для того, чтобы не было конфликтов между именами различных пользовательских сессий внутри процесса MTS при создании нескольких однотипных объектов). Свяжем компонент TDatabase с псевдонимом IBLOCAL, установив его свойство LoginPrompt равным False (вполне очевидно, что в серверном объекте диалог ввода пароля появляться не должен - ведь клиентское приложение, использующее его, может находиться на удаленном компьютере, рис. 11).

Рис. 11. Параметры компонента TDatabase серверного объекта StockDM1

Свяжем компоненты TTable и TQuery с компонентом TDatabase, и в качестве значения свойства TableName выберем имя вновь созданной таблицы STOCKTABLE. Свяжем компонент TProvider с компонентом TTable.

Далее установим значения свойств SQL компонентов TQuery:

insert into STOCKTABLE values(:a,:b,GEN_ID(GEN1,1))

и

delete from STOCKTABLE where GOODSNUMBER=:C

Первое из SQL-предложений добавляет запись в таблицу STOCKTABLE с автоматической генерацией первичного ключа. Второе удаляет запись на основе значения первичного ключа.

Обратите внимание: ни компонент TTable, ни компонент TProvider не следует экспортировать из модуля данных. Причина этого заключается в том, что подобные экспортированные объекты хранят состояние данных, с которыми работает конкретное клиентское приложение, поэтому при коллективном использовании таких объектов могут возникнуть коллизии. По этой причине сведения о состоянии данных для конкретных клиентов хранятся менеджером разделяемых свойств MTS (MTS shared property manager), а в модулях данных между вызовами методов эти сведения присутствовать не должны. Поэтому вместо экспорта объектов из модуля данных мы создадим метод GetGoods, предоставляющий эти данные клиентскому приложению по его запросу.

После этого можно отредактировать библиотеку типов. Добавим к ней методы GetGoods для передачи клиентскому приложению содержимого таблицы и методы AddGoods и DeleteGoods для выполнения запросов, содержащихся в компонентах TQuery (рис. 12):

Рис. 12. Библиотека типов серверного объекта

Реализация созданных методов приведена ниже:

unit st1;
//Simple MTS server
//By N.Elmanova
//01.12.1998

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ComServ, ComObj, VCLCom, StdVcl, BdeProv, BdeMts, DataBkr, DBClient,
  MtsRdm, Mtx, st_TLB, DBTables, Provider, Db;

type
  TStockDM1 = class(TMtsDataModule, IStockDM1)
    stable: TTable;
    StProvider: TProvider;
    Database1: TDatabase;
    Query1: TQuery;
    Query2: TQuery;
    Session3: TSession;
  private
    { Private declarations }
  public
    { Public declarations }
  protected
    function GetGoods: OleVariant; safecall;
    procedure AddGoods(const Gname: WideString; Gprice: Double); safecall;
    procedure DeleteGoods(Gnumber: Integer); safecall;
  end;

var
  StockDM1: TStockDM1;

implementation

{$R *.DFM}

function TStockDM1.GetGoods: OleVariant;
begin
  Result:=StProvider.Data;
  SetComplete;
end;
procedure TStockDM1.AddGoods(const Gname: WideString; Gprice: Double);
begin
try
  Database1.Open;
  Query1.Params[0].Value:=Gname;
  Query1.Params[1].Value:=Gprice;
  Query1.Prepare;
  Query1.ExecSQL;
  Database1.Сlose;
  SetComplete;
except
  SetAbort;
  raise;
end;
end;

procedure TStockDM1.DeleteGoods(Gnumber: Integer);
begin
try
  Database1.Open;
  Stable.open;
  Stable.SetRangeStart;
  Stable.Fields[2].AsInteger:=Gnumber;
  Stable.SetRangeEnd;
  Stable.Fields[2].AsInteger:=Gnumber;
  Stable.ApplyRange;
  Stable.Delete;
  Database1.Close;
  SetComplete;
except
  SetAbort;
  raise;
end;
end;

initialization
  TComponentFactory.Create(ComServer, TStockDM1,
    Class_StockDM1, ciMultiInstance, tmApartment);
end.

Прокомментируем приведенный выше код. Напомним, что мы не экспортировали компонент TProvider или компонент TTable из модуля данных, а вместо этого создали метод GetGoods, предоставляющий клиентскому приложению данные из таблицы динамически, позволяя не хранить сведения о состоянии данных в серверном объекте. Метод GetGoods представляет собой так называемый "stateless code", а сам модуль данных в этом случае представляет собой так называемый "stateless object" (об этом было рассказано выше). Именно отсутствие статических данных, связанных с конкретным клиентом, позволит в дальнейшем сделать этот модуль данных разделяемым ресурсом.

Вызов метода SetComplete внутри метода GetGoods означает, что модуль данных более не нуждается в хранении информации о состоянии и может быть деактивирован. Если модуль данных представляет собой одну из частей распределенной транзакции (пока это не так, но чуть позже он станет одной из таких частей), этот метод означает, что данная часть транзакции может быть завершена (естественно, при условии, что все другие части этой транзакции также могут быть завершены; в противном случае произойдет откат транзакции, в том числе и данной части). Если же MTS начинает транзакцию автоматически при создании модуля данных (опция Requires a transaction), вызов метода SetComplete приведет к попытке ее завершения.

Перед компиляцией проекта рекомендуется убедиться, что компоненты TDatabase, TSession, TTable неактивны.

Далее следует выбрать из меню Delphi опцию Run/Install MTS Objects. После этого следует выбрать или ввести имя "пакета" MTS (MTS package). После этого объект окажется зарегистрированным в MTS (рис. 13):

Рис. 13. Серверный объект StockDM1, зарегистрированный в MTS

Следует обратить внимание на то, что регистрировать серверный объект как обычный COM-сервер не следует - в роли сервера с точки зрения реестра для клиента в данном случае выступает MTS, а не созданная библиотека.

При попытках внесения неоднократных изменений в код серверного объекта и запуска сервера с помощью собственно MTS или обращающихся к нему клиентов могут возникнуть проблемы. В частности, может оказаться, что при попытке компиляции библиотеки появляется сообщение о невозможности создания выходного файла. Это может быть связано с тем, что какие-то экземпляры объекта уже созданы в адресном пространстве MTS, поэтому файл оказался заблокированным. В этом случае следует в MTS Explorer найти соответствующий "пакет" и из его контекстного меню выбрать опцию Shut down. Можно также выбрать в MTS Explorer раздел My Computer и из его контекстного меню выбрать опцию Shut down server processes, прекратив таким образом существование всех серверных объектов. Кроме того, можно уменьшить время существования серверного объекта в неактивном состоянии. Это делается с помощью выбора пункта контекстного меню Properties соответствующего пакета и установкой необходимого значения свойства Shut down after being idle for… на странице Advanced появившейся диалоговой панели.

4.3. Создание клиентского приложения

Зарегистрировав созданный серверный объект в MTS, можно приступить к созданию клиентского приложения. Добавим в имеющуюся программную группу новый проект (или просто создадим новый проект). На главную форму будущего приложения поместим компоненты TDCOMConnection, TClientDataSet, TDataSourse, TDBGrid, два компонента TEdit, два компонента TLabel и три кнопки (рис. 14)

Рис. 14. Клиентское приложение для тестирования серверного объекта

В качестве свойства ServerName компонента TDCOMConnection выберем имя только что созданного нами серверного объекта (оно будет доступно, если объект зарегистрирован в MTS, и при его выборе свойство GUID будет установлено автоматически). Если же клиент разрабатывается на удаленном компьютере, следует заполнить свойства GUID и ComputerName, причем в качестве свойства GUID следует выбирать не идентификатор сервера, а идентификатор соответствующего класса объектов - так называемый CoClass GUID. Причина этого очевидна - в общем случае динамически загружаемая библиотека может содержать несколько классов серверных объектов.

Свяжем компонент TClientDataSet с компонентом TDCOMConnection, выбрав его свойство RemoteServer из единственной позиции выпадающего списка. Свойство ProviderName оставим пустым - ведь при создании сервера мы не экспортировали никаких объектов. Далее свяжем компонент TDataSource с компонентом TClientDataSet, и, наконец, свяжем компонент TDBGrid с компонентом TDataSource. Убедимся, что все невизуальные компоненты неактивны - до возникновения реальной необходимости получить какие-либо данные серверные объекты не должны быть созданы, поэтому установка свойств Active или Connected должна быть произведена на этапе выполнения.

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

unit stscl1;
//Client of Simple MTS server
//By N.Elmanova
//01.12.1998

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Db, DBClient, MConnect, Grids, DBGrids, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    DBGrid1: TDBGrid;
    DCOMConnection1: TDCOMConnection;
    ClientDataSet1: TClientDataSet;
    DataSource1: TDataSource;
    Button2: TButton;
    Button3: TButton;
    Label2: TLabel;
    Label1: TLabel;
    Edit1: TEdit;
    Edit2: TEdit;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);


begin
 DCOMConnection1.Connected:=True;
 ClientDataSet1.Data:=DCOMConnection1.AppServer.GetGoods;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
try
 DCOMConnection1.AppServer.AddGoods(Edit1.Text,
 StrToInt(Edit2.Text));
Except
 ShowMessage('Не могу добавить запись');
end;
 end;

procedure TForm1.Button3Click(Sender: TObject);
var recnum:integer;
begin
recnum:=ClientDataSet1.FieldByName('GOODSNUMBER').Value;
try
 DCOMConnection1.AppServer.DeleteGoods(recnum);
except
 ShowMessage('Не могу удалить запись');
end;
end;

end.

Запустив клиентское приложение, протестируем сервер, попытавшись добавить или удалить записи (заодно проверим правильность текста созданных нами SQL-запросов). Обратите внимание: для контроля изменений в базе данных следует нажимать на кнопку Connect & Refresh - обработчик соответствующего события вызывает серверный метод, выполняющий выгрузку данных из таблицы и передачу их клиентскому приложению (рис. 15):

Рис. 15. Тестирование серверного объекта

<< Назад | Содержание | Вперед >>

 

VPS в 21 локации

От 104 рублей в месяц

Безлимитный трафик. Защита от ДДоС.

🔥 VPS до 5.7 ГГц под любые задачи с AntiDDoS в 7 локациях

💸 Гифткод CITFORUM (250р на баланс) и попробуйте уже сейчас!

🛒 Скидка 15% на первый платеж (в течение 24ч)

Скидка до 20% на услуги дата-центра. Аренда серверной стойки. Colocation от 1U!

Миграция в облако #SotelCloud. Виртуальный сервер в облаке. Выбрать конфигурацию на сайте!

Виртуальная АТС для вашего бизнеса. Приветственные бонусы для новых клиентов!

Виртуальные VPS серверы в РФ и ЕС

Dedicated серверы в РФ и ЕС

По промокоду CITFORUM скидка 30% на заказ VPS\VDS

Новости мира IT:

Архив новостей

IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

Информация для рекламодателей PR-акции, размещение рекламы — adv@citforum.ru,
тел. +7 495 7861149
Пресс-релизы — pr@citforum.ru
Обратная связь
Информация для авторов
Rambler's Top100 TopList liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня This Web server launched on February 24, 1997
Copyright © 1997-2000 CIT, © 2001-2019 CIT Forum
Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее...