グーグルは、マスターCDSを閉じて再度開くことなく、ネストされたClientDataSetsではまったく不可能であると述べている多数の記事を見つけました.OPはこの場合やりたくない. でも ...
私がテストしたかなり単純なケースでは、q に対する短い答えは「はい」です。必要な手順を正しく行うには、理解するのに時間がかかりました。
以下のコードには、その動作方法といくつかの潜在的な問題、およびそれらを回避または回避する方法を説明するコメントが含まれています。私は、CDS のプロバイダに供給する TAdoQueries でのみテストしました。
これらすべてを調査し始めたとき、通常のマスターと詳細のセットアップでは、プロバイダーと CDS はサーバーからマスター データを更新することを喜んでいるにもかかわらず、詳細レコードを一度更新すると、単に詳細レコードを更新しないことがすぐに明らかになりました。 cdsMaster が開かれてから初めてサーバーから読み取られました。もちろん、これは設計によるものかもしれません。
コードに合わせて DFM を投稿する必要はないと思います。AdoQueries を通常のマスター/ディテール方式 (パラメーターとしてマスターの PK を持つディテール クエリ) でセットアップし、マスター AdoQuery を指す DataSetProvider、プロバイダーを指すマスター CDS、およびcdsMaster の DataSetField。実験して何が起こっているかを確認するために、これらのデータセットごとに DBGrid と DBNavigator があります。
簡単に言うと、以下のコードが機能する方法は、AdoQuery マスターと CDS マスターを現在の行に一時的にフィルター処理し、それらのデータと現在のマスター行の詳細データを強制的に更新することです。このようにすると、他の方法とは異なり、cdsMaster の DataSet フィールドにネストされた詳細行が更新されます。
ところで、私が試した他の袋小路には、poFetchDetailsOnDemand を true に設定した場合としない場合があり、cdsMaster.FetchDetailsOnDemand と同じです。明らかに、「FetchDetailsOnDemand」は ReFetchDetailsOnDemand を意味するものではありません!
「ソリューション」を機能させるために1つか2つの問題に遭遇しました。最も難しい問題は、このSO
の質問で説明されています:
これが Sql Server 2000(!) バックエンドで正しく動作することを確認しました。これには、ISqlW からサーバーで発生した行データの変更が含まれます。また、Sql Server の Profiler を使用して、更新のネットワーク トラフィックに 1 つのマスター行とその詳細のみが含まれることを確認しました。
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;