有没有一种方法可以只刷新Detail DataSet而无需重新加载所有主数据集?
到目前为止,这是我尝试过的:
DM.ClientDataSet2.Refresh;
DM.ClientDataSet2.RefreshRecord;
我也尝试过:
DM.ClientDataSet1.Refresh;
但是上面的方法刷新了整个Master数据集,而不仅仅是当前记录。
现在,以下代码似乎可以执行任何操作:
DM.ClientDataSet1.RefreshRecord;
是否有解决方法或正确的方法来做我想要的? (也许是插入器...)
附加信息:
ClientDataSet1 =主数据集
ClientDataSet2 = Detail DataSet,如下所示:*
object ClientDataSet2: TClientDataSet
Aggregates = <>
DataSetField = ClientDataSet1ADOQuery2
FetchOnDemand = False
.....
end
提供者属性:
object DataSetProvider1: TDataSetProvider
DataSet = ADOQuery1
Options = [poFetchDetailsOnDemand]
UpdateMode = upWhereKeyOnly
Left = 24
Top = 104
end
最佳答案
Googling发现了许多文章,它们说嵌套的ClientDataSets根本不可能不关闭并重新打开主CDS,而OP在这种情况下是不希望这样做的。但是...
在我测试的相当简单的情况下,对q的简短回答是“是”,并且如果有点冗长,它也非常简单。正确地采取必要步骤需要花费一些时间才能弄清楚。
该代码在下面,并包含说明其工作方式和一些潜在问题以及如何避免或解决这些问题的注释。我只在提供CDS提供者的TAdoQueries上对其进行了测试。
当我开始研究所有这些内容时,很快就会发现,通常的大师
+详细信息设置,尽管提供者+ CDS乐于从服务器刷新主数据,但是自从cdsMaster打开以来首次从服务器读取详细信息记录时,他们根本不会刷新详细记录。当然,这可能是设计使然。
我认为我不需要发布DFM来附带代码。我只是以通常的主从方式设置了AdoQueries(详细查询以主PK作为参数),DataSetProvider指向主AdoQuery,主CDS指向提供者,详细cDS指向了主体。 cdsMaster的DataSetField。为了进行实验并查看发生了什么,每个数据集都有DBGrids和DBNavigators。
简而言之,以下代码的工作方式是暂时将AdoQuery母版和CDS母版过滤到当前行,然后强制刷新当前母版行的数据和详细数据。这样,与我尝试过的其他方法不同,这样做会刷新嵌套在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;