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 添加计算字段时无需重新加载数据(关闭并重新打开数据集)
我在弄清楚如何将新数据字段添加到旧数据集文件时遇到问题。例如,旧数据集可能只有一个 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.
示例代码:
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 添加计算字段时无需重新加载数据(关闭并重新打开数据集)