8

Precis:我的代码试图更新作为运行时命令的结果创建的 Delphi XE 中的非物理字段(通过其属性集TClientDataset连接到 a )。TSQLQuerySQLOpen

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

通常我会通过在 的属性中指定TClientDataset一些 SQL并调用. 通过对ie的调用创建in 。它们之前不存在。SQLTSQLQueryOpenTClientDataSetFieldsTClientDatasetOpenOpen

我遇到了一个问题,其中生成的三个字段TClientDataset是非物理的;也就是说,SQL 进行计算以生成它们。不幸的是,在 中TClientDataset,这 3 个字段的创建与物理字段没有任何不同;它们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'.

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

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

调用后我无法更改它们的FieldKindCalculated属性(分别为fkInternalCalcTrue),Open因为这会生成WorkCDS: Cannot perform this operation on an open dataset异常消息。而且,我不能在Open调用之前更改这些属性,因为Fields它们还不存在。

之后我可以从这些属性中删除pfInUpdate标志,Field但这不会传递到到达事件处理程序的“Delta”上。我还尝试设置字段的属性;同样,这不会传递给 Delta 数据集。ProviderFlagsOpenTClientDatsetOnUpdateDataFieldDefs.InternalCalcField

所以,我尝试过的所有信号想法都没有奏效。如果有任何新想法或替代方法,我将不胜感激。

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

4

2 回答 2

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;

把它当作一个想法。

我在这里写了所有代码,可能语法错误,但它显示了整个想法。

于 2012-11-22T07:13:04.963 回答
1

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

于 2012-11-22T12:04:16.237 回答