谷歌搜索发现大量文章表示,如果不关闭并重新打开主CDS,OP不希望在此情况下执行嵌套的ClientDataSets,则根本不可能。然而......
对于q的简短回答是肯定的,在我测试过的相当简单的情况下,如果有点冗长,这很简单;正确地采取必要措施花了一段时间才弄清楚。
代码如下,包含解释它如何工作的注释以及一些潜在的问题,以及它如何避免或解决它们。我只用TAdoQueries为CDS提供商提供测试。
当我开始寻找到这一切,它很快变得明显,与通常的主 +详细设置,虽然供应商+ CDS是高兴地从服务器刷新的主数据,他们根本不会刷新详细信息记录自从cdsMaster打开以来第一次从服务器读取它们。这当然可以通过设计。
我不认为我需要发布一个DFM去与代码。我只需要以通常的主细节方式设置AdoQueries(详细查询将主设备的PK作为参数),指向主AdoQuery的DataSetProvider,指向提供程序的主CDS以及指向该设备的详细cDS cdsMaster的DataSetField。为了试验并看看发生了什么,为每个这些数据集都有DBGrid和DBNavigators。
简而言之,下面代码的工作方式是暂时过滤AdoQuery主控和CDS主控到当前行,然后强制刷新其数据和当前主控行的dtail数据。这样做,与我尝试的其他方法不同,导致嵌套在cdsMaster的DataSet字段中的详细信息行得到刷新。
顺便说一句,我试过的其他盲人胡同包括有和没有poFetchDetailsOnDemand设置为true,同上cdsMaster.FetchDetailsOnDemand。显然“FetchDetailsOnDemand”并不意味着ReFetchDetailsOnDemand!
我遇到了一个问题或两个让我的“解决方案”的工作,在这太问题被描述最棘手的一个:(!) 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;
AFAIK,在嵌套的客户端数据集中,不能将'Refresh'用于详细数据集。查看Cary Jensen编写的这篇文章(http://edn.embarcadero.com/article/29825)并搜索更新词。 –
在链接的文章中,autor说我不能在没有数据集提供程序的情况下刷新数据集。好。那么,刷新整个主数据集的唯一方法是什么? – EProgrammerNotFound
不是100%确定,但似乎。无论如何,你有什么问题刷新主数据集? –