- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有一个如下所示的 TComponent 类派生类,尝试保存到 clientdataset blob 字段:(摘自网络,注明出处)
type
TSaveComponent = class(TComponent)
private
FFileName: string;
public
constructor Create(AFileName:string);
destructor Destroy;
procedure ReadFromBlobField1(AField: TField);
procedure SaveToBlobField1(AField: TField);
end;
...
constructor TSaveComponent.Create(AFileName: string);
begin
Name := Copy(Self.ClassName, 2, 99);
FFileName := AFileName; //-- disabled file saving for now
end;
procedure TSaveComponent.ReadFromBlobField1(AField: TField);
var
Stream: TStream;
i: integer;
begin
try
Stream := TClientDataSet(AField.DataSet).CreateBlobStream(AField, bmRead);
try
{delete the all child components}
for i := Self.ComponentCount - 1 downto 0 do
Self.Components[i].Free;
Stream.ReadComponent(Self); //--ERROR here: Stream read error.
finally
Stream.Free;
end;
except
on EFOpenError do {nothing};
end;
end;
procedure TSaveComponent.SaveToBlobField1(AField: TField);
var
Stream: TStream;
begin
Stream := TClientDataSet(AField.DataSet).CreateBlobStream(AField,bmWrite);
try
Stream.WriteComponent( Self);
finally
Stream.Free;
end;
end;
Firebird 表是...
CREATE TABLE APPOBJECTS
(
FORMDM_NAME varchar(31),
OBJ_NAME varchar(40),
OBJECT blob sub_type 1,
CONSTRAINT UNQ_NAME UNIQUE (OBJ_NAME)
);
写入数据库...
with dmMain.ClientDataSet2 do
begin
if Locate('OBJ_NAME',GlobalSetting.Name,[]) then
Edit
else
Append;
FieldByName('OBJ_NAME').AsString := GlobalSetting.Name;
end;
GlobalSetting.SaveToBlobField1(dmMain.ClientDataSet2.FieldByName('OBJECT'));
dmMain.ClientDataSet2.Post;
dmMain.ClientDataSet2.ApplyUpdates(0);
(全局设置是 TSaveComponent。)
从数据库读取...
with dmMain.ClientDataSet2 do
begin
if Locate('OBJ_NAME',GlobalSetting.Name,[]) then
begin
GlobalSetting.ReadFromBlobField1(dmMain.ClientDataSet2.FieldByName('OBJECT'));
end;
end;
问题:Stream.ReadComponent(self) 行中始终出现“流读取错误”。请问这个问题怎么解决?
我可以确认保存组件是否有效。我检查了该表并查看了 GlobalSetting 中已发布的字段,我只是不确定它的格式是否正确。 (我可以显示十六进制如果需要代表)
编辑:整个解决方案与IBX组件配合使用;使用 DBExpress/Clientdataset 组件,从 blob 字段读取流总是会导致“流读取错误”。
最佳答案
正如评论中所述,您需要实现IStreamPersist
。为此,您可以使用 RTTI
来存储和恢复您的属性。我为您创建了一个示例:
首先,您需要一个可以保留所有属性及其值的类。
unit PropertyPersistU;
interface
uses
System.Classes, System.RTTI;
type
TPropertyPersist = class(TComponent, IStreamPersist)
strict private
class var RttiContext: TRttiContext;
class function GetProperty(const aObject: TObject; const aPropertyName: string): TRttiProperty; overload; static;
public
procedure LoadFromStream(Stream: TStream);
procedure SaveToStream(Stream: TStream);
procedure SaveToFile(const FileName: string);
procedure LoadFromFile(const FileName: string);
end;
implementation
uses
System.SysUtils;
class function TPropertyPersist.GetProperty(const aObject: TObject; const aPropertyName: string): TRttiProperty;
begin
Result := RttiContext.GetType(aObject.ClassType).GetProperty(aPropertyName);
end;
procedure TPropertyPersist.LoadFromFile(const FileName: string);
var
Stream: TStream;
begin
Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
LoadFromStream(Stream);
finally
Stream.Free;
end;
end;
procedure TPropertyPersist.LoadFromStream(Stream: TStream);
var
Reader: TReader;
RttiProperty: TRttiProperty;
begin
Reader := TReader.Create(Stream, $FFF);
Stream.Position := 0;
Reader.ReadListBegin;
while not Reader.EndOfList do
begin
RttiProperty := GetProperty(Self, Reader.ReadString); // Get property from property name read from stream
RttiProperty.SetValue(Self, TValue.FromVariant(Reader.ReadVariant)); // Get the property value
end;
Reader.Free;
end;
procedure TPropertyPersist.SaveToFile(const FileName: string);
var
Stream: TStream;
begin
Stream := TFileStream.Create(FileName, fmCreate);
try
SaveToStream(Stream);
finally
Stream.Free;
end;
end;
procedure TPropertyPersist.SaveToStream(Stream: TStream);
var
RttiType: TRttiType;
RttiProperty: TRttiProperty;
Writer: TWriter;
begin
RttiType := RttiContext.GetType(Self.ClassType);
Writer := TWriter.Create(Stream, $FFF);
try
Writer.WriteListBegin;
for RttiProperty in RttiType.GetProperties do
if RttiProperty.IsWritable then
if TRttiInstanceType(RttiProperty.Parent).MetaclassType.InheritsFrom(TPropertyPersist) then // Only save components on TPropertyPersist decendans
begin
Writer.WriteString(RttiProperty.Name); // Write the property name
Writer.WriteVariant(RttiProperty.GetValue(Self).AsVariant); // Write the property value
end;
Writer.WriteListEnd;
finally
Writer.Free;
end;
end;
end.
编辑如果您有旧版本的 Delphi,没有扩展 RTTI,那么您需要 TPropertyPersist
unit PropertyPersistU;
interface
uses
Classes;
type
TPropertyPersist = class(TComponent, IStreamPersist)
public
procedure LoadFromStream(Stream: TStream);
procedure SaveToStream(Stream: TStream);
procedure SaveToFile(const FileName: string);
procedure LoadFromFile(const FileName: string);
end;
implementation
uses
TypInfo, Sysutils;
{ TPropertyPersist }
procedure TPropertyPersist.LoadFromFile(const FileName: string);
var
Stream: TStream;
begin
Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
LoadFromStream(Stream);
finally
Stream.Free;
end;
end;
procedure TPropertyPersist.LoadFromStream(Stream: TStream);
var
Reader: TReader;
PropName, PropValue: string;
begin
Reader := TReader.Create(Stream, $FFF);
Stream.Position := 0;
Reader.ReadListBegin;
while not Reader.EndOfList do
begin
PropName := Reader.ReadString;
PropValue := Reader.ReadString;
SetPropValue(Self, PropName, PropValue);
end;
FreeAndNil(Reader);
end;
procedure TPropertyPersist.SaveToFile(const FileName: string);
var
Stream: TStream;
begin
Stream := TFileStream.Create(FileName, fmCreate);
try
SaveToStream(Stream);
finally
Stream.Free;
end;
end;
procedure TPropertyPersist.SaveToStream(Stream: TStream);
var
PropName, PropValue: string;
cnt: Integer;
lPropInfo: PPropInfo;
lPropCount: Integer;
lPropList: PPropList;
lPropType: PPTypeInfo;
Writer: TWriter;
begin
lPropCount := GetPropList(PTypeInfo(ClassInfo), lPropList);
Writer := TWriter.Create(Stream, $FFF);
Stream.Size := 0;
Writer.WriteListBegin;
for cnt := 0 to lPropCount - 1 do
begin
lPropInfo := lPropList^[cnt];
lPropType := lPropInfo^.PropType;
if lPropInfo^.SetProc = nil then
continue;
if lPropType^.Kind = tkMethod then
continue;
PropName := lPropInfo.Name;
PropValue := GetPropValue(Self, PropName);
Writer.WriteString(PropName);
Writer.WriteString(PropValue);
end;
Writer.WriteListEnd;
FreeAndNil(Writer);
end;
end.
然后你需要调用它。
首先创建一个小的虚拟类,上面有一些属性:
{$M+}
type
TSettings = class(TPropertyPersist)
private
FPropertyString: string;
FPropertyDate: TDateTime;
FPropertyInt: Integer;
published
property PropertyInt: Integer read FPropertyInt write FPropertyInt;
property PropertyString: string read FPropertyString write FPropertyString;
property PropertyDate: TDateTime read FPropertyDate write FPropertyDate;
end;
你需要调用它。
procedure TForm1.FormCreate(Sender: TObject);
const
StringValue = 'Dummy';
begin
with TSettings.Create(self) do
try
PropertyInt := 1;
PropertyString := StringValue;
PropertyDate := Now;
SaveToFile('Settings.dmp');
finally
Free;
end;
with TSettings.Create(self) do
try
LoadFromFile('Settings.dmp');
Assert(PropertyString = StringValue); //Test that the property is correctly read
finally
Free;
end;
end;
现在您可以将类的属性保存并加载到流中。
下一步是创建一个完整的工作示例:
新建项目,然后将 ClientDataset 添加到 MainForm 和 FromCreate
事件。
ClientDataset 的第一个 DFM 代码:
object ClientDataSet1: TClientDataSet
Aggregates = <>
FieldDefs = <>
IndexDefs = <>
Params = <>
StoreDefs = True
Left = 312
Top = 176
object ClientDataSet1FORMDM_NAME: TStringField
FieldName = 'FORMDM_NAME'
Size = 31
end
object ClientDataSet1OBJ_NAME: TStringField
FieldName = 'OBJ_NAME'
Size = 40
end
object ClientDataSet1Object: TBlobField
FieldName = 'Object'
end
end
然后是该单元的完整代码:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, DB, DBClient;
type
TForm1 = class(TForm)
ClientDataSet1: TClientDataSet;
ClientDataSet1FORMDM_NAME: TStringField;
ClientDataSet1OBJ_NAME: TStringField;
ClientDataSet1Object: TBlobField;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses
PropertyPersistU;
type
TSettings = class(TPropertyPersist)
private
FPropertyString: string;
FPropertyDate: TDateTime;
FPropertyInt: Integer;
published
property PropertyInt: Integer read FPropertyInt write FPropertyInt;
property PropertyString: string read FPropertyString write FPropertyString;
property PropertyDate: TDateTime read FPropertyDate write FPropertyDate;
end;
procedure TForm1.FormCreate(Sender: TObject);
const
StringValue = 'Dummy';
var
Stream : TMemoryStream;
Settings : TSettings;
begin
ClientDataSet1.CreateDataSet;
Stream := TMemoryStream.Create;
Settings := TSettings.Create(self);
try
Settings.PropertyInt := 1;
Settings.PropertyString := StringValue;
Settings.PropertyDate := Now;
Settings.Name := 'ObjectName';
Settings.SaveToStream(Stream);
finally
Settings.Free;
end;
Stream.Position := 0;
ClientDataSet1.Append;
ClientDataSet1FORMDM_NAME.AsString := Form1.Name;
ClientDataSet1OBJ_NAME.AsString := 'ObjectName';
ClientDataSet1Object.LoadFromStream(Stream);
ClientDataSet1.Post;
Caption := 'ClientDataSet1.RecordCount = ' + IntToStr(ClientDataSet1.RecordCount);
Stream.Free;
Stream := TMemoryStream.Create;
Settings := TSettings.Create(self);
ClientDataSet1.First;
ClientDataSet1Object.SaveToStream(Stream);
try
Settings.LoadFromStream(Stream);
Assert(Settings.PropertyString = StringValue);
finally
Settings.Free;
end;
Stream.Free;
end;
end.
就是这样。
向 TPropertyPersist
类添加一些错误处理,但我会将其留给您。
关于Delphi:将 TComponent 保存到 Clientdataset blob 字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33224848/
我想知道在添加或编辑Interbase记录时,将ClientDataset与数据感知控件一起使用是否可行。 我的意图是最初使用SQLDataset打开单个记录,但我希望可以将该记录加载到Clientd
我目前正在测试: 指向 IB 数据库的 SQLConnection。 一个 SQLDataset,其 SQLConnection 字段设置为上述字段。 将 (2) 中的 SQLDataset 作为其数
我在 Delphi 7 中有一个应用程序,它使用 clientdataset,并对其进行多项操作。 ClientDataSet 链接到 Intraweb 网格。 我在 ClientDataSet 上进
我正在尝试编写一个从 TClientDataset 继承的组件。在设计时创建组件时,我想实例化一个在我的框架中使用的公共(public)字段列表。下面的代码将在没有错误的情况下执行,并且该字段将出现在
打开一个 TClientDataset,并获取包含 3 个 TBlobfields 的 10.000 行需要 3分钟 . 打开完全相同的数据,但将 BLOB 字段转换为 Varchar,需要 1 秒
我试图在计算的 bool 字段上对 ClientDataSet 进行排序,但由于某种原因,数据未排序。我有一个名为 Highlight 的 bool 字段,其中 FieldKind 设置为 fkInt
我有一个使用 ClientDataSets 和本地文件存储的应用程序。一些信息显示在数据库网格中,我发现它被切断了——显示了字符串的前 500 个字符左右,但底层字段需要更长。所以我回到我的代码并增加
我使用此代码在运行时创建一组字段 ClientDataSet1.FieldDefs.Add('ID', ftInteger, 0); ClientDataSet1.FieldDefs.Add(
当使用带有过滤器的 ClientDataSet 的 Filter 属性时 Name NOT LIKE 'Paulo%' 表示语法不正确。 如何在过滤器中找到解决方法? 最佳答案 将过滤器语法更改为
我想反转 TClientDataSet 中索引的顺序,下面的代码看起来应该可以解决问题,但什么也没做。有没有一种好方法来反转索引的顺序? procedure TForm8.Button1Click(S
我正在使用连接到 DBGrid 的 TClientDataset 和几个聚合字段,用于计算其他几个浮点字段的总和。所有字段均已在设计时创建。 一切都按预期工作,直到 ClientDataset 的 I
我在使用 ClientDataSet.StatusFilter := [usDeleted] 时遇到一些困难。 它没有任何作用。我正在将我的 ClientDataSet 连接到 Provider。 应
我在应用程序中使用 TClientDataSet 来管理从多个 CSV 文件导入的数据负载。总共可以是一百万个或更多条目。我希望能够删除与特定 CSV 文件关联的所有数据集条目,但删除大量项目的时间非
我正在使用 Firebird 2.1、DevArt 和 Delphi 2010 的 DBExpress 驱动程序。我的一些用于 Delphi 2006 的报告停止工作并生成一条错误消息,指示“算术异常
我对 Delphi/ClientDataSets 了解不多,但我愿意研究一下。不过,在我追求它之前我有一个问题,以确定我想要实现的目标是否可行。 我想每周一次使用 PHP 脚本将我的 MYSQL 数据
我正在使用 MySQL 数据库自学 Delphi 数据库编程。我正在尝试从嵌套的 ClientDataSet 添加一条记录,其中包含主表和详细表之间的链接以及主表中的自动增量字段。我在以下位置找到了似
对于在设计时定义字段的 Delphi ClientDataSets,有没有办法在运行时更改特定字段的数据类型(更改 cds.Fields[n].DataType)? 我有一个遗留的 Delphi 7
在过去的几天中,我们看到在加载XML块时使用ClientDataSet在Delphi 2006上引发了随机访问冲突,但是在使用CodeGear 2007 rad Studio时错误消失了。 我有一种奇
编辑:似乎 DataSetProvider 没有该项目所需的功能,因此我将实现一个自定义类来将数据加载到 ClientDataSet 中。 我正在尝试从连接到我的数据库的 TMSQuery 获取数据,
我在处理 TClientDataSet 的一些代码时遇到了问题Delta,我将其归结为以下测试用例。 我有两个 ClientDataSets,cdsData 和 cdsDelta,两者都有一个 Dat
我是一名优秀的程序员,十分优秀!