我需要避免尝试更新连接到TSQLQuery的Delphi TClientDataset中的非物理字段

Precis:我的代码试图更新Delphi XE TClientDataset中的非物理字段(连接到SQL属性集的TSQLQuery ),这些字段是由运行时Open命令创build的。

我有一个TClientDataset连接到一个TDatasetProvider连接到TDatasetProvider连接到一个TSQLConnection 。 这些对象中的前三个封装在一个库中的几个类中,我在许多地方使用了几个项目。 这些类在运行时创build这3个对象,并消除了大量的重复代码,因为我有许多这样的三元组。

通常我会通过在TSQLQuerySQL属性中指定一些SQL并在TClientDataSet上调用Open来从数据库加载TClientDataSetTClientDataset中的Fields是通过调用Open来创build的。 它们在Open之前不存在。

TClientDataset生成的三个字段是非物理的情况下,我遇到了一个问题。 也就是说,SQL会进行计算来生成它们。 不幸的是,在TClientDataset ,这三个字段的创build方式与物理字段不同。 他们的FieldKindfkData (最好是fkInternalCalc ), Calculated属性是False (最好是True ),他们的ProviderFlags包含pfInUpdate (理想情况下它不应该)。 毫不奇怪,当要在TClientDataset上执行ApplyUpdates时,会抛出exception…

 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标志来避免此错误。 然而,这个解决scheme要求特定的字段名称被这个函数知道,它位于上面提到的generics类中,因此破坏了代码的一般性。

我正在寻找的是将这些字段的计算性质通知给事件处理函数的通用方法。

Open调用后,我无法更改其FieldKindCalculated属性(分别为fkInternalCalcTrue ),因为这会生成WorkCDS: Cannot perform this operation on an open datasetexception消息WorkCDS: Cannot perform this operation on an open dataset 。 而且,由于Fields还不存在,我无法在Open呼叫之前更改这些属性。

我可以从Open后从这些FieldProviderFlags属性中删除pfInUpdate标志,但是这不会传递到到达OnUpdateData事件处理程序的“Delta” TClientDatset 。 我也尝试设置字段的FieldDefs.InternalCalcField属性; 再次,这不会传递给Delta数据集。

所以,我尝试过的所有信号想法都没有奏效。 我会感谢任何新的想法或替代方法。

我遇到的所有互联网search结果(包括Cary Jensen的优秀文章)都涉及devise时或非SQL生成的设置,这些设置不适用于我的情况。

Solutions Collecting From Web of "我需要避免尝试更新连接到TSQLQuery的Delphi TClientDataset中的非物理字段"

你可以在你的类中创建一个机制来为你想在你的更新过程中忽略的单个字段预先配置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; 

把它作为一个想法。

我在这里写了所有的代码,也许语法是错误的,但它显示了整个想法。

您可以通过在CDS上设置相应的可选参数,将ProviderFlags(以及其他一些属性)从客户端传递给提供者(delta)端。 不要忘记设置IncludeInDelta参数