[英]How can I save and restore the state of a Delphi component properly?
我正在尝试保存组件的状态,然后再恢复它。 我已经制作了一个小班级助手来这样做,但 Delphi 恢复班级的顺序导致了问题。 它正确恢复定界符,然后设置 RecordFormat,后者又将定界符写入分号。 所以我最终得到了错误的值。 你对我如何解决这个问题有什么建议吗? 我喜欢使用类助手的简单性,但如果我以后必须处理它,它会变得复杂。
interface
type
TFDBatchMoveTextReaderHelper = class helper for TFDBatchMoveTextReader
private
function GetState: String;
procedure SetState( const Value: String );
published
property State: String read GetState write SetState;
end;
implementation
function ComponentToStringProc( Component: TComponent ): string;
var
BinStream: TMemoryStream;
StrStream: TStringStream;
s : string;
begin
BinStream := TMemoryStream.Create;
try
StrStream := TStringStream.Create( s );
try
BinStream.WriteComponent( Component );
BinStream.Seek( 0, soFromBeginning );
ObjectBinaryToText( BinStream, StrStream );
StrStream.Seek( 0, soFromBeginning );
Result := StrStream.DataString;
finally
StrStream.Free;
end;
finally
BinStream.Free
end;
end;
procedure StrtoComp( Value: string; AComponent: TComponent );
var
StrStream: TStringStream;
BinStream: TMemoryStream;
begin
StrStream := TStringStream.Create( Value );
try
BinStream := TMemoryStream.Create;
try
ObjectTextToBinary( StrStream, BinStream );
BinStream.Seek( 0, soFromBeginning );
BinStream.ReadComponent( AComponent );
finally
BinStream.Free;
end;
finally
StrStream.Free;
end;
end;
{ TFDBatchMoveTextReaderHelper }
function TFDBatchMoveTextReaderHelper.GetState: String;
begin
Result := ComponentToStringProc( Self );
end;
procedure TFDBatchMoveTextReaderHelper.SetState( const Value: String );
begin
StrtoComp( Value, Self );
end;
initialization
RegisterClass( TFDBatchMoveTextReader );
end.
这是我的结果(忽略文件名中的差异,我在再次记录状态之前更改了它。这是我遇到问题的 DataDef 值)
Text Compare
Produced: 27/03/2020 07:56:58
Mode: All
Left file: Clipboard at 27/03/2020 07:54:09 Right file: Clipboard at 27/03/2020 07:54:18
AReaderState from the database = object TextReader: TFDBatchMoveTextReader <> lReader.State after applied to the object = object TextReader: TFDBatchMoveTextReader
FileName = 'c:\molen\impexp\import\adm_loads_foynes.csv' FileName =
'C:\Users\stevesinclair\AppData\Local\Temp\96FFA4BD1C064E77972398' +
'7AD6E15495\adm_loads_foynes.csv'
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
DataDef.Fields = < = DataDef.Fields = <
item item
FieldName = 'LOAD_ID' FieldName = 'LOAD_ID'
DataType = atLongInt DataType = atLongInt
FieldSize = 9 FieldSize = 9
end end
item item
FieldName = 'SITE_ID' FieldName = 'SITE_ID'
DataType = atLongInt DataType = atLongInt
FieldSize = 5 FieldSize = 5
end end
item item
FieldName = 'VEHICLE_REG' FieldName = 'VEHICLE_REG'
DataType = atString DataType = atString
FieldSize = 32 FieldSize = 32
end end
item item
FieldName = 'CREATED_BY' FieldName = 'CREATED_BY'
DataType = atString DataType = atString
FieldSize = 32 FieldSize = 32
end end
item item
FieldName = 'UPDATED_BY' FieldName = 'UPDATED_BY'
DataType = atString DataType = atString
FieldSize = 32 FieldSize = 32
end end
item item
FieldName = 'LDD_ID' FieldName = 'LDD_ID'
DataType = atLongInt DataType = atLongInt
FieldSize = 9 FieldSize = 9
end end
item item
FieldName = 'TRANS_TYPE_ID' FieldName = 'TRANS_TYPE_ID'
DataType = atLongInt DataType = atLongInt
FieldSize = 3 FieldSize = 3
end end
item item
FieldName = 'STAGE_ID' FieldName = 'STAGE_ID'
DataType = atLongInt DataType = atLongInt
FieldSize = 1 FieldSize = 1
end end
item item
FieldName = 'ACCOUNT_ID' FieldName = 'ACCOUNT_ID'
DataType = atLongInt DataType = atLongInt
FieldSize = 9 FieldSize = 9
end end
item item
FieldName = 'COMMODITY_ID' FieldName = 'COMMODITY_ID'
DataType = atLongInt DataType = atLongInt
FieldSize = 9 FieldSize = 9
end end
item item
FieldName = 'FLN_ID' FieldName = 'FLN_ID'
DataType = atLongInt DataType = atLongInt
FieldSize = 9 FieldSize = 9
end end
item item
FieldName = 'TLN_ID' FieldName = 'TLN_ID'
DataType = atLongInt DataType = atLongInt
FieldSize = 9 FieldSize = 9
end end
item item
FieldName = 'NET_WEIGHT' FieldName = 'NET_WEIGHT'
DataType = atLongInt DataType = atLongInt
FieldSize = 5 FieldSize = 5
end end
item item
FieldName = 'CREATED_DATE' FieldName = 'CREATED_DATE'
DataType = atDateTime DataType = atDateTime
FieldSize = 19 FieldSize = 19
end end
item item
FieldName = 'UPDATED_DATE' FieldName = 'UPDATED_DATE'
DataType = atDate DataType = atDate
FieldSize = 10 FieldSize = 10
end end
item item
FieldName = 'UPDATED_TIME' FieldName = 'UPDATED_TIME'
DataType = atTime DataType = atTime
FieldSize = 8 FieldSize = 8
end end
item item
FieldName = 'TRAILER_REG' FieldName = 'TRAILER_REG'
DataType = atString DataType = atString
FieldSize = 6 FieldSize = 6
end end
item item
FieldName = 'TRANSACTION_DATE' FieldName = 'TRANSACTION_DATE'
DataType = atDateTime DataType = atDateTime
FieldSize = 19 FieldSize = 19
end end
item item
FieldName = 'IOX' FieldName = 'IOX'
DataType = atString DataType = atString
FieldSize = 1 FieldSize = 1
end end
item item
FieldName = 'SHIP_ID' FieldName = 'SHIP_ID'
DataType = atLongInt DataType = atLongInt
FieldSize = 9 FieldSize = 9
end> end>
DataDef.Delimiter = '"' DataDef.Delimiter = '"'
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
DataDef.Separator = ',' <> DataDef.Separator = ';'
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
DataDef.RecordFormat = rfCustom = DataDef.RecordFormat = rfCustom
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
DataDef.FormatSettings.DateSeparator = '/' +-
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
DataDef.FormatSettings.ShortDateFormat = 'DD/MM/YYYY' = DataDef.FormatSettings.ShortDateFormat = 'DD/MM/YYYY'
DataDef.FormatSettings.ShortTimeFormat = 'HH:mm:ss' DataDef.FormatSettings.ShortTimeFormat = 'HH:mm:ss'
Left = 430 Left = 430
Top = 20 Top = 20
end end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
这是我为此编写的一个测试,它通过了。
procedure TMyTestObject.TestReaderStaterfCustom;
var
LOriginalReader: TFDBatchMoveTextReader;
LRestoredReader: TFDBatchMoveTextReader;
const
LDelimiter = '|';
LSeparator = ',';
begin
LOriginalReader := TFDBatchMoveTextReader.Create( nil );
LRestoredReader := TFDBatchMoveTextReader.Create( nil );
try
LOriginalReader.DataDef.RecordFormat := rfCustom;
LOriginalReader.DataDef.Delimiter := LDelimiter;
LOriginalReader.DataDef.Separator := LSeparator;
LRestoredReader.State := LOriginalReader.State;
Assert.AreEqual( LOriginalReader.State, LRestoredReader.State, 'LOriginalReader.State, LRestoredReader.State (after setting)' );
Assert.AreEqual( LOriginalReader.DataDef.Delimiter, LRestoredReader.DataDef.Delimiter );
Assert.AreEqual( LOriginalReader.DataDef.Separator, LRestoredReader.DataDef.Separator );
Assert.AreEqual( LDelimiter, LRestoredReader.DataDef.Delimiter );
Assert.AreEqual( LSeparator, LRestoredReader.DataDef.Separator );
finally
LOriginalReader.Free;
LRestoredReader.Free;
end;
end;
但是当我尝试在我的应用程序中使用它时它失败了。 我已经将一些日志记录在 setter 中,如下所示:
procedure TFDBatchMoveTextReaderHelper.SetState( const Value: String );
begin
CodeSite.Send( 'TFDBatchMoveTextReaderHelper.SetState', Value );
StrtoComp( Value, Self );
CodeSite.Send( 'self.DataDef.Separator', self.DataDef.Separator );
end;
将分隔符记录为 self.DataDef.Separator = ; 这是没有意义的,因为状态字符串字符串中的值是:
DataDef.Delimiter = '"'
DataDef.Separator = ','
DataDef.RecordFormat = rfCustom
DataDef.FormatSettings.DateSeparator = '/'
DataDef.FormatSettings.ShortDateFormat = 'DD/MM/YYYY'
DataDef.FormatSettings.ShortTimeFormat = 'HH:mm:ss'
这就是我在应用程序中实际使用它的方式:
lConn := TFDConnection.Create( nil );
try
lConn.ConnectionDefName := AConnectionDefName;
lMover := TFDBatchMove.Create( lConn );
lMemTable := TFDMemTable.Create( lConn );
lReader := TFDBatchMoveTextReader.Create( lConn );
lReader.State := AReaderState;
lReader.FileName := lTempFileName;
lWriter := TFDBatchMoveDataSetWriter.Create( lConn );
lWriter.Dataset := lMemTable;
lQuery := TFDQuery.Create( lConn );
lQuery.Connection := lConn;
lQuery.SQL.Text := ATargetSQL;
try
lQuery.Prepare;
except
on E: Exception do
begin
CodeSite.SendException( Format( 'Error perparing query: %s', [ ATargetSQL ] ), E );
raise;
end
end;
lMover.Reader := lReader;
lMover.Writer := lWriter;
在应用状态字符串后,我已经解决了这个设置分隔符和定界符的问题。 在我的休息中,我发现状态保存的顺序导致了问题。 如果 DataDef.RecordFormat 在 DataDef.Delimiter 和 DataDef.Separator 之前,它就起作用了。 但是我无法控制它是如何保存的,所以我在读回它时已经破解了它。
procedure TFDBatchMoveTextReaderHelper.SetState( const Value: String );
var
RegEx: TRegEx;
Match: TMatch;
const
PatternRecordFormat = 'DataDef\.RecordFormat *= *(rfCustom)';
PatternSeparator = 'DataDef\.Separator *= *''(.)''';
PatternDelimiter = 'DataDef\.Delimiter *= *''(.)''';
begin
StrtoComp( Value, Self );
RegEx := TRegEx.Create( PatternRecordFormat, [ roIgnoreCase, roMultiLine ] );
Match := RegEx.Match( Value );
if Match.Success and ( Match.Groups.Count > 1 ) then
begin
RegEx := TRegEx.Create( PatternSeparator, [ roIgnoreCase, roMultiLine ] );
Match := RegEx.Match( Value );
if Match.Success and ( Match.Groups.Count > 1 ) then
Self.DataDef.Separator := ( Match.Groups[ 1 ].Value + ',' )[ 1 ];
RegEx := TRegEx.Create( PatternDelimiter, [ roIgnoreCase, roMultiLine ] );
Match := RegEx.Match( Value );
if Match.Success and ( Match.Groups.Count > 1 ) then
Self.DataDef.Delimiter := ( Match.Groups[ 1 ].Value + '"' )[ 1 ];
end;
end;
不是我知道的最好的答案,但这是我能想出的全部。 这是我的测试以确认它有效。
[ Test ]
[ TestCase( 'Comma Double Quote', 'rfCommaDoubleQuote' ) ]
[ TestCase( 'Semicolon Double Quote', 'rfSemicolonDoubleQuote' ) ]
[ TestCase( 'Tab Double Quote', 'rfTabDoubleQuote' ) ]
[ TestCase( 'Fixed Length', 'rfFixedLength' ) ]
[ TestCase( 'Field Per Line', 'rfFieldPerLine' ) ]
[ TestCase( 'Bar Double Quote', 'rfCustom' ) ]
procedure TestReaderState( const ARecordFormat: TFDTextRecordFormat );
procedure TMyTestObject.TestReaderState( const ARecordFormat: TFDTextRecordFormat );
var
LOriginalReader: TFDBatchMoveTextReader;
LRestoredReader: TFDBatchMoveTextReader;
LDelimiter : Char;
LSeparator : Char;
LWithFieldNames: Boolean;
begin
LOriginalReader := TFDBatchMoveTextReader.Create( nil );
LRestoredReader := TFDBatchMoveTextReader.Create( nil );
try
case ARecordFormat of
rfCommaDoubleQuote:
begin
LDelimiter := #34;
LSeparator := #44;
end;
rfSemicolonDoubleQuote:
begin
LDelimiter := #34;
LSeparator := #59;
end;
rfTabDoubleQuote:
begin
LDelimiter := #34;
LSeparator := #9;
end;
rfFixedLength:
begin
LDelimiter := #0;
LSeparator := #0;
LWithFieldNames := False;
end;
rfFieldPerLine:
begin
LDelimiter := #0;
LSeparator := #0;
LWithFieldNames := False;
end;
rfCustom:
begin
LDelimiter := #124;
LSeparator := #44;
end;
else
begin
LDelimiter := #34;
LSeparator := #59;
end;
end;
LOriginalReader.DataDef.RecordFormat := ARecordFormat;
if LOriginalReader.DataDef.RecordFormat = ARecordFormat then
begin
LOriginalReader.DataDef.Delimiter := LDelimiter;
LOriginalReader.DataDef.Separator := LSeparator;
end;
LRestoredReader.State := LOriginalReader.State;
Assert.AreEqual( LOriginalReader.State, LRestoredReader.State, 'LOriginalReader.State, LRestoredReader.State (after setting)' );
Assert.AreEqual( LOriginalReader.DataDef.Delimiter, LRestoredReader.DataDef.Delimiter );
Assert.AreEqual( LOriginalReader.DataDef.Separator, LRestoredReader.DataDef.Separator );
Assert.AreEqual( LDelimiter, LRestoredReader.DataDef.Delimiter );
Assert.AreEqual( LSeparator, LRestoredReader.DataDef.Separator );
Assert.AreEqual( LOriginalReader.DataDef.WithFieldNames, LRestoredReader.DataDef.WithFieldNames )
finally
LOriginalReader.Free;
LRestoredReader.Free;
end;
end;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.