重构 TClient 数据集

发布于 2024-07-12 05:36:17 字数 74 浏览 13 评论 0原文

能否在不丢失数据的情况下重构 TClientDataSet XML 文件? 是否有任何演示应用程序或源代码可以展示如何进行此类重组?

Can a TClientDataSet XML file be restructured without losing data? Are there any demo applications or source code that shows how to do such a restructure?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

舂唻埖巳落 2024-07-19 05:36:17

是和否,xml 文档是使用 XLST 进行转换的,因此它需要符合该模板才能被 TClientDataSet 读取。

然而,这也意味着您还可以将文档转换为您喜欢的任何格式到单独的文档中,只是不能将转换后的文档直接加载到 TClientDataSet 中。

编辑:
糟糕,忘记发布示例了。

Code Central 上的此项目展示了从 clientdataset 到 ADO 记录集的转换。

yes and no, the xml doc is transformed using XLST so it needs to conform to that template to be able to be read by the TClientDataSet.

However that does also mean that you can also transform the doc to any format you like yourself into a separate doc, you just can't load the transformed doc directly into the TClientDataSet.

EDIT:
Oops, forgot to post an example.

This project on code central shows a transform from clientdataset to an ADO recordset.

私藏温柔 2024-07-19 05:36:17

为了更改磁盘上的 CDS 结构,我使用了下面概述的子类。 我们以二进制格式将数据写入流(在压缩/加密之前),但它对于 XML 格式的工作原理应该大致相同。

如果您需要从保存的数据集中添加/删除任何字段或更改字段定义,则只需增加数据集表版本即可。 每次打开数据集时,它都会将保存的版本号与当前版本号进行比较。 如果保存的表是旧的,它将被复制到新的结构中,因此,如果您需要进行更改,第一次重新加载该表时,性能将会受到影响,但之后它应该像往常一样从磁盘加载。

因此,如果您在合并后将 CDS 保存回磁盘 - 瞧 - 您的 XML 结构就会以 CDS 友好的格式更新。

TCDS = class(TCustomClientDataset)
private
 fTableVersion: integer;
 /// <summary> Copies records from source with potentially different table
 ///  structure/field defs from self, providing defaults for missing fields</summary>
 procedure CopyFromDataset(const ASource: TCustomClientDataset);
 /// <summary>Provide a default value, if necessary, for any new fields</summary>
 function GetDefaultValue(const AFieldName: string): variant;
public
 procedure LoadFromStream(AStream: TStream);
 procedure SaveToStream(AStream: TStream);
end;

procedure TCDS.LoadFromStream(AStream: TStream);
var
 ATemp: TCDS;
 APersistedVersion: integer;
begin
 AStream.ReadData(APersistedVersion);
 if APersistedVersion = fTableVersion then
 begin
  Close;
  ReadDataPacket(AStream, True);
  Open;
 end
 else if APersistedVersion < fTableVersion then
 begin
  // It's an old table structure:
  // - Load old structure into temp CDS
  // - Merge temp CDS records into new structure
  ATemp := TCDS.Create;
  try
   ATemp.Close;
   ATemp.ReadDataPacket(AStream, True);
   ATemp.Open;
   CopyFromDataset(ATemp);
  finally
   FreeAndNil(ATemp);
  end;
 end;
end;

procedure TCDS.SaveToStream(AStream: TStream);
begin
 AStream.WriteData(fVersionNumber);
 WriteDataPacket(AStream, True);
end;

procedure TCDS.CopyFromDataset(const ASource: TCustomClientDataset);
var
 ACurrentFieldNames: TStrings;
 i: integer;
begin
 // Assuming we don't want to keep any records already in dataset
 EmptyDataSet;
 ACurrentFieldNames := TStringList.Create;
 try
  Fields.GetFieldNames(ACurrentFieldNames);
  for i := 0 to ACurrentFieldNames.Count-1 do
   ACurrentFieldNames.Objects[i] := ASource.Fields.FindField(ACurrentFieldNames[i]);

  ASource.First;
  while not ASource.Eof do
  begin
   Append;
   for i := 0 to Fields.Count-1 do
   begin
    if Assigned(ACurrentFieldNames.Objects[i]) then
     Fields[i].Value := TField(ACurrentFieldNames.Objects[i]).Value
    else if Fields[i].Required then
     Fields[i].Value := GetDefaultValue(ACurrentFieldNames[i]);
    end;
    Post;
    ASource.Next;
   end;
 finally
  FreeAndNil(ACurrentFieldNames);
 end;
end; 

In order to make changes to CDS structure on disk I used a sub-class outlined below. We write our data in binary format to a stream (before compression/encryption) but it should work much the same for XML format.

If you need to add/remove any fields from your saved dataset or change the field definitions then you just increment the dataset table version. When the dataset is opened each time it compares the saved version number with the current. If the saved table is old it will be copied into the new structure, so if you need to make changes, you will take one performance hit the first time you reload the table, but after that it should load from disk as usual.

So, if you save the CDS back to disk after doing the merge - voila - your XML structure is updated, in a CDS friendly format.

TCDS = class(TCustomClientDataset)
private
 fTableVersion: integer;
 /// <summary> Copies records from source with potentially different table
 ///  structure/field defs from self, providing defaults for missing fields</summary>
 procedure CopyFromDataset(const ASource: TCustomClientDataset);
 /// <summary>Provide a default value, if necessary, for any new fields</summary>
 function GetDefaultValue(const AFieldName: string): variant;
public
 procedure LoadFromStream(AStream: TStream);
 procedure SaveToStream(AStream: TStream);
end;

procedure TCDS.LoadFromStream(AStream: TStream);
var
 ATemp: TCDS;
 APersistedVersion: integer;
begin
 AStream.ReadData(APersistedVersion);
 if APersistedVersion = fTableVersion then
 begin
  Close;
  ReadDataPacket(AStream, True);
  Open;
 end
 else if APersistedVersion < fTableVersion then
 begin
  // It's an old table structure:
  // - Load old structure into temp CDS
  // - Merge temp CDS records into new structure
  ATemp := TCDS.Create;
  try
   ATemp.Close;
   ATemp.ReadDataPacket(AStream, True);
   ATemp.Open;
   CopyFromDataset(ATemp);
  finally
   FreeAndNil(ATemp);
  end;
 end;
end;

procedure TCDS.SaveToStream(AStream: TStream);
begin
 AStream.WriteData(fVersionNumber);
 WriteDataPacket(AStream, True);
end;

procedure TCDS.CopyFromDataset(const ASource: TCustomClientDataset);
var
 ACurrentFieldNames: TStrings;
 i: integer;
begin
 // Assuming we don't want to keep any records already in dataset
 EmptyDataSet;
 ACurrentFieldNames := TStringList.Create;
 try
  Fields.GetFieldNames(ACurrentFieldNames);
  for i := 0 to ACurrentFieldNames.Count-1 do
   ACurrentFieldNames.Objects[i] := ASource.Fields.FindField(ACurrentFieldNames[i]);

  ASource.First;
  while not ASource.Eof do
  begin
   Append;
   for i := 0 to Fields.Count-1 do
   begin
    if Assigned(ACurrentFieldNames.Objects[i]) then
     Fields[i].Value := TField(ACurrentFieldNames.Objects[i]).Value
    else if Fields[i].Required then
     Fields[i].Value := GetDefaultValue(ACurrentFieldNames[i]);
    end;
    Post;
    ASource.Next;
   end;
 finally
  FreeAndNil(ACurrentFieldNames);
 end;
end; 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文