Работа с Datasnap заключается в запросе от сервера данных и вызове серверных методов, например:
— Запросить список товаров с сервера (dataset )
— Создать новый документ
— Добавить в него позиции
— Закрыть документ
Иногда при добавлении позиции необходимо сделать дополнительный выбор, например выбрать партию товара или разрез.
Это можно сделать через возврат кода ошибки (что-то вроде Prepare/Execute), чтобы клиент запросил пользователя, а затем попробовал снова выполнить операцию.
Или же дать серверу возможность запросить клиента непосредственно во время операции все необходимые ему данные.
Что можно сделать:
Первый вариант требует перечня кодов ошибок с соответствующей обработкой.
Второй вариант требует зарегистрировать клиенту функции обратного вызова, которые может вызывать сервер, а также при каждом обращении к серверу иметь в виду, что в ответ сервер может что-то потребовать от клиента (а это грозит блокировкой, если запрос требует реакции от пользователя). Для того, чтобы не было блокировок при однопоточном пользовательском интерфейсе, обращения к серверу нужно делать в отдельном потоке — тогда пришедший от сервера вызов можно синхронизировать с основным потоком (который уже не ждет возврата управления от исходного вызова серверного метода). А также не забывать сообщать серверу ID клиента.
Вызов:
procedure TForm2.btnRegisterWareClick(Sender: TObject); begin TThread.CreateAnonymousThread( procedure() begin clmClient.ServerMethods1Client.RegisterWare(seWareID.Value, clmClient.DSClientCallbackChannelManager1.ManagerId) end).Start; end;
Сама функция обратного вызова:
TmyCallback = class(TDBXCallback) protected FSelectedString: Integer; procedure SelectString(const Arg: TJSONValue); public function Execute(const Arg: TJSONValue): TJSONValue; override; end; { TmyCallback } function TmyCallback.Execute(const Arg: TJSONValue): TJSONValue; begin TThread.Synchronize(nil, procedure() // запрос данных у пользователя - только в основном потоке begin SelectString(Arg); end); Result := TJSONNumber.Create(FSelectedString); end; procedure TmyCallback.SelectString(const Arg: TJSONValue); var strs: TStringList; enum: TJSONPairEnumerator; val, str: string; begin enum := TJSONObject(Arg).GetEnumerator; if Assigned(enum) then begin strs := TStringList.Create; try while enum.MoveNext do begin Val:= enum.Current.JsonString.Value; str := enum.Current.JsonValue.Value; strs.AddObject(str, TObject(val.ToInteger())); end; FSelectedString := TfrmSelectString.SelectString(strs); finally strs.Free; end; end; enum.Free; end;
А вот что происходит на сервере:
procedure TServerMethods1.RegisterWare(ID: Integer; ClientID: string); var Params, ParamsServ: TJSONObject; ResObj: TJSONValue; temp: TJSONValue; begin ResObj := nil; Params := TJSONObject.Create; Params.AddPair(TJSONPair.Create('1', 'Размер 42')); Params.AddPair(TJSONPair.Create('2', 'Размер 43')); Params.AddPair(TJSONPair.Create('3', 'Размер 44')); ParamsServ := TJSONObject(Params.Clone); ServerContainer1.DSServer1.NotifyCallback(ClientID, 'SelectString', Params, ResObj); if Assigned(ResObj) then begin temp := ParamsServ.GetValue(ResObj.Value); if Assigned(temp) then Form1.QueueLogMsg(Format('RegisterWare %d с разрезом %s', [ID, temp.ToString])) else Form1.QueueLogMsg(Format('RegisterWare %d без разреза', [ID])); ResObj.Free; ParamsServ.Free; end else Form1.QueueLogMsg(Format('RegisterWare %d без разреза', [ID])); end;
И напоследок напоминание:
— Локальные переменные процедур — не инициализируются
— Всё, что вы передаете в функцию — можете попрощаться с ними и не освобождать
— Всё, что вы получаете из функции — надо освободить (ведь тут ARC не работает (хотя, надо уточнить для мобилок))
С обратными вызовами можно делать что угодно — передавать в неё готовый набор данных или SQL-запрос, или имя справочника, из которого хранимая процедура вернет набор для выбора.
Код: http://code.google.com/p/datasnap-callback-with-ui/source/browse/
Это пример был сделан мной для изучения самой технологии обратных вызовов в Datasnap, в частности именно такого обратного запроса к клиенту во время запроса сервера
Так как пока с технологией жестко не определился, следующим в изучении такого приема будет RealThinkClient
ссылка на оригинал статьи http://habrahabr.ru/post/202688/
Добавить комментарий