Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Datenklasse und Rtti


ProggyPeter - Mi 18.04.12 09:22
Titel: Datenklasse und Rtti
Hallo,
mit Rtti habe ich mich erst wenig beschäftigt.
Deshalb wäre ich dankbar, wenn ein paar Tips/Codezeilen zur Lösung beitragen könnten.

Ich möchte eine Datenklasse definieren.
Aus dieser Klasse möchte ich zur Laufzeit Variablenname und Variablentyp auslesen.
Die Datenklasse hat die gleiche Struktur und Feldnamen wie ein zugehöriger Datenbankrecord.

Beispiel:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
type
  TDataClass = class
   KID: Integer;
   Name: string;
   Vorname: string;
   ADRID: integer;
 end;


Zur Laufzeit möchte ich diese Datenklasse aus einer Query füllen.


Delphi-Quelltext
1:
GetData('SELECT * FROM TESTDAT WHERE KID=123' , DataClass);                    



Für einen Tip dankbar.

Mit Gruß
Peter


Moderiert von user profile iconNarses: Topic aus Sonstiges (Delphi) verschoben am Mi 18.04.2012 um 10:02


bummi - Mi 18.04.12 11:35


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
unit Unit2;
// DemoRumpf für RTTI - Setter, Thomas Wassermann 2012
interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls,TypINfo,rtti;

type
  TDataClass = class
   FKID: Integer;
   FName: string;
   FVorname: string;
   FADRID: integer;
   published
   Property KID: Integer read FKID write FKID;
   Property Name: string read FName write FName;
   Property Vorname: string read FVorname write FVorname;
   Property ADRID: Integer read FADRID write FADRID;
 end;
  TForm2 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
   d:TDataClass;
    { Public-Deklarationen }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}



Procedure SetValue(obj:TObject;aName:String;AValue:Variant);
var
   PropInfos: TPropList;
   PropTypeName : string;
   pi : PPropInfo;
   i:Integer;
begin
      for i := 0 to GetPropList(obj.ClassInfo, tkProperties, @PropInfos) - 1 do
      begin
         if PropInfos[i].Name = aName then
            begin
               case PropType(obj , PropInfos[i].Name) of
                 tkInteger:
                   SetOrdProp(obj , PropInfos[i].Name, AValue);
                 tkFloat:
                   SetFloatProp(obj , PropInfos[i].Name, AValue);
                 tkString, tkLString,tkUString:
                   SetStrProp(obj , PropInfos[i].Name, AValue);
               end;
            end;

      end;
end;

procedure TForm2.Button1Click(Sender: TObject);
var
 d:TDataClass;
begin
  d:=TDataClass.Create;
  SetValue(d,'Name','Test');
  SetValue(d,'KID',1234);
  Showmessage(d.Name + #13#10 + IntToStr(d.KID) );
  d.Free;
end;

end.


ProggyPeter - Mi 18.04.12 14:02

Erst mal Danke für den Tip.
Mit propertys wollte ich eigentlich nicht arbeiten.
Ich habe zwischenzeitlich weiter probiert und habe diese Lösung
angewendet.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
var
  Obj: TObject;
  context: TRttiContext;
  typinfo: TRttiType;
  field: TRttiField;
  s : string;
begin
  lst1.Items.Clear;
  context := TRttiContext.Create;
  typinfo := context.GetType(Data.ClassInfo);
  for field in typinfo.GetFields do
  begin
    typinfo := field.FieldType;
    s := '';
    if typinfo <> nil then
      s := typinfo.Name;
           lst1.Items.Add(field.Name + '  ' + s );
   end;
  context.Free
end;



Mit
if UpperCase(field.Name) = 'BEZEICHNUNG' then
l := field.GetValue(Data).AsString;

kann ich den Inhalt der Variablen auslesen. Das funktioniert auch.
Was ich noch suche ist die Gegenrichtung.
Also

if UpperCase(field.Name) = 'BEZEICHNUNG' then
field.SetValue(Data , Value);

Hier habe ich noch ein Problem mit dem Handling von TValue;

Gruß
Peter


ProggyPeter - Do 19.04.12 09:25

So jetzt habe ich den zweiten Teil auch gelöst.
Für alle, die es interessiert nachfolgend die Realisierung.

Ziel war es, eine komplette Datenklasse aus einer Firebird-Datenbank zu füllen.
Vielen Dank nochmals für die freundliche Unterstützung.

MfG
Peter

Die Anwendung


Delphi-Quelltext
1:
if DataSQL.Select('KOMBINIERT', Data, ['KID=',9003]) then ...                    



Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
procedure TIBDacSQL.FieldstoInstance(Fields: TFields; Instance: TObject);
var
  context: TRttiContext;
  typinfo: TRttiType;
  field: TRttiField;
  n : Integer;
  s : string;
  va: TValue;
begin
  n := 0;
  context := TRttiContext.Create;
  typinfo := context.GetType(Instance.ClassInfo);

  for field in typinfo.GetFields do
  begin
    Va := Field.GetValue(Instance);
    case va.Kind of
      tkInteger: field.SetValue(Instance,Fields[n].AsInteger );
      tkInt64  : field.SetValue(Instance,Fields[n].AsLargeInt);
      tkFloat  : field.SetValue(Instance,Fields[n].AsFloat);
      tkChar   : field.SetValue(Instance,Fields[n].AsString);
      tkWChar  : field.SetValue(Instance,AnsiChar(Fields[n].AsBytes));
      tkUString: field.SetValue(Instance,Fields[n].AsString);
      tkString,
      tkLString,
      tkWString : field.SetValue(Instance,AnsiString(Fields[n].AsString));
    end;
    inc(n);
  end;
end;