delphistreamdelphi-7firebird2.1tcomponent

Delphi: Save TComponent to Clientdataset blob field


I have a TComponent class derivative like below, trying to save to a clientdataset blob field: (Copied from internet, due credits)

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 table is ...

CREATE TABLE APPOBJECTS
(
  FORMDM_NAME varchar(31),
  OBJ_NAME varchar(40),
  OBJECT blob sub_type 1,
  CONSTRAINT UNQ_NAME UNIQUE (OBJ_NAME)
);

Writing to db ...

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);

(Globalsetting is TSaveComponent.)

Reading from db ...

with dmMain.ClientDataSet2 do
begin
  if Locate('OBJ_NAME',GlobalSetting.Name,[]) then
  begin
    GlobalSetting.ReadFromBlobField1(dmMain.ClientDataSet2.FieldByName('OBJECT'));
  end;
end;

PROBLEM: "Stream read error" in Stream.ReadComponent(self) line, always. How to solve this, please?

I can confirm saving the component works. I inspected the table and see the published fields in GlobalSetting, I'm just not sure if it is the correct format. (I can show the hex representation if needed)

EDIT: The whole solution works with IBX components; With DBExpress/Clientdataset components, reading the stream from the blob field always results in 'Stream read error.'


Solution

  • The Firebird table DDL should have been defined as follows (note sub_type 0, not 1 as originally defined):

    CREATE TABLE APPOBJECTS
    (
      FORMDM_NAME varchar(31),
      OBJ_NAME varchar(40),
      OBJECT blob sub_type 0,
      CONSTRAINT UNQ_NAME UNIQUE (OBJ_NAME)
    );
    

    What a .... been ignoring it all the while.

    Reference: http://www.firebirdfaq.org/faq165/