2012-11-22 9 views
8

Precis: Il mio codice sta tentando di aggiornare i campi non-fisici in un Delphi XE TClientDataset, (collegati ad un TSQLQuery con il suo set di SQL immobili) che sono stati creati come risultato di un comando di runtime Open.Ho bisogno di evitare di tentare di aggiornare i campi non-fisici in un Delphi TClientDataset collegato ad un TSQLQuery

Ho un TClientDataset connesso a un TDatasetProvider collegato a un TSQLQuery collegato a un TSQLConnection. I primi 3 di questi oggetti sono incapsulati in un paio di classi in una libreria che uso in molti posti su diversi progetti. Queste classi creano questi 3 oggetti in fase di esecuzione ed eliminano una quantità significativa di codice ripetitivo, necessario in quanto ho molte, molte di queste terzine.

Piuttosto tipicamente io caricare il TClientDataset da un database specificando alcuni SQL nella SQL proprietà della TSQLQuery e chiamando Open sul TClientDataSet. Il Fields nel TClientDataset vengono creati tramite questa chiamata a Open ie. non esistono prima di Open.

Ho incontrato un problema in una situazione in cui tre dei campi generati nello TClientDataset non sono fisici; cioè, l'SQL esegue calcoli per generarli. Sfortunatamente, nel TClientDataset, questi 3 campi non vengono creati in modo diverso rispetto ai campi fisici; loro FieldKind è fkData (in teoria sarebbe fkInternalCalc), Calculated proprietà è False (ideale sarebbe True) e la loro ProviderFlags includere pfInUpdate (che idealmente non dovrebbe). Non sorprende che, quando arriva il momento di fare un ApplyUpdates sul TClientDataset viene generata un'eccezione ...

Project XXX.exe raised exception class TDBXError with message 
SQL State: 42S22, SQL Error Code: 207 Invalid column name 'Received'. 
SQL State: 42S22, SQL Error Code: 207 Invalid column name 'Issued'. 
SQL State: 42S22, SQL Error Code: 207 Invalid column name 'DisplayTime'. 

posso evitare questo errore eliminando pfInUpdate bandiere di questi campi nel gestore OnUpdateData evento s' il TDatasetProvider. Tuttavia questa soluzione richiede che i nomi dei campi specifici siano noti a questa funzione che si trova nelle classi generiche menzionate sopra, rompendo così la generalità del codice.

Quello che sto cercando è un mezzo generico per segnalare la natura calcolata di questi campi alla funzione del gestore eventi.

non riesco a cambiare le loro FieldKind o Calculated proprietà (rispettivamente fkInternalCalc e True) dopo la chiamata Open come questo genera un messaggio WorkCDS: Cannot perform this operation on an open dataset eccezione. E, non posso modificare queste proprietà prima della chiamata Open poiché lo Fields non esiste ancora.

posso rimuovere la bandiera pfInUpdate da ProviderFlags proprietà 's questi Field dopo Open ma questo non viene più passato sul 'Delta' TClientDatset che arriva al gestore OnUpdateData eventi. Ho anche provato a impostare le proprietà del campo FieldDefs.InternalCalcField; ancora una volta questo non viene passato al set di dati Delta.

Quindi, tutte le idee di segnalazione che ho provato non hanno funzionato. Sarei grato per eventuali nuove idee o un approccio alternativo.

tutti i risultati della ricerca su Internet che ho incontrato - tra cui eccellenti articoli di Cary Jensen - accordo con la fase di progettazione o di non-SQL generato messe a punto che non si applicano alla mia situazione.

+0

Il componente deriva da TClientDataSet o è una composizione? – jachguate

+0

Spero di aver capito la tua domanda. Le due classi che ho citato non sono componenti in sé, ma contengono le classi TClientDataSet, TDataSetProvider e TSQLQuery, nessuna delle quali è derivata ie. non sottoclasse. Delle due classi, una deriva dall'altra, ma la classe base dei due deriva solo da TObject. –

+0

Come si _open_ il ClientDataSet interno? Voglio dire, chiami un metodo sulla tua classe o chiami direttamente il metodo interno ClientDataSet.Open? – jachguate

risposta

5

È possibile creare un meccanismo nella classe per preconfigurare ProviderFlags per i singoli campi che si desidera ignorare nel processo di aggiornamento.

Come per i commenti sulla tua domanda, ti sto suggerendo di creare un nuovo metodo nella classe per aprire il ClientDataSet interno, tutta la magia avrà luogo all'interno di questo metodo.

In primo luogo, un semplice meccanismo consiste nell'includere una nuova proprietà TStringList che elenca tutti i campi che si desidera ignorare, che corrisponderanno per nome. Sentiti libero di adottare questo o creare un nuovo meccanismo migliore, l'importante è che tu sia in grado di identificare quali campi vuoi configurare in quel modo.

type 
    TMyClass = class 
    // all your current class here 
    private 
    FUpdateIgnoredFields: TStringList; 
    public 
    property UpdateIgnoredFields: TStringList read FUpdateIgnoredFields write SetUpdateIgnoredFields; 
    //don't forget to create this in your constructor, free it in the destructor 
    //and Assign any new value in the SetUpdateIgnoreFields method, as usual. 
    procedure OpenInnerCDS; //the magic goes here 
    end; 

procedure TMyClass.OpenInnerCDS; 
var 
    FieldName: string; 
    AFieldToIgnore: TField; 
begin 
    //opens the inner cds, but before that, configures the update-ignored 
    //fields in the underlying dataset 
    //Let's call it InnerBaseDataSet; 
    FInnerBaseDataSet.Open; //this opens the DataSet and creates all the fields for it. 
    try 
    for FieldName in FUpdateIgnoredFields do 
    begin 
     AFieldToIgnore := FInnerBaseDataSet.FindField(FieldName); 
     if Assigned(AFieldToIgnore) then 
     AFieldToIgnore.ProviderFlags := AFieldToIgnore.ProviderFlags - [pfInUpdate, pfInWhere]; 
    end; 
    //now, let's open the ClientDataSet; 
    FInnerClientDataSet.Open; 
    finally 
    //I suggest no matter what happens, always close the inner data set 
    //but it depends on how the CDS->Provider->DataSet interaction is configured 
    FInnerBaseDataSet.Close; 
    end; 
end; 

//the way you use this is to replace the current ClientDataSetOpen with something like: 

var 
    MyInsance: TMyClass; 
begin 
    MyInstance := TMyInstance.Create(); //params 
    try 
    //configuration code here 
    //MyInstance.InnerCDS.Open; <-- not directly now 
    MyInstance.UpdateIgnoreFields.Add('CALCULATED_SALARY'); 
    MyInstance.OpenInnerCDS; 
    //use the CDS here. 
    MyInstance.InnerCDS.ApplyUpdates(-1); //safely apply updates now. 
    finally 
    MyInstance.Free; 
    end; 
end; 

Prendere come idea.

Ho scritto tutto il codice qui, forse la sintassi è sbagliata, ma mostra l'intera idea.

+0

Il principio alla base di questa idea è corretto e risponde alle mie esigenze di segnalazione. Tuttavia, come ho menzionato nel mio commento precedente, l'eccezione si verifica durante 'ApplyUpdates' piuttosto che quando si chiama' Open'. Per utilizzare la soluzione sopra, utilizzerei la proprietà 'UpdateIgnoredFields' all'interno del gestore di eventi' OnUpdateData' di TDataSetProvider perché 'Open' opera sull'intero dataset mentre 'OnUpdateData' opera sul set di dati" Delta ". Molte grazie per il vostro aiuto. Mi spiace di non poterti votare, la mia "reputazione" è insufficiente :-) –

+0

@Chris, l'approccio mostrato impedisce solo l'eccezione al tempo di ApplyUpdates. Rimuovendo il flag pfInUpdate. Ora che ci penso un po 'di più devi rimuovere anche il flag pfInWhere. Provalo e vedrai come funziona. – jachguate

+0

Hai ragione sulla necessità di rimuovere anche il flag pfInWhere. Tuttavia, non funziona per cancellare ProviderFlags sul ClientDataSet principale - l'ho provato senza successo all'inizio del mio test - funziona solo sul set di dati "Delta". Il motivo è che, sebbene sia possibile cancellare i flag sul set di dati principale, questo non viene segnalato al set di dati "Delta", che ha ancora i flag impostati. Ho provato a tracciare il VCL ma non ho trovato la "disconnessione", ma sicuramente non funziona. Di qui la necessità di farlo nel gestore di eventi OnUpdateData che tratta solo con il set di dati delta. –

1

è possibile passare ProviderFlags (così come poche altre proprietà) dal lato client a quello del provider (delta) impostando i parametri opzionali corrispondenti su CDS. non dimenticare di impostare il parametro IncludeInDelta

Problemi correlati