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

от автора

Что это и для чего надо:

Работа с 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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *