Что это и для чего надо:
Работа с 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
Автор: Cobalt747