使用poFetchDetailsOnDemand刷新嵌套数据集

9

有没有一种方法可以仅刷新详细数据集而无需重新加载所有主数据集?

这是我迄今为止尝试过的:

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

我也尝试过:
DM.ClientDataSet1.Refresh;

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

现在,以下代码似乎没有任何作用:

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

不是百分之百确定,但看起来是这样。无论如何,您在刷新主数据集方面遇到了什么问题? - Guillem Vicens
你尝试过将详细记录检索到另一个客户端数据集,然后将其数据分配给ClientDataSet2吗? - MartynA
@GuillemVicens 我修复了“已关闭的数据集”异常,但仍然无法工作。 - EProgrammerNotFound
@MartynA 这不是一个好主意,因为我必须对我拥有的每个 M/D 数据集都这样做。 - EProgrammerNotFound
如果您已经更正了代码,应该更新您的问题,删除错误的提及,并解释为什么它不起作用。无论如何,我认为这是不可能的。我正在尝试在Delphi XE中做同样的事情。到目前为止还没有成功。:( - EMBarbosa
显示剩余4条评论
1个回答

2
谷歌搜索可以找到很多文章,称在嵌套的ClientDataSets中不关闭和重新打开主CDS是不可能的,而这正是本文作者不想要做的。然而……
简短地回答问题,是的,在我测试过的相当简单的情况下,它是可行的,而且非常简单,尽管有点冗长;弄清楚必要的步骤花了一些时间。下面的代码包括解释它如何工作以及一些潜在问题以及如何避免或解决它们的注释。我只用TAdoQueries测试过提供CDSs的情况。
当我开始研究所有这些时,很快就明显了,虽然提供程序和CDS可以从服务器刷新主数据,但自从cdsMaster被打开以来,它们根本不会刷新细节记录。这可能当然是设计上的。
我认为我不需要发布DFM与代码一起。 我只是按照通常的主从方式设置了AdoQueries(详细查询具有主键作为参数),将DataSetProvider指向主AdoQuery,将主CDS指向提供程序,并将详细cDS指向cdsMaster的DataSetField。 为了实验和查看正在进行的操作,每个数据集都有DBGrid和DBNavigator。
简而言之,下面的代码的工作方式是临时过滤AdoQuery主和CDS masterdown到当前行,然后强制刷新它们的数据以及当前主行的dtail数据。 以这种方式执行,与我尝试过的任何其他方式不同,结果是在cdsMaster的DataSet字段中嵌套的详细行得到刷新。
顺便说一句,我尝试的其他死胡同包括使用和不使用poFetchDetailsOnDemand以及cdsMaster.FetchDetailsOnDemand。 显然,“FetchDetailsOnDemand”并不意味着“ReFetchDetailsOnDemand”!
我遇到了一些问题,让我的“解决方案”工作起来有点困难,其中最棘手的一个在这个SO问题中描述: 刷新嵌套在DataSetField中的ClientDataSet 我已经验证了它可以与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;

好的,我会测试并很快回来,对此提出更多评论。 - EProgrammerNotFound

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接