In this week we’ll talking about some database / communication features (including Firebird support, Intraweb etc.) Mind you, is a prerelease beta. Anything can change till release. All our gratitude to Embarcadero for giving us the permission to talk about this.
But first, let us try to calm the stir provoked by a post of Embarcadero’s Malcolm Groves in which our Senior Director talks a little bit about attributes. We’ll try here to give an extended example (the classical one about using attributes to implement class persistence) and explain why the attributes are useful and how they will help you in your work…
You can see the attributes like labels with parseable information attached to a programming entity. Comments which add data, which describe better a programming entity. Let’s say that we have the property ‘Name: string’. The class works very well as is. But we can add more and more info (more and more attributes) to this simple property: is deprecated, is (the) default (property) etc.). Starting with Delphi 2010 you can build and use your own attributes. How? Let’s take an example:
Let’s say that we have the following class hierarchy:
TKid = class(TContact); TEmployee = class(TContact); TLawyer = class(TEmployee); TDriver = class(TEmployee); TSoldier = class(TContact); TClerk = class(TContact);
…and of course each class with it’s own fields according with the situation. (Please don’t flame me because the hierarchy perhaps isn’t in accordance with the reality. Let’s move on).
Well, it would be the best to have a general mechanism to store and retrieve data from those classes without having to wire in each class the code for this. It would be the best if with a minimal effort we can ‘glue’ that general mechanism in each class. Mind you, in each class we’ll implement only the glue and not the mechanism. This will be general. Something like:
[Table('CONTACTS')] TContact = class(TStorable) private [FieldName('NAME')] FName: string; [FieldName('AGE')] FAge: integer; procedure SetAge(const Value: integer); procedure SetName(const Value: string); published public property Name: string read FName write SetName; property Age: integer read FAge write SetAge; end;
See the things in the right braces? These ‘things’ are attributes for the entity which follows. We had this already in Delphi syntax albeit in a reduced form. Think about ‘packed record’, ‘default property’ aso.
…and of course we must define the attributes. See the attributes like a regular class which descend from
TCustomAttribute. You must put any parameters in the constructor. In our concrete case our attributes are defined like:
Table = class(TCustomAttribute) //here is the important part private FName: string; procedure SetName(const Value: string); published public constructor Create(ATableName: string); //the attribute parameter(s) are passed in the constructor property Name: string read FName write SetName; //classical setters and getters end; FieldName = class(TCustomAttribute) private FField: string; procedure SetField(const Value: string); published public constructor Create(AField: string); property Field: string read FField write SetField; end;
Well, as I said, the attributes doesn’t have any effect by themselves. You can see them also like a special kind of comments parseable at runtime. So, in order to use them we must write a mechanism to ‘parse’ them. The main advantage is that this mechanism is rather simple and can be written once, usually in the common ancestor of all the class hierarchy:
TStorable = class public procedure Insert; end;
Insert is implemented like…
procedure TStorable.Insert; var LContext: TRTTIContext; LType: TRTTIType; crtField: TRTTIField; I, k: TCustomAttribute; LQuery, LFields, LValues: string; begin LContext:=TRttiContext.Create; //we need to use the new RTTI engine try LType:=LContext.GetType(ClassType); //get the RTTI representation of our class LQuery:=''; LFields:=''; LValues:=''; //init the SQL query parts for I in LType.GetAttributes do //scan the class attributes if i is Table then //got it! begin LQuery := 'INSERT INTO '+Table(i).Name+' '; //add the actual value for crtField in LType.GetFields do //for each class field look for attribs for k in crtField.GetAttributes do if k is FieldName then //aha! begin LFields:=LFields+FieldName(k).Field+','; //the value of the attribute LValues:=LValues+''''+crtField.GetValue(Self).ToString+''','; //the value of the class field end; //format a little... LFields:=Copy(LFields, 1, Length(LFields)-1); //cut the last , LValues:=Copy(LValues, 1, Length(LValues)-1); //cut the last , //assemble it... LQuery:=LQuery+' ('+LFields+') VALUES ('+LValues+');'; //... and send it to the SQL backend // ShowMessage(LQuery); //if we want to debug... FInternalQuery.SQL.Text:=LQuery; //we have an internal DBX Query configured accordingly FInternalQuery.ExecSQL; end; finally LContext.Free; //housekeeping end; end;
And a simple usage example:
procedure TForm4.ToolButton1Click(Sender: TObject); var oContact: TContact; //no need to be TStorable begin oContact:=TContact.Create; oContact.Name:='wings-of-wind.com'; oContact.Age:=15; oContact.Insert; //'sees' the actual attributes of TContact and acts accordingly posting the two properties above in the database oContact.Free; end;
As you see, we wrote the code only once and this adapts dynamically to each class guided by the class’s attributes.
Of course, for the ones who are familiar with .NET (or Java) programming the above things should be well known. The aim of this post was to clear up the notion of attributes for the ones from us which are more ‘Delphi oriented’ ;-). Also, it was a good occasion to show a little the new RTTI engine which has an OO approach, more clearer and intuitive compared with the old RTTI.
Also, guys, don’t be very nitpickers. 🙂 The code, even if was tested, was written only as an example. But if you want to ‘enhance’ it feel free to do so.
Also, as always, comments and questions are very welcomed. How do you see this new addition to Delphi 2010?
PS: Special thanks for this post to Robert Love, Henrik Hellstrom, Eric Thorniley, Rudy Velthuis, Robert Dawson, Joanna Carter and all the others which gave useful insights in .public.non-technical newsgroup. …and of course to Barry Kelly which implemented them 🙂