代码之家  ›  专栏  ›  技术社区  ›  Alexander

Delphi-异步Datasnap方法调用

  •  0
  • Alexander  · 技术社区  · 7 年前

    我正在编写一个Datasnap应用程序,在客户端/服务器之间使用TCP连接,服务器连接到SQL server。

    服务器 具有具有所有数据集查询和SQL连接的datamodule DM1。DM1还具有REST请求/客户端/响应组件。

    DM1有一个公开的函数PostDataAsync,ID为param:从数据集生成json,然后通过HTTP将其发布到RESTFul服务。它返回未能在回调参数中发布的记录数。

    此DM1的DSServer是调用。

    调用服务器类型应确保每个服务器方法调用都有自己的数据库连接、数据集、Rest组件,这些组件不会有多个调用干扰彼此的数据(如果添加了并行线程)。

    procedure TServerMethods1.postCustOrderHistAsync(CustomerID: String; callback: TDBXcallback);
    var
      jsonObject: TJSONObject;
      CallbackValue: TJsonValue;
      errors: Integer;
    begin
      errors := postCustOrderHist(CustomerID); //takes time to post, returns num of failed records
      jsonObject := TJSONObject.create;
      jsonObject.AddPair(tjsonpair.create('errors', errors.ToString));
      CallbackValue := callback.Execute(jsonObject);
    end;
    

    客户 有一个按钮,该按钮使用ID param调用服务器方法PostDataAsync,还有一个回调函数ShowNotification(它使用windows通知中心显示通知后状态)。

    目前,应用程序的工作方式如下:客户端同步调用服务器函数,这意味着主线程等待服务器函数完成HTTP post,然后运行回调通知;客户同时被绞死。

    TDSCallbackWithMethod = class(TDBXCallback)
    private
      FCallbackMethod: TDSCallbackMethod;
    public
      constructor Create(ACallbackMethod: TDSCallbackMethod);
      function Execute(const Args: TJSONValue): TJSONValue; override; //executes FCallbackMethod
    end;
    
    procedure TMainForm.BtnPostOrderHistoryClick(Sender: TObject);
    var
      callback: TDBXCallback;
      ServerMethods1Client: TServerMethods1Client;
    begin
      //Define Callback to show notification
      callback := TDSCallbackWithMethod.Create(
        function(const Args: TJSONValue): TJSONValue
        var
          errors: integer;
        begin
          errors := Args.GetValue<integer>('errors');
    
          if errors = 0 then
            showNotification(StrSentSuccessfully)
          else
            showNotification(StrSendingFailed + '(' + errors.ToString + ' not sent)');
    
          result := TJsonTrue.Create;
        end);
    
      //Call Server Method
      ServerMethods1Client := TServerMethods1Client.Create(DMServerConnection.SQLConnection1.DBXConnection);
      try
        ServerMethods1Client.postCustOrderHistAsync(EditCustomerId.Text, callback)
      finally
        ServerMethods1Client.Free;
      end;
    end;
    

    注意:我已经尝试在TTask中运行客户机函数调用,当用户一次运行一次函数时,它就会工作。但是当服务器方法同时运行多次时,我得到一个DBXErrorRead errorcallback,期望X得到Y。当客户端等待来自第一个请求的响应回调格式时,它似乎与来自第二个请求的其他tcp协议包混淆了。我曾尝试在服务器端运行ttask,但出现异常“TOLEDBCommand.Destroy-接口未发布”

    2 回复  |  直到 7 年前
        1
  •  1
  •   TDC    7 年前

    退房 this

        2
  •  0
  •   Alexander    7 年前

    为了简化客户端服务器方法调用,我从客户端删除了回调,只创建了等待服务器响应的并行线程。我仍然得到相同的错误DBXErrorRead errorcallback,期望X得到Y。因此,我知道该错误不是回调问题,而是线程之间的干扰。事实证明,当我创建客户端的代理方法时,所有线程都使用同一个DBXConnection实例。这将使SQLconnection在不同的服务器调用/响应之间丢失,并导致解析错误。我做了一个函数“getNewSqlConnection”,它将TSQLConnection的所有设置复制到一个新实例中。
    现在,客户端调用方法如下所示:

    procedure TMainForm.BtnPostOrderHistoryClick(Sender: TObject);
    begin
      ttask.Run(
        procedure
        var
          ServerMethods1Client: TServerMethods1Client;
          SqlConnectionLocal: TSqlConnection;
          errors: Integer;
        begin
          // Call Server Method
          SqlConnectionLocal := DMServerConnection.getNewSqlConnection(Self);
          ServerMethods1Client := TServerMethods1Client.Create(SqlConnectionLocal.DBXConnection);
          try
            errors := ServerMethods1Client.postCustOrderHist(EditCustomerId.Text);
            if errors = 0 then
              TThread.Synchronize(nil,
                Procedure
                begin
                  showNotification(StrSentSuccessfully)
                end)
            else
              TThread.Synchronize(nil,
                Procedure
                begin
                  showNotification(StrSendingFailed + '(' + errors.ToString + ' not sent)')
                end);
          finally
            ServerMethods1Client.Free;
            SqlConnectionLocal.Free;
          end;
        end);
    end;