Delphi ClientDataSet - 如何向现有数据集添加新的数据字段?

Delphi ClientDataSet - How to add new data fields to existing dataset?

我在弄清楚如何将新数据字段添加到旧数据集文件时遇到问题。例如,旧数据集可能只有一个 ID 字段。后来我们决定我们需要一个 ISACTIVE 字段。我想重新打开我的仅 ID 数据,然后在添加 ISACTIVE 值的情况下重新保存它。例如:

CDS := TClientDataset.Create(nil);
with TIntegerField.Create(CDS) do
begin
  FieldName := 'ID';
  FieldKind := fkData;
  DataSet := CDS;
end;
CDS.CreateDataSet;

CDS.Close;
with TBooleanField.Create(CDS) do
begin
  FieldName := 'ISACTIVE';
  FieldKind := fkData;
  DataSet := CDS;
end;
CDS.Open; // <--Raises EDatabaseError with message 'Field 'ISACTIVE' not found'.

我查找了类似的问题,我找到的最接近的问题是仅涉及将新的计算字段添加到数据集。上述方法适用于添加计算字段。

目前我能想到的唯一(混乱)解决方案是将仅 ID 数据加载到临时数据集中,然后创建一个定义了 ID 和 ISACTIVE 字段的新数据集,然后遍历仅 ID数据集并将记录复制到新数据集。

有一个简单的方法可以做到这一点。

如果您有一个带有整数 ID 字段和字符串 80 名称字段的 CDS,并且您将数据集保存到 XML,如

AFileName := 'C:\Temp\CDSData.Xml';
CDS1.SaveToFile(AFileName, dfXML);

生成的 XML 文件将如下所示(对于 D7)

<?xml version="1.0" standalone="yes"?>  
<DATAPACKET Version="2.0">
  <METADATA>
    <FIELDS>
      <FIELD attrname="ID" fieldtype="i4"/>
      <FIELD attrname="Name" fieldtype="string" WIDTH="80"/>
    </FIELDS><PARAMS CHANGE_LOG="1 0 4"/>
  </METADATA>
  <ROWDATA>
    <ROW RowState="4" ID="1" Name="one"/>
  </ROWDATA>
</DATAPACKET>

然后您可以使用 MSXML 或您最喜欢的 XML 处理器进行微不足道的更改,向 METADATA 添加额外的 FIELD 节点定义CCDS 的数据包,添加额外字段。然后从 XML 重新加载 CDS。添加的字段值当然将为 NULL,要使此技术起作用,您在从已保存的 XML.

重新加载时不得在 CDS 上定义持久的 TFields

示例代码:

procedure TForm1.CopyWithAddedFields;
var
  SS : TStringStream;
  XMLDoc : IXmlDomDocument;
  FieldsNode : IXmlDomNode;
  FieldElement : IXmlDomElement;
begin
  SS := TStringStream.Create('');
  try
    //  Save the CDS's current contents in XML format, close it and clear any presistent fields
    CDS1.SaveToStream(SS, dfXML);
    CDS1.Close;
    CDS1.Fields.Clear;

    //  Next create an XML Document object and load the saved dataset into it
    XMLDoc := CoDomDocument.Create;
    XMLDoc.LoadXML(SS.DataString);

    //  Find the FIELDS node and add a new FIELD node to it       
    FieldsNode := XMLDoc.selectSingleNode('/DATAPACKET/METADATA/FIELDS');
    FieldElement := XMLDoc.createElement('FIELD');
    FieldElement.SetAttribute('attrname', 'Active');
    FieldElement.SetAttribute('fieldtype', 'boolean');
    FieldsNode.appendChild(FieldElement);

    // Save the XML to the stream
    SS.Size := 0;
    SS.WriteString(XmlDoc.xml);
    SS.Position := 0;

    //  Reload the ClientDataset
    CDS1.LoadFromStream(SS);
  finally
    XMLDoc.Free;
    SS.Free;
  end;
end;

显然,如果您愿意,可以将修改后的 XML 加载到不同的 CDS 中。

当然,您甚至可以将额外的 FIELD 节点添加到 XML 中,只需将其加载到 TStringList 中,前提是您准备好自己进行一定数量的字符串操作。

Fwiw,我在尝试修改 CDS 的 XML 以包含 XML 文件中每个 ROW 节点的额外信息时偶然发现了这个诡计;事实证明,LoadFromFile & LoadFromStream 进程完全没有注意到我添加的信息。

嗯,计算字段出了什么问题?能否请您解释一下您不想使用它们的原因?

一个额外提示:您是否尝试过 InternalCalc (FieldKind = fkInternalCalc)。据我所知,它的行为应该类似于数据字段(它的值存储在数据集记录中)

PS 添加计算字段时无需重新加载数据(关闭并重新打开数据集)