【问题标题】:Delphi : Globally change ADO command timeoutDelphi:全局更改 ADO 命令超时
【发布时间】:2015-03-27 22:34:45
【问题描述】:

我们有一个巨大的 Delphi 2005 应用程序,其中包含大量 ADO 组件(TADODataset、TADOStoredPROc、TADOCommand...),分布在数百个表单上。它们都连接到 SINGLE TADOConnection。

这些组件中的大多数都将其 CommandTimeout 属性设置为默认值 (30 秒),但少数将其设置为 5 分钟 (300 秒),有些设置为永不超时 (0 秒)。

我希望能够为应用程序范围内的所有 ADO 组件全局更改此设置。我更喜欢在运行时以编程方式执行此操作,以便我可以根据需要在每个安装的基础上调整超时。

我希望在创建/附加 ADO 组件时可以在连接上找到一个全局事件,我可以在其中调整命令超时,或者破解将我的代码注入组件本身的方法,但结果空白。

我不想创建后代,因为我必须搜索/替换所有组件,如果我忘记使用后代而不是常规 ADO 组件,我的超时将不会跟随应用程序的其余部分。

有人知道我们如何做到这一点吗?

【问题讨论】:

    标签: delphi timeout ado


    【解决方案1】:

    如果所有 ADO 组件都放置在一个表单上,则可以使用 Screen.Forms 和 Screen.FormCount 属性遍历所有表单。对于每个表单,迭代其 ComponentCount/Components 属性并检查 TADOCommand、TADODataSet、TADOQuery、TADOStoredProc 和 TADOTable。然后,您可以根据需要设置超时。当然,如果您动态创建表单,则必须单独考虑这一点。

    下面的代码可以指导你。

    procedure SetADOTimeout(ATimeout: Integer);
    var
      cmp: TComponent;
      frm: TForm;
      I: Integer;
      J: Integer;
    begin
      for I := 0 to Screen.FormCount - 1 do begin
        frm := Screen.Forms[I];
        for J := 0 to frm.ComponentCount - 1 do begin
          cmp := frm.Components[J];
          if cmp is TADOCommand then
            TADOCommand(cmp).CommandTimeout := ATimeout
          else if cmp is TADODataSet then
            TADODataSet(cmp).CommandTimeout := ATimeout
          else if cmp is TADOQuery then
            TADOQuery(cmp).CommandTimeout := ATimeout
          else if cmp is TADOStoredProc then
            TADOStoredProc(cmp).CommandTimeout := ATimeout
          else if cmp is TADOTable then
            TADOTable(cmp).CommandTimeout := ATimeout;
        end;
      end;
    end;
    

    【讨论】:

      【解决方案2】:

      向所有阿根廷解决方案致敬!

      只需为您拥有的 TADOConnection 定义 OnWillExecute 事件处理程序并编写以下代码:

      type
        TCustomADODataSetAccess = class(TCustomADODataSet);
      
      procedure TYourDataModule.ADOConnectionWillExecute(...);
      var
        i: Integer;
      begin
        for i := 0 to ADOConnection.DataSetCount - 1 do
          TCustomADODataSetAccess(ADOConnection.DataSets[i]).CommandTimeout := Connection.CommandTimeout;
      end;
      

      这将为使用您的 ADO 连接的任何查询/表/存储过程设置命令超时。

      【讨论】:

        【解决方案3】:

        根据文档,您可以使用CommandCountCommands 来定位附加到TADOConnection 的所有open 组件。

        您的问题很可能是动态创建的表单。当创建表单并检查该表单上的 ADO 组件时,您需要找到要挂钩的“东西”。

        如果您的表单源自自定义表单类,您可以在表单的 constructorOnCreate 事件中执行此操作。

        如果没有,您可以查看TApplicationEvents 并使用TApplication's OnIdle 事件。

        【讨论】:

          【解决方案4】:

          由于在 TCustomADODataset 类中引入了 CommandTimeout,您可以对每个表单/数据模块进行迭代,找到 TCustomADODataset 及其后代(ADODataset、ADOTable、ADOQuery)然后设置属性。

          procedure SetADOCommandTimeOut(aTimeOut: integer);
          var
            i, j: integer;
          begin
            for i:= 0 to Screen.FormCount-1 do
            begin
              for j:= 0 to Forms[i].ComponentCount-1 do
                if Forms[i].Components[j] is TCustomADODataset then
                  TCustomADODataset1(Forms[i].Components[j]).CommandTimeOut:= aTimeOut;
            end;
          
            for i:= 0 to Screen.DataModuleCount-1 do
            begin
              for j:= 0 to Datamodules[i].ComponentCount-1 do
                if Datamodules[i].Components[j] is TCustomADODataset then
                  TCustomADODataset1(Datamodules[i].Components[j]).CommandTimeOut:= aTimeOut;
            end;
          end;
          

          注意:TCustomADODataset1 正是 TCustomADODataset,只是它有一个发布的 CommandTimeOut 属性:

          TCustomADODataset1 = class(TCustomADODataset)
          published
            property CommandTimeOut;
          end;
          

          但它只适用于已经创建的表单/数据模块。如果您动态创建表单/数据模块,则必须在创建新表单/数据模块时应用它。 一种方法是在 Mainform 中覆盖 Notification,检查表单/数据模块的新创建,但这有点棘手,因为在创建时,所有组件都还没有创建。您可以通过使用计时器将其延迟一段时间来欺骗它(我不知道更优雅的方式 - 只是为了展示这个想法)

          Procedure TMainForm.Notification(AComponent: TComponent; Operation: TOperation);
          begin
            inherited;
            if (Operation = opInsert) and (
               ((AComponent is TForm) and not (aComponent is TMainForm)) // exclude MainForm 
               or (AComponent is TDataModule)
               ) then
            begin
              Timer1.Interval:= 2000; // 2 seconds ?
              Timer1.Enabled:= True;
            end;
          end;
          
          Procedure TMainForm.Timer1Timer(Sender: TObject);
          begin
            Timer1.Enabled:= False;
            SetADOCommandTimeOut(MyTimeOut);
          end;
          

          【讨论】:

          • 受保护的属性可由与具有目标属性的类位于同一单元中的类访问。这意味着您不必发布CommandTimeout 属性,只要您将TCustomADODataset1 的声明与引用该属性的代码保持在同一单元中。因此,您的 TCustomADODataset1 可能看起来像这样:TCustomADODataset1 = class(TCustomADODataset) end;。除此之外,这个方法没有考虑TADOCommand,它不是TCustomADODataset的后代。
          • 哇,我从没想过应用这种行为来简化这样的事情。谢谢安德烈。
          【解决方案5】:

          您创建数据模块层次结构?如果是这样,您可以在您的族长表单(所有其他数据模块继承)上使用 Uwe 的答案之类的代码。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2010-11-01
            • 1970-01-01
            • 2011-01-20
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多