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

Chr*_*rgh 8 windows delphi tclientdataset

Precis:我的代码试图更新Delphi XE中的非物理字段TClientDataset(连接到TSQLQuerySQL属性集),这些字段是作为运行时Open命令创建的.

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

非常典型地我将加载TClientDataset从数据库通过在指定一些SQL SQL的性能TSQLQuery,并呼吁OpenTClientDataSet.将FieldsTClientDataset通过此调用创建OpenIE浏览器.他们之前不存在Open.

我遇到了一个问题,其中三个字段TClientDataset都是非物理的; 也就是说,SQL会进行计算以生成它们.不幸的是,在TClientDataset这三个字段中,与物理字段的创建方式不同; 他们FieldKindfkData(理想情况下是fkInternalCalc),Calculated属性是False(理想情况下是True)及其ProviderFlags包括pfInUpdate(在理想情况下不应该).毫不奇怪,当谈到时间做一个ApplyUpdatesTClientDataset抛出一个异常...

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'.
Run Code Online (Sandbox Code Playgroud)

我可以清除这些领域的避免这个错误pfInUpdate的标志TDatasetProviderOnUpdateData事件处理程序.但是,此解决方案要求此函数知道特定字段名称,该函数位于上述泛型类中,从而破坏了代码的通用性.

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

我不能改变他们的FieldKindCalculated性质(以fkInternalCalcTrue所述后分别)Open呼叫,因为这产生一个WorkCDS: Cannot perform this operation on an open dataset异常消息.并且,我不能在Open调用之前更改这些属性,因为Fields它还不存在.

我可以删除pfInUpdate这些标志FieldProviderFlags后性能Open但这并不获得通过到'三角’ TClientDatset即到达OnUpdateData事件处理程序.我也试过设置字段的FieldDefs.InternalCalcField属性; 再次,这不会传递到Delta数据集.

所以,我尝试过的所有信号传递方法都没有奏效.我会对任何新想法或替代方法表示感谢.

我遇到的所有互联网搜索结果 - 包括Cary Jensen的优秀文章 - 处理的设计时或非SQL生成的设置都不适用于我的情况.

jac*_*ate 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;
Run Code Online (Sandbox Code Playgroud)

把它作为一个想法.

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