为什么主记录更改时 TADOQuery AfterOpen 事件不发生?

Why doesn't TADOQuery AfterOpen event occur when master record is changed?

假设我们有两个 TADOQuery 的标准主从关系。当在主数据集上导航时,详细数据集不会出现 AfterOpen 事件。

此事件在其他数据访问包中出现,例如 BDE。为什么 dbGo 的这种行为不同?

.dfm 的一部分:

  object DataSource1: TDataSource
    DataSet = SDQuery1
    Left = 504
    Top = 72
  end
  object DataSource2: TDataSource
    DataSet = SDQuery2
    Left = 520
    Top = 360
  end
  object ADOConnection1: TADOConnection
    LoginPrompt = False
    Left = 336
    Top = 464
  end
  object ADOQuery1: TADOQuery
    Connection = ADOConnection1
    Parameters = <>
    Left = 504
    Top = 160
  end
  object ADOQuery2: TADOQuery
    Connection = ADOConnection1
    AfterOpen = ADOQuery2AfterOpen // <- rised when dataset was opened at first time only
    DataSource = DataSource1
    Parameters = <>
    Left = 520
    Top = 296
  end
This event is raised in other data access packages, such as BDE. Why does 
this behavior differ for dbGo?

大多数其他数据访问包在这种情况下不会引发打开事件。例如,FireDAC 组件的帮助提到使用 OnMasterSetValues(dbGo 组件不可用):

Use the OnMasterSetValues event handler to override parameter values supplied to the detail dataset from the master dataset. Also, as the BeforeOpen and AfterOpen events do not fire for a detail dataset, OnMasterSetValues can be used instead.

因此不应触发打开事件,但某些数据访问组件会在这种情况下提供其他事件。

参考:FireDAC.Comp.DataSet.TFDDataSet.OnMasterSetValues

Delphi ADO 组件行为的原因是当 主数据集滚动,ADODB.Pas中的这段代码执行

  procedure TCustomADODataSet.MasterChanged(Sender: TObject);
  begin
    if not Active then Exit;
    if Parameters.Count = 0 then
    begin
      CheckBrowseMode;
      if SetDetailFilter then First;
    end else
      RefreshParams;
  end;

而且 SetDetailFilterRefreshParams 都不涉及关闭和重新打开 细节数据集。 Requery 最终调用

  procedure TCustomADODataSet.InternalRequery(Options: TExecuteOptions = []);
  begin
    if FConnectionChanged then
      DatabaseError(SCantRequery);
    try
      Recordset.Requery(ExecuteOptionsToOrd(Options));
    except
      if Recordset.State = adStateClosed then Close;
      raise;
    end;
    DestroyLookupCursor;
  end;

它使用 ADO RecordSet 对象的特定功能(也称为 Requery) 在 TCustomADODataSet 的基础上检索匹配的详细记录,效率显着提高 而不是关闭并重新打开 Detail 数据集,这就是不调用其 AfterOpen 事件的原因。

另请参阅 DB.Pas 中定义的 TDetailDatalinkTMasterDatalink