Delphi 10西雅图数据捕捉错误:“;操作失败。连接已关闭。”;

Delphi 10西雅图数据捕捉错误:“;操作失败。连接已关闭。”;,delphi,datasnap,delphi-10-seattle,Delphi,Datasnap,Delphi 10 Seattle,我使用向导创建了一个独立的Datasnap TCP/IP服务器。我选择了示例方法(echostring和reversestring)。我保存了服务器并运行了它。然后,我创建了一个客户机应用程序,并使用文件newother向该客户机项目添加了一个ClientModule,以及ClientClasses单元。在主窗体上。我加了一个按钮。在按钮的onclick事件处理程序上,我添加了以下代码: procedure TForm1.Button1Click(Sender: TObject); begin

我使用向导创建了一个独立的Datasnap TCP/IP服务器。我选择了示例方法(echostring和reversestring)。我保存了服务器并运行了它。然后,我创建了一个客户机应用程序,并使用文件newother向该客户机项目添加了一个ClientModule,以及ClientClasses单元。在主窗体上。我加了一个按钮。在按钮的onclick事件处理程序上,我添加了以下代码:

procedure TForm1.Button1Click(Sender: TObject);
begin
  if ClientModule1.SQLConnection1.Connected then
  begin
    Button1.Text := 'Open';
    ClientModule1.SQLConnection1.Close;
  end
  else
  begin
    Button1.Text := 'Close';
    // ClientModule1.SQLConnection1.Open;
    ClientModule1.ServerMethods1Client.ReverseString('myteststring');
  end;
end;
这里的目的是模拟客户端定期登录和注销服务器的情况,而不是保持连接。这对于部署到移动设备上的应用程序尤其重要

您可以看到我注释掉了Connection.Open,因为对ServerMethods1client的第一次调用打开了连接。生成的代码如下所示:

function TClientModule1.GetServerMethods1Client: TServerMethods1Client;
begin
  if FServerMethods1Client = nil then
  begin
    SQLConnection1.Open;
    FServerMethods1Client := TServerMethods1Client.Create(SQLConnection1.DBXConnection, FInstanceOwner);
  end;
  Result := FServerMethods1Client;
end;
现在问题出现了。在第一次单击该按钮时,将打开连接,并调用该方法。第二次单击按钮时,连接即关闭。 在第三次单击时,引发了一个异常“操作失败。连接已关闭”,该异常来自TDBXCommand代码

作为解决办法,我尝试了以下方法:

procedure TForm1.Button1Click(Sender: TObject);
begin
  if ClientModule1.SQLConnection1.Connected then
  begin
    Button1.Text := 'Open';
    ClientModule1.SQLConnection1.Close;
    ClientModule1.ServerMethods1Client := nil;
  end
  else
  begin
    Button1.Text := 'Close';
    // ClientModule1.SQLConnection1.Open;
    ClientModule1.ServerMethods1Client.ReverseString('myteststring');
  end;
end;
这在某种程度上解决了问题,因为ClientModule1的fsServerMethods1Client实例被重置,所以创建代码会像第一次运行时一样再次运行

现在唯一的另一个问题是(我正在使用Eurekalog),它会造成内存泄漏


我做错了什么?在不重新启动应用程序的情况下,重复连接/断开Datasnap服务器的正确方法是什么?

第一个错误的原因是绑定客户端代理(允许调用服务器方法)的代码绑定到本地SQL连接。请注意创建代理类的调用:

FServerMethods1Client := TServerMethods1Client.Create(SQLConnection1.DBXConnection, ...)
底层DBExpress连接通过引用传递,代理类使用该连接调用服务器。您关闭并重新打开了连接,但ServerMethodsClient1使用的底层DBExpress连接已被破坏。因此,您将收到“连接已关闭”异常。ServerMethodsClient1使用的连接已关闭。您必须像在第二个示例中一样重新创建ServerMethodsClient1

我不能回答你的第二个问题,因为我认为它是特定的。对于VCL DataSnap应用程序,我会调用ServerMethodsClient1.Free,而不是将其设置为nil。基于我对Delphi的ARC实现(全部来自新闻组)非常有限的理解,我认为您应该调用ServerMethodsClient1.DisposeOf,因为该类是从TComponent派生的


但我不确定。我敢肯定,有人会跳到这里,了解ARC和销毁对象的正确解决方案,而不是内存泄漏。

在我的Android FMX实现中,我只调用servermethods来完成任务。(即我不使用Datasnap数据组件)。Datasnap架构有太多不受控制的数据传输开销,无法在移动设备上实际考虑其他任何事情。。。为了解决这个问题(而不是内存泄漏),我现在在需要时创建TServermethods1Client的本地实例,并在上下文中释放它们:

function TClientModule1.PostTheLog: Boolean;
var
  Server: TServerMethods1Client;
begin

  Server := TServerMethods1Client.Create(ClientModule1.SQLConnection1.DBXConnection);
  try
      UserID := Server.GetUserID; 
      ...
  finally
      Server.Free;
  end;
end;
现在可以随意连接和断开ClientModule1.SQLConnection1(最好在调用servermethod之前连接,然后断开),并且不会出现进一步的问题


这就引出了一个问题:在哪个理想世界中,可公开访问的ServerMethods1Client会真正有用?

谢谢。你说的有道理。。在一定程度上。实际上,我已经编写了每天都在使用的完整VCL datasnap应用程序,我从未遇到过这个问题。为什么不呢?因为我使用设计时组件(TDSProviderConnection和TSqlServerMethod)从该服务器获取数据。更新通过相应的TClientDataset.ApplyUpdates完成。我不调用任何servermethods,在这种实现中,用户整天都在连接和断开连接,而不关闭应用程序。更有效的是,只要连接打开,保持客户端代理实例的分配就会更有效,而不是每次都创建和销毁一个新实例。内存分配/管理非常昂贵,尤其是与备选方案(不重复执行)相比。我很困惑,您回答了自己的问题并将其标记为答案,而您提供的信息实际上并不是原始问题的答案。当然,欢迎您提供完整的答案。同时,我自己的答案已经足够了,不是吗?除了你提供的代码示例之外,我不知道你的“新”答案与我的答案有什么不同。我花时间给出了详细的答案,这显然也解决了你的内存泄漏问题。你的答案只是我答案的实现,但你没有给我回答这个问题的荣誉。没什么大不了的,但我觉得你并不感激我花时间回答你的问题。你可以肯定我很感激,因为我投了一票。然而,这并不是一个完整的答案,这是我在研究后提供给自己的。如果您确实希望我将您的答案标记为这样,请演示如何在TSqlConnection关闭时保留DBXconnection。