如何在运行时更改Clientdataset字段的数据类型

5
对于在设计时定义字段的Delphi ClientDataSets,是否有办法在运行时更改特定字段的数据类型(即更改cds.Fields [n].DataType)?
我有一个遗留的Delphi 7程序,其中SQLDataSet和ClientDataSet字段都是在设计时设置的(以覆盖各种属性)。
这些字段连接到第三方Sybase SQL Anywhere 11数据库。
最近,供应商将所有“Description”字段从VarChar(128)更改为长varchar,但仅适用于某些客户。因此,当我查询这些“Description”字段时,我的代码必须支持两种类型的字段。
我希望在类字段类型上设置条件编译(然后在打开SQL / Client Dataset之前添加字段),但编译器忽略了类组件定义部分中的{$IFDEF}条件(经过思考,这样做是有道理的!)!
有数十个模块,数百个受影响的字段,因此任何见解都将不胜感激。

3
要么在运行时不添加该字段,并在那时添加正确的类型,要么在运行时删除该字段(如果需要),然后再添加一个您需要的类型。 - Ken White
就像@KenWhite所说的那样。如果涉及到的字段有对应的FieldDef,那么你需要删除并重新创建它,否则在打开CDS时可能会出现数据类型不匹配的错误,因为FieldDef中定义的数据类型与替换的TField不一致。个人而言,我倾向于重新创建所有的字段/FieldDefs,而不仅仅是涉及到的那些。 - MartynA
很不幸,数据库的变化比我最初想象的要多 - 这可能涉及到数千个字段(跨越多个项目)。这也会产生副作用 - 我广泛使用DBgrids - 我得到的是(MEMO)而不是数据。我可以设置字段的OnGetText - 但是又有很多字段... CDS在设计时定义,但我动态创建SQL - 我考虑只在SQL中进行CAST / Convert to VARCHAR。嗯,可能会出现fields [n] .DataSize等问题 - 将长varchar转换为最初在CDS中定义为VarChar [128]的字段可能会有问题... - edbored
@MartynA - 我从SDS和CDS中删除了该字段。然后使用条件编译来1)向公共声明添加适当的字段/类型;2)添加SDS fielddef;以及3)添加CDS fielddef和field。工作正常,但是现在网格中有一个“备忘录”。我有一种感觉我将会“分叉”代码... - edbored
“天啊,这太难了”,我想你可能会这么说(抱歉,我忍不住了)。说真的,我越想越倾向于避免拥有任何持久化字段/字段定义:要么在代码中创建它们,要么让 CDS 通过 SDS 从服务器接收到的内容创建它们。 - MartynA
如果这是新代码-当然可以。这是10年前的代码(在我了解CDS内部工作原理之前编写的),其中一个项目中有40个包含额外InternalCalc字段的CDS,大多数从数据库中提取的字段都在SDS中具有提供程序标志覆盖(在设计时在SDS字段编辑器中设置)。你可能是对的。不过明天早上不会完成了。 "该死"的确。 - edbored
1个回答

2
我之前也遇到过这个问题,不是在使用 CDS 上,而是在设计时使用 TADODataSet 持久字段。我认为下面的代码将帮助您了解如何修复/修补 CDS 数据集。
思路是查询相关表架构;获取实际的字段数据类型;并通过将持久字段从数据集中取消连接并添加一个匹配的新持久字段来“更改”持久字段类型:
// TData class    
procedure TData.DataModuleCreate(Sender: TObject);
var
  I: Integer;
begin
  for I := 0 to ComponentCount - 1 do
    if (Components[I] is TCustomADODataSet) then
      DataSetPrepareMemoFields(TDataSet(Components[I]));
end;

procedure TData.DataSetPrepareMemoFields(DataSet: TDataSet);
var
  Fld: TField;
  I: Integer;
  FldName, CompName: string;
  AOwner: TComponent;
begin
  // Here you need to query the actual table schema from the database 
  // e.g. ADOConnection.GetFieldNames and act accordingly

  // check which DataSet you need to change
  // if (DataSet = dsOrders) or ... then... 

  if DataSet.FieldList.Count > 0 then
    for I := DataSet.FieldList.Count - 1 downto 0 do 
    begin
      if DataSet.FieldList.Fields[I].ClassNameIs('TMemoField') and (DataSet.FieldList.Fields[I].FieldKind = fkData) then 
      begin
        // save TMemoField properties
        AOwner := DataSet.FieldList[I].Owner;
        CompName := DataSet.FieldList[I].Name;
        FldName := DataSet.FieldList.Fields[I].FieldName;
        // dispose of TMemoField
        DataSet.FieldList[I].DataSet := nil; // Un-Attach it from the DataSet
        // create TWideADOMemoField instead
        Fld := TWideADOMemoField.Create(AOwner); // Create new persistent Filed instead 
        Fld.Name := CompName + '_W';
        Fld.FieldName := FldName;
        Fld.DataSet := DataSet;
      end;
    end;
end;

说到这里,我解决了那个问题之后再也没有使用持久化字段了。现在我的所有字段都是在运行时生成的,包括计算/查找/内部字段。

我已经完成了转换,但这看起来很有趣。基于我通过自动声明引用字段的事实,我可以看到问题 - 例如在datamodule类声明中,我有myStringField:TStringField;如果我像上面那样替换数据类型,我认为我仍然可能会看到不兼容的类型错误。我需要花费一周左右的时间才能回到这个问题。目前先谢谢!它将帮助我向前迈进。 - edbored
我正在尝试这种技术,但是在数据集打开的情况下无法添加字段,而在关闭时也无法访问字段。 - Frank Pedro

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接