为了更改磁盘上的cds结构,我使用了下面列出的一个子类。我们以二进制格式将数据写入流(在压缩/加密之前),但是对于XML格式,它的工作原理应该是一样的。
如果需要从保存的数据集中添加/删除任何字段或更改字段定义,则只需增加数据集表的版本。每次打开数据集时,它都会将保存的版本号与当前版本号进行比较。如果保存的表是旧的,它将被复制到新的结构中,因此如果需要进行更改,则在第一次重新加载表时会受到一次性能影响,但在这之后,它应该像往常一样从磁盘加载。
因此,如果在执行merge-voila之后将CD保存回磁盘,XML结构将以CD友好格式更新。
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;