2013-10-14 14 views
9

C'è un modo per aggiornare solo il DataSet di dettaglio senza ricaricare tutto il set di dati master?Aggiorna DataSet annidato con poFetchDetailsOnDemand

questo è quello che ho provato finora:

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

Ho anche provato:

DM.ClientDataSet1.Refresh; 

Ma il metodo di cui sopra aggiorna l'intero set di dati master, non solo il record corrente.

Ora, il seguente codice sembra di fare nulla:

DM.ClientDataSet1.RefreshRecord; 

C'è una soluzione o un modo corretto di fare quello che voglio? (Forse un interposizione ...)

Informazione supplementare:

ClientDataSet1 = Maestro Dataset

ClientDataSet2 = Particolare DataSet, è la seguente: *

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

proprietà del provider :

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

per quanto ne so, nel set di dati client nidificate non è possibile utilizzare 'Refresh' per il dettaglio set di dati. Controlla questo [articolo] (http://edn.embarcadero.com/article/29825) scritto da Cary Jensen e cerca la parola di aggiornamento. –

+0

Nell'articolo collegato l'autore ha affermato che non è possibile aggiornare un set di dati senza un provider di set di dati. ok. Quindi, è l'unico modo per aggiornare l'intero set di dati master? – EProgrammerNotFound

+0

Non sicuro al 100% ma così sembra. Ad ogni modo, che problema hai con l'aggiornamento del set di dati master? –

risposta

2

Cerca su Google trova numerosi articoli che dicono che non è possibile affatto con ClientDataSet nidificati senza chiudere e riaprire il CD master, che l'OP non vuole fare in questo caso. Tuttavia ...

La risposta breve alla q è sì, nel caso ragionevolmente semplice che ho provato, ed è piuttosto semplice, se un po 'prolisso; ottenere i necessari passi ha richiesto un po 'di tempo per capire.

Il codice è di sotto e include commenti che spiegano come funziona e alcuni potenziali problemi e come evita o funziona intorno a loro. L'ho provato solo con TAdoQueries che alimenta il provider di CDS.

Quando ho iniziato a guardare in tutto questo, divenne presto evidente che con il solito maestro + dettaglio di set-up, anche se Provider + CDS sono felici di aggiornare i dati anagrafici dal server, essi semplicemente non aggiornare la dettagli record una volta che sono stati letti dal server per la prima volta da quando è stato aperto cdsMaster. Questo può essere dovuto alla progettazione, ovviamente.

Non penso di dover pubblicare un DFM per andare con il codice. Ho semplicemente impostato AdoQueries nel solito modo master-detail (con la query di dettaglio che ha come parametro il PK del master), un DataSetProvider indirizzato al master AdoQuery, un master CDS puntato sul provider e un dettaglio cDS puntato sul DataSetField di cdsMaster. Per sperimentare e vedere cosa sta succedendo, ci sono DBGrid e DBNavigator per ciascuno di questi set di dati.

In breve, il modo in cui funziona il codice seguente consiste nel filtrare temporaneamente il master AdoQuery e il masterdown CDS nella riga corrente e quindi forzare un aggiornamento dei propri dati e dei dati dtail per la riga master corrente. Facendolo in questo modo, a differenza di qualsiasi altro che ho provato, i risultati nelle righe di dettaglio nidificate nel campo DataSet del cdsMaster vengono aggiornati.

Btw, gli altri vicoli ciechi che ho provato incluso con e senza poFetchDetailsOnDemand impostato su true, ditto cdsMaster.FetchDetailsOnDemand. Evidentemente "FetchDetailsOnDemand" non significa ReFetchDetailsOnDemand!

mi sono imbattuto in un problema o due ottenere la mia "soluzione" di lavoro, il stickiest uno è descritta in questa domanda SO: (!) Refreshing a ClientDataSet nested in a DataSetField

Ho verificato che questo funziona correttamente con un SQL Server 2000 back-end, inclusa la raccolta delle modifiche ai dati delle righe attivate sul server da ISqlW. Ho anche verificato, usando Sql Server's Profiler, che il traffico di rete in un aggiornamento coinvolge solo la singola riga master e i suoi dettagli.

Delphi 7 + Win7 64-bit, btw.

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

Bello, testerò e tornerò presto con altri commenti su questo. – EProgrammerNotFound