9

すべてのマスター データセットをリロードせずに、詳細 DataSet のみを更新する方法はありますか?

これは私がこれまでに試したことです:

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

私も試しました:

DM.ClientDataSet1.Refresh;

ただし、上記の方法では、現在のレコードだけでなく、マスター データセット全体が更新されます。

さて、次のコードは何でもするようです:

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
4

1 に答える 1

2

グーグルは、マスター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;
于 2014-07-13T16:33:39.083 に答える