2012-11-22 40 views
8

簡記非物理場:我的代碼試圖更新德爾福XE TClientDataset非物理領域,(連接到TSQLQuerySQL屬性集),它們是由運行時Open命令創建的。我需要避免試圖更新德爾福的TClientdataSet連接到TSQLQuery

我具有連接到與連接到一個TSQLConnection一個TSQLQuery一個TDatasetProvider一個TClientDataset。這些對象中的前三個被封裝在一個庫中的幾個類中,我在很多地方使用了幾個項目。這些類在運行時創建這3個對象,並消除了大量的重複代碼,因爲我有許多這樣的三元組。

非常典型,我將在TSQLQuerySQL屬性中指定某些SQL,並呼籲TClientDataSetOpen加載從數據庫中TClientDataset。在TClientDatasetFields通過這個呼叫Open即創建。它們在Open之前不存在。

我遇到了一個問題,其中生成到TClientDataset的三個字段是非物理的;也就是說,SQL會進行計算以生成它們。不幸的是,在TClientDataset中,這3個字段的創建方式與物理字段不同。他們FieldKindfkData(理想情況下是fkInternalCalc),Calculated屬性是False(理想情況下是True)及其ProviderFlags包括pfInUpdate(在理想情況下不應該)。毫不奇怪,當談到時間做對TClientDataset一個異常被拋出的ApplyUpdates ...

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'. 

我可以在TDatasetProviderOnUpdateData事件處理程序清除這些領域的pfInUpdate標誌避免這個錯誤。然而,這個解決方案要求這個函數知道特定的字段名稱,它位於上面提到的泛型類中,因此破壞了代碼的一般性。

我在尋找的是將這些字段的計算性質通知給事件處理函數的通用方法。

Open呼叫後我不能改變他們的FieldKindCalculated性質(以fkInternalCalcTrue分別),因爲這產生一個WorkCDS: Cannot perform this operation on an open dataset異常消息。而且,由於Fields尚不存在,我無法在撥打Open之前更改這些屬性。

我可以Open之後,從這些FieldProviderFlags屬性中刪除pfInUpdate標誌,但這沒有得到傳遞到‘三角洲’TClientDatset即到達OnUpdateData事件處理程序。我也嘗試設置字段的FieldDefs.InternalCalcField屬性;再次,這不會傳遞給Delta數據集。

所以,我嘗試過的所有信令想法都沒有奏效。我會很感激任何新的想法或替代方法。

我遇到的所有互聯網搜索結果(包括Cary Jensen的優秀文章)都涉及設計時或非SQL生成的設置,這些設置不適用於我的情況。

+0

您的組件是從TClientDataSet派生的還是組合的? – jachguate

+0

希望我已經理解你的問題。我提到的兩個類本身不是組件,而是它們包含TClientDataSet,TDataSetProvider和TSQLQuery類,其中沒有一個是派生的,即。不分類。在這兩個類中,一個從另一個派生,而兩個派生的基類只從TObject派生。 –

+0

你如何打開內部的ClientDataSet?我的意思是,你是否在你的類上調用了一個方法,或者你直接調用內部的ClientDataSet.Open方法? – jachguate

回答

5

您可以在您的更新過程中創建一個機制,以便爲您想在忽略的各個字段預配置ProviderFlags。

根據你的問題的意見,我建議你在類中創建一個新的方法來打開內部的ClientDataSet,所有的魔法都會發生在這個方法內部。

首先,一個簡單的機制是包含一個新的TStringList屬性,該屬性列出了您想要忽略的所有字段,您將按名稱匹配。隨意採用這一點或創建一個更好的機制,重要的是你能夠確定你想要配置哪些字段。

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; 

把它作爲一個想法。

我寫了所有的代碼,可能語法錯了,但它顯示了整個想法。

+0

這個想法背後的原理是正確的,並回答我的信號需求。但是,正如我在之前的評論中提到的那樣,「ApplyUpdates」期間發生異常,而不是在調用「Open」時發生。爲了使用上面的解決方案,我會利用TDataSetProvider的OnUpdateData事件處理程序中的'UpdateIgnoredFields'屬性,因爲'Open'操作整個數據集,而'OnUpdateData'操作「Delta」數據集。非常感謝您的幫助。對不起,我不能投票給你,我的「信譽」是不夠的:-) –

+0

@Chris,顯示的方法只是在ApplyUpdates時間防止異常。通過刪除pfInUpdate標誌。現在我考慮了一下,你還必須刪除pfInWhere標誌。試試吧,你會看到這是如何工作的。 – jachguate

+0

您對刪除pfInWhere標誌的需求是正確的。但是,它無法清除主ClientDataSet上的ProviderFlags - 我在測試的早期嘗試了這種方法,但它只能在「Delta」數據集上運行。原因是,儘管您可以清除主數據集上的標誌,但不會將信號傳遞給仍然具有標誌設置的「Delta」數據集。我試圖追蹤VCL,但找不到「斷開」,但它絕對不行。因此需要在僅處理delta數據集的OnUpdateData事件處理程序中執行此操作。 –

1

通過在CDS上設置相應的可選參數,您可以將ProviderFlags(以及其他一些屬性)從客戶端傳遞到提供者(delta)端。不要忘記設置IncludeInDelta參數