【问题标题】:Refreshing a ClientDataSet nested in a DataSetField刷新嵌套在 DataSetField 中的 ClientDataSet
【发布时间】:2014-09-01 08:51:13
【问题描述】:

我几乎完成了一些代码来解决去年这个未回答的问题:

Refresh Nested DataSet with poFetchDetailsOnDemand

公认的观点是,如果不关闭并重新打开主 CDS,就不可能从服务器刷新嵌套的详细 CDS,但显然这会产生不成比例的网络流量,仅刷新单个主行及其详细信息行。

我想到了一种直接刷新细节 CDS 的方法 从服务器,它几乎工作。我下面的代码基本上是通过暂时刷新细节 将过滤器应用于主 ADO 查询以将其过滤到当前主行,并且在该过滤器有效时, 通过对其应用类似的过滤器然后调用其 Refresh 方法来刷新主 CDS。这是由主 CDS AfterScroll 事件触发的。

只有一个 teeny 问题:在我的表单以及 4 个数据集和随附的网格上,我在表单上有一个刷新按钮,它调用了我的 RefreshcdsMasterAndDetails 也在 cdsMasterAfterScroll 中调用。如果我使用其网格在主 CDS 中移动,我的代码一切正常,并且详细 CDS 行以及 AdoQuery 详细信息 中的那些立即正确更新,但如果我通过单击刷新按钮触发它,CDS 详细信息行仅在我单击“刷新”按钮时每秒钟更新一次。

我的问题是:为什么当我的代码从按钮单击而不是从 AfterScroll 事件触发时,它的效果应该有什么不同,因为它可靠地完成了从 AfterScroll 事件调用它应该做的事情但只有在通过按钮点击触发时才每隔一次?

//Obviously MasterPKName below is a const and DoingRefresh is a boolean
// flag on the form

procedure TForm1.cdsMasterRowRefresh(MasterPK : Integer);
begin
  if DoingRefresh then Exit;

  DoingRefresh := True;

  try
    cdsMaster.Prior;
    cdsMaster.Next;
    cdsMaster.Filter := MasterPKName + ' = ' + IntToStr(MasterPK);
    cdsMaster.Filtered := True;
    cdsMaster.Refresh;
    cdsMaster.Filtered := False;

    cdsMaster.Locate(MasterPKName, MasterPK, []);

  finally
    DoingRefresh := False;
  end;
end;

procedure TForm1.qMasterRowRefresh(MasterPK : Integer);
begin
  qMaster.Filter := MasterPKName + ' = ' + IntToStr(MasterPK);
  qMaster.Filtered := True;
  qMaster.Refresh;

  cdsMasterRowRefresh(MasterPK);

  qMaster.Filtered := False;
  qMaster.Locate(MasterPKName, MasterPK, []);
end;

procedure TForm1.RefreshcdsMasterAndDetails;
var
  MasterPK : Integer;
begin
  MasterPK := cdsMaster.FieldByName(MasterPKName).AsInteger;

  cdsDetail.DisableControls;
  cdsMaster.DisableControls;
  qDetail.DisableControls;
  qMaster.DisableControls;

  try
    qMasterRowRefresh(MasterPK);
  finally
    qMaster.EnableControls;
    qDetail.EnableControls;
    cdsMaster.EnableControls;
    cdsDetail.EnableControls;
  end;
end;

procedure TForm1.cdsMasterAfterScroll(DataSet: TDataSet);
begin
  RefreshcdsMasterAndDetails;
end;

【问题讨论】:

  • 好点。我已经被这个问题包裹了一天多,以至于我设法把这个问题排除在外!我马上编辑它。
  • 好吧,我相信在 CDS 中,您可以拥有多个密钥字段,但您对我的问题的解决方案将是不完整的。但是,嘿,+1 这是解决问题的一个棘手的想法......
  • 谢谢。由于您的意思是多字段键或其他原因而导致不完整?由于我在服务器详细信息表中遇到了类似的问题,我的代码与我发布的内容有所不同。
  • 因为多字段,但没关系,因为这很容易改变。反正你为什么选择OnAfterScroll
  • 基本上是因为 IME 是最可靠的事件,可以识别当前主行已更改的事实,因此 cdsDetail 需要获取一组不同的行。我曾在许多情况下使用它,由于各种原因,依靠 VCL 获取正确主记录的详细信息是不切实际的。

标签: delphi ado master-detail tclientdataset


【解决方案1】:

尽管进行了很多仔细的观察和调试,但我仍然无法令人满意地解释我的 CDS 刷新代码的确切原因 如果在主 CDS 的 AfterScroll 事件中调用,则行为不同,其中 detail CDS 总是得到正确更新,并且在 ButtonClick 处理程序中 详细 CDS 仅在每秒钟单击一次时更新。我想这是什么 与主 CDS 的光标已经移动的事实有关 调用 AfterScroll 处理程序,这与我单击按钮的情况不同。

但是,我找到了一个简单的解决方法和修复方法。

解决方法就是不要在之前的 4 个数据集上调用 DisableControls 做刷新。然后细节 CDS 总是被正确刷新。任何其他 禁用部分或全部数据集的排列会导致我的 q 的差异。不过我不喜欢这种解决方法,因为 cdsMaster DBGrid 必须一直滚动浏览数据,只需刷新一个主行 + 它的详细信息。

解决方法是做一些我一开始就应该做的事情,即强制 刷新详细 ADO 查询(使用我的数据,只需调用它的 Refresh,这是我的第一次尝试 在修复时,会引发熟悉的 Ado 错误“Insufficient key column information 用于更新...”,尽管详细信息表在服务器上有一个 PK)。

所以,这里是修复:

procedure TForm1.qMasterRowRefresh(MasterPK : Integer);
begin
  try
    qMaster.Filter := MasterPKName + ' = ' + IntToStr(MasterPK);
    qMaster.Filtered := True;

    qMaster.Refresh;

    //  Do NOT omit the next 3 lines, needed to ensure that the detail query
    //  and hence the detail CDS, is refreshed

    qDetail.Parameters.ParamByName(MasterPKName).Value := MasterPK;
    qDetail.Close;
    qDetail.Open;

    cdsMasterRowRefresh(MasterPK);

  finally
    qMaster.Filtered := False;
    qMaster.Locate(MasterPKName, MasterPK, []);
  end;
end;

当我通过查看之前未回答的 SO 问题来解决这个问题时,我会 将代码的更新版本移植到该代码的答案中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-11
    • 2020-07-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多