2013-10-14 49 views
9

有沒有辦法只刷新Detail DataSet而無需重新加載所有主數據集?使用poFetchDetailsOnDemand刷新嵌套數據集

這是我到目前爲止已經試過:

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

我也曾嘗試:

DM.ClientDataSet1.Refresh; 

但上述方法刷新整個主數據集,而不僅僅是當前的記錄。

現在,下面的代碼似乎做任何事情:

DM.ClientDataSet1.RefreshRecord; 

是否有解決方法或做我想做什麼有道? (也許一插...)

附加信息:

ClientDataSet1 =主數據集

ClientDataSet2 =詳細信息數據,如下:*

object ClientDataSet2: TClientDataSet 
    Aggregates = <> 
    DataSetField = ClientDataSet1ADOQuery2 
    FetchOnDemand = False 
    ..... 
end 

Provider屬性:

object DataSetProvider1: TDataSetProvider 
    DataSet = ADOQuery1 
    Options = [poFetchDetailsOnDemand] 
    UpdateMode = upWhereKeyOnly 
    Left = 24 
    Top = 104 
    end 
+0

AFAIK,在嵌套的客戶端數據集中,不能將'Refresh'用於詳細數據集。查看Cary Jensen編寫的這篇文章(http://edn.embarcadero.com/article/29825)並搜索更新詞。 –

+0

在鏈接的文章中,autor說我不能在沒有數據集提供程序的情況下刷新數據集。好。那麼,刷新整個主數據集的唯一方法是什麼? – EProgrammerNotFound

+0

不是100%確定,但似乎。無論如何,你有什麼問題刷新主數據集? –

回答

2

谷歌搜索發現大量文章表示,如果不關閉並重新打開主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; 
+0

不錯,我會測試並很快回來,對此有更多評論。 – EProgrammerNotFound