2016-04-15 36 views
1

我使用向导创建了一个独立的Datasnap TCP/IP服务器。我选择了示例方法(回声串和反转串)。我保存了服务器并运行了它。然后我创建了一个客户端应用程序,并使用file-new-other,将ClientModule和ClientClasses单元一起添加到该客户端项目。在主要形式上。我添加了一个按钮。在按钮的onclick事件处理程序,添加以下代码:Delphi 10西雅图Datasnap错误:“操作失败,连接已关闭。”

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; 

现在问题出现了。首先点击按钮,打开连接,并调用该方法。在第二次点击按钮时,连接关闭。 在第3次点击时,会引发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的FServerMethods1Client实例被重置,以便在创建代码,就像它在第一次运行再次运行。

现在唯一的其他问题是(我正在使用Eurekalog)它会产生内存泄漏。

我在做什么错?无需重新启动应用程序,连接/断开Datasnap服务器的正确方法是什么?

回答

-1

在我的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可以连接和断开处将(最好在任何调用服务器方法之前连接,之后断开连接),并且不会出现进一步的问题。

然后,这引出了一个问题:在哪个理想的世界中公共可访问的ServerMethods1Client实际上是有用的?

+0

这是有效的,但只要连接打开就保持客户端代理实例的分配效率更高,而不是每次创建和销毁新实例。内存分配/管理很昂贵,尤其是与替代方案相比(不重复执行)。 –

+0

当你提供的信息真的不是对原始问题的回答时,我很困惑你回答了你自己的问题并将其标记为答案。 –

+0

当然欢迎您提供完整的答案。与此同时,我自己的回答就足够了,不是吗? – nolaspeaker

2

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

FServerMethods1Client := TServerMethods1Client.Create(SQLConnection1.DBXConnection, ...) 

底层的dbExpress连接是通过引用传递,和代理类使用该连接调用服务器。您关闭并重新打开连接,但ServerMethodsClient1正在使用的基础DBExpress连接已被销毁。因此,您收到“连接已关闭”异常。 ServerMethodsClient1正在使用的连接已关闭。您必须像在第二个示例中那样重新创建ServerMethodsClient1。

我无法回答你的第二个问题,因为我相信它是ARC特定的。对于VCL DataSnap应用程序,我会调用ServerMethodsClient1.Free,而不是将其设置为零。基于对Delphi的ARC实现(这些都来自新闻组)的理解非常有限,我相信你应该调用ServerMethodsClient1.DisposeOf,因为这个类从TComponent下降。我敢肯定有人会跳到这里,理解ARC和正确的解决方案来销毁对象,而不是发生内存泄漏。

+0

谢谢。你说的话很有道理..达到一定程度。我实际上编写了每天都在使用的完整的VCL datasnap应用程序,我从来没有遇到过这个问题。为什么不?因为我使用设计时组件(TDSProviderConnection和TSqlServerMethod)从该服务器获取数据。更新通过相应的TClientDataset.Applyupdattes完成。我不会调用任何服务器方法,并且在该实现中,用户连接并断开连接而不关闭应用程序。更多... – nolaspeaker

相关问题