使用poFetchDetailsOnDemand刷新嵌套数据集

时间:2013-10-14 19:47:58

标签: delphi dataset delphi-7 tclientdataset

有没有办法只刷新Detail DataSet而不重新加载所有主数据集?

这是我到目前为止所尝试的:

DM.ClientDataSet2.Refresh;      
DM.ClientDataSet2.RefreshRecord;

我也尝试过:

DM.ClientDataSet1.Refresh;

但是上面的方法会刷新整个Master数据集,而不仅仅是当前记录。

现在,以下代码似乎可以做任何事情:

DM.ClientDataSet1.RefreshRecord;

是否有解决方法或正确的方法来做我想要的事情? (也许是插入者...)

其他信息:

ClientDataSet1 =主数据集

ClientDataSet2 =详细数据集,如下所示:*

object ClientDataSet2: TClientDataSet
    Aggregates = <>
    DataSetField = ClientDataSet1ADOQuery2
    FetchOnDemand = False
    .....
end

提供者属性:

object DataSetProvider1: TDataSetProvider
    DataSet = ADOQuery1
    Options = [poFetchDetailsOnDemand]
    UpdateMode = upWhereKeyOnly
    Left = 24
    Top = 104
  end

1 个答案:

答案 0 :(得分:2)

谷歌搜索发现许多文章说,在没有关闭并重新打开主CDS的情况下,根本不可能使用嵌套的ClientDataSets,OP在这种情况下不想这样做。但是......

对q的简短回答是肯定的,在我测试过的相当简单的情况下,如果有点啰嗦,这很简单;正确地采取必要措施需要一段时间才能弄明白。

代码如下,其中包含解释其工作原理和一些潜在问题的评论以及如何避免或解决这些问题。我只用TAdoQueries对CDS的提供商进行了测试。

当我开始研究这一切时,很快就会发现与普通大师一样 +细节设置,虽然Providers + CDS很乐意从服务器刷新主数据,但是一旦他们第一次从服务器读取了记录,他们就不会刷新详细信息记录。 cdsMaster被打开了。当然,这可能是设计的。

我认为我不需要发布DFM来使用代码。我只是以通常的主 - 细节方式设置AdoQueries(详细查询以主人的PK作为参数),指向主AdoQuery的DataSetProvider,指向提供者的主CDS,以及指向提供者的详细cDS cdsMaster的DataSetField。要试验并查看正在发生的事情,每个数据集都有DBGrids和DBNavigators。

简而言之,下面代码的工作方式是临时过滤AdoQuery主服务器和CDS masterdown到当前行,然后强制刷新他们的数据和当前主行的dtail数据。这样做,与我尝试的任何其他方式不同,导致嵌套在cdsMaster的DataSet字段中的细节行得到刷新。

顺便说一下,我尝试过的其他盲道,包括和不带poFetchDetailsOnDemand设置为true,ditto cdsMaster.FetchDetailsOnDemand。显然“FetchDetailsOnDemand”并不意味着ReFetchDetailsOnDemand!

我遇到了一两个让我的“解决方案”正常工作的问题,在这个SO问题中描述了最棘手的问题: Refreshing a ClientDataSet nested in a DataSetField

我已经验证了这适用于Sql Server 2000(!)后端,包括从ISqlW获取在服务器上触发的行数据更改。我还使用Sql Server的Profiler验证了刷新中的网络流量只涉及单个主行及其详细信息。

Delphi 7 + Win7 64位,顺便说一句。

procedure TForm1.cdsMasterRowRefresh(MasterPK : Integer);
begin
  //  The following operations will cause the cursor on the cdsMaster to scroll
  //  so we need to check and set a flag to avoid re-entrancy
  if DoingRefresh then Exit;
  DoingRefresh := True;

  try
    //  Filter the cdsMaster down to the single row which is to be refreshed.
    cdsMaster.Filter := MasterPKName + ' = ' + IntToStr(MasterPK);
    cdsMaster.Filtered := True;
    cdsMaster.Refresh;
    Inc(cdsMasterRefreshes);  //  just a counter to assist debugging

    //  release the filter
    cdsMaster.Filtered := False;

    // clearing the filter may cause the cdsMaster cursor to move, so ...
    cdsMaster.Locate(MasterPKName, MasterPK, []);
  finally
    DoingRefresh := False;
  end;
end;

procedure TForm1.qMasterRowRefresh(MasterPK : Integer);
begin
  try
    //  First, filter the AdoQuery master down to the cdsMaster current row
    qMaster.Filter := MasterPKName + ' = ' + IntToStr(MasterPK);
    qMaster.Filtered := True;

    //  At this point Ado is happy to refresh only the current master row from the server
    qMaster.Refresh;

    // NOTE:
    //  The reason for the following operations on the qDetail AdoQuery is that I noticed
    //  during testing situations where this dataset would not be up-to-date at this point
    //  in the refreshing operations, so we update it manually.  The reason I do it manually
    //  is that simply calling qDetail's Refresh provoked the Ado "Insufficient key column
    //  information for updating or refreshing" despite its query not involving a join
    //  and the underlying table having a PK

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

    //  With the master and detail rows now re-read from the server, we can update
    //  the cdsMaster
    cdsMasterRowRefresh(MasterPK);
  finally
    //  Now, we can clear the filter
    qMaster.Filtered := False;
    qMaster.Locate(MasterPKName, MasterPK, []);
    // Obviously, if qMaster were filtered in the first place, we'd need to reinstate that later on
  end;
end;

procedure TForm1.RefreshcdsMasterAndDetails;
var
  MasterPK : Integer;
begin
  if cdsMaster.ChangeCount > 0 then
    raise Exception.Create(Format('cdsMaster has %d change(s) pending.', [cdsMaster.ChangeCount]));
  MasterPK := cdsMaster.FieldByName(MasterPKName).AsInteger;

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

  try
    try
      qMasterRowRefresh(MasterPK);
    except
      //  Add exception handling here according to taste
      //  I haven't encountered any during debugging/testing so:
      raise;
    end;
  finally
    qMaster.EnableControls;
    qDetail.EnableControls;
    cdsMaster.EnableControls;
    cdsDetail.EnableControls;
  end;
end;

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

procedure TForm1.cdsMasterAfterPost(DataSet: TDataSet);
//  NOTE:  The reason that this, in addition to cdsMasterAfterScroll, calls RefreshcdsMasterAndDetails is
//         because RefreshcdsMasterAndDetails only refreshes the master + detail AdoQueries for the current
//         cdsMaster row.  Therefore in the case where the current cdsMaster row or its detail(s)
//         have been updated, this row needs the refresh treatment before we leave it.
begin
  cdsMaster.ApplyUpdates(-1);
  RefreshcdsMasterAndDetails;
end;

procedure TForm1.btnRefreshClick(Sender: TObject);
begin
  RefreshcdsMasterAndDetails;
end;

procedure TForm1.cdsDetailAfterPost(DataSet: TDataSet);
begin
  cdsMaster.ApplyUpdates(-1);
end;