MIDAS. Практическое применение

select * from REP_INOUT(:FromDate, :ToDate) order by TO_NAMEПри этом необходимо учитывать, что данные из этой процедуры получаются совершенно не в

MIDAS. Практическое применение

Информация

Компьютеры, программирование

Другие материалы по предмету

Компьютеры, программирование

Сдать работу со 100% гаранией
unlock;

end;

end;

 

function TrdmCommon.Get_ClientName(ClientID: Integer): WideString;

begin

lock;

try

with spClientFullName do

begin

paramByName('ID').AsInteger := ClientID;

ExecProc;

Result := paramByName('FULL_NAME').AsString;

end;

finally

unlock;

end;

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

Рисунок 3.

Здесь уже все немного сложнее. Разумеется, здесь тоже есть соединение с базой ibdDoc, настроенное на сервер БД. Хранимая процедура spNewID выдает на этот раз номер для нового документа, используя процедуру DOC_TITLE_ID, аналогичную процедуре CLIENT_ID.

На этот раз в модуле данных, помимо компонентов запросов к серверу, присутствуют два компонента TСlientDataSet и два провайдера данных. Эти дополнительные компоненты предназначены именно для организации расчетов на сервере. Поскольку, как мы договорились, на сервере приложений должна рассчитываться сумма документа, то на нем должно быть известно содержимое документа до того, как оно будет сохранено в БД. Разумеется, это можно осуществить, используя события провайдера для предварительной обработки пакета изменений, поступившего от клиентской части, но мне хотелось показать возможность организации работы с документом как с полноценным объектом.

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

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

К одному компоненту транзакции ibtDoc присоединено на этот раз два запроса ibqTitle и ibqBody, соответственно выбирающих одну строку заголовка документа (select * from DOC_TITLE where DOC_ID = :DocID) и все строки этого документа (select * from DOC_BODY where DOC_ID = :DocID).

ПРИМЕЧАНИЕ

Хотя MIDAS требует наличия своей IBTransaction для каждой пары компонентов "IBQuery-провайдер", в данном случае это необязательно. Провайдеры не будут начинать и завершать транзакции, открываться и закрываться транзакция будет явно, в соответствующих методах.К этим запросам присоединены провайдеры dspTitleInner и dspBodyInner, назначение которых получить данные с сервера БД и передать их в соответствующие ClientDataSet. Свойство Exported у этих провайдеров установлено в false, они нужны только внутри сервера приложений, и видеть их на клиентской части незачем. Соответственно, клиентский набор данных cdsTitle (компонент TClientDataSet) получает одну строку заголовка из dspTitle и cdsBody, содержимое документа из dspBody.

Для того, чтобы клиентская часть могла получать и изменять данные документа, к клиентским наборам данных cdsTitle и cdsBody присоединены провайдеры данных, dspTitle и dspBody, соответственно. Свойству Exported этих провайдеров оставлено значение по умолчанию, True, зато свойство ResolveToDataSet установлено в True, для того, чтобы эти провайдеры не пытались работать с ClientDataSet с помощью запросов. Таким образом, клиентская часть может получать и изменять данные не из TIBQuery, но из TClientDataSet, причем совершенно об этом не догадываясь. По команде с клиентской части изменения, передаются серверу приложений, который и сохраняет их в БД.

Теперь посмотрим, что нам нужно для подобной реализации. Функции для синхронизации обработки документов RegisterDoc и UnregisterDoc уже есть, нужно их только использовать. С их помощью гарантируется, что одновременно один и тот же документ редактироваться не будет, поэтому у провайдеров данных dspTitleInner и dspTitleBody достаточно установить UpdateMode = upWhereKeyOnly, и указать ключевые поля у запросов. Содержимое документа может состоять из нескольких строк, поэтому у dspBodyInner и dspBody нужно установить флаг poAllowMultiRecordUpdates. Теперь нужно разобраться с полями клиентских наборов данных, установив у них соответствующие свойства. Я остановлюсь здесь только на свойстве ProviderFlags. Поскольку поле «Ссылка на документ» (DOC_ID) на клиентской части не нужно, ему можно задать флаг pfHidden. Разумеется, у всех ключевых полей (DOC_ID и LINE_NUM) и в наборе данных заголовка, и в содержимом документа надо указать флаг pfInKey. У провайдеров dspTitle и dspBody нужно установить политику обновления UpdateMode = upWhereKeyOnly, клиентская часть у модуля данных одна, и другие значения совершенно ни к чему.

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

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

osInactive: данных нет, документ не редактируется,

osInsert: создан новый документ и

osUpdate происходит изменение существующего документа.

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

Переход из одного состояния в другое должен обеспечиваться соответствующими методами. Я назвал эти методы DoInactiveState (перевод в неактивное состояние), DoOpen (открыть существующий документ) и DoCreateNew (создание нового документа). При редактировании или добавлении документа нужно знать его уникальный номер, записываемый в поле DOC_ID. Для этого достаточно объявить в секции private переменную FDocID: integer, которая и будет его хранить.

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

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

procedure DoInactiveState;

procedure DoCreateNew;

procedure DoOpen(DocID: integer);Рассмотрим их по порядку.

procedure TrdmDoc.DoInactiveState;

begin

UnregisterDoc(FDocID);

FDocID := 0;

cdsTitle.Active := False;

cdsBody.Active := False;

ibtDoc.Active := False;

FState := osInactive;

end;Процедура DoInactiveState удаляет документ из списка редактируемых, закрывает все клиентские наборы данных, а также производит откат транзакции (если она была активна).

procedure TrdmDoc.DoOpen(DocID: Integer);

begin

if DocID = 0 then Exit;

try

if not RegisterDoc(DocID) then

raise Exception.Create('Документ редактируется');

FDocID := DocID; // и только здесь, иначе DoInactiveState удалит документ

ibdDocs.Connected := True;

ibtDoc.StartTransaction;

with cdsTitle do

begin

params.paramByName('DocID').AsInteger := FDocID;

Active := True;

if BOF and EOF then

raise Exception.Create('Документ не найден');

end;

with cdsBody do

begin

params.paramByName('DocID').AsInteger := FDocID;

Active := True;

end;

FState := osUpdate;

ibtDoc.Commit;

except

DoInactiveState;

raise;

end;

end;DoOpen предназначена для открытия существующего документа, идентификатор DOC_ID которого равен входному параметру DocID. Первым делом с помощью RegisterDoc производится проверка того, что документ в данный момент не редактируется. Затем идентификатор документа запоминается, и в клиентские наборы данных загружаются данные документа. В случае ошибки состояние документа переводится в osInactive.

procedure TrdmDoc.DoCreateNew;

var

NewDocID: Integer;

begin

try

NewDocID := NewID;

if not RegisterDoc(NewDocID) then

raise Exception.Create('Документ редактируется');

FDocID := NewDocID;

ibdDocs.Connected := True;

ibtDoc.StartTransaction;

with cdsTitle do

begin

params.paramByName('DocID').AsInteger := FDocID;

Active := True;

Append;

Post;

end;

with cdsBody do

begin

params.paramByName('DocID').AsInteger := FDocID;

Active := True;

end;

ibtDoc.Commit;

FState := osInsert;

except

DoInactiveState;

raise;

end;

end;Процедура DoCreateNew предназначена для создания нового документа. Она практически аналогична предыдущей, за исключением того, что идентификатор документа получается от сервера БД с помощью процедуры NewID, которая обращается к хранимой процедуре на сервере. Реализация процедуры DoCreateNew

Похожие работы

<< < 1 2 3 4 5 6 7 8 > >>