RAD Studio 2010 Review #9: – Attributes, The new RTTI and DB Access

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

…where 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 🙂

29 thoughts on “RAD Studio 2010 Review #9: – Attributes, The new RTTI and DB Access

  1. I have been waiting for a LONG time for richer RTTI and attributes to do more. Add a new object oriented interface to access that information and hey, I think we have a winner.

  2. @Ossian, yes, you could do the same thing with virtual class methods — but only if all the classes you care about descend from the same base class, and that common base class is under your control. Attributes, on the other hand, let you tag any of your classes with metadata, regardless of what they descend from.

    • @Joe White: um… you’ve heard of these things called “interfaces”, right? 😉

      Attributes are a mess imho (a good idea, but the implementation results in *very* messy code and a real mixing of concerns, which is surely the exact opposite of the direction we should be heading in).

      And when the number of attributes accumulates, performance of code that works with those attributes will take a nose dive.

      Imagine your persistence attributes mixed in with scripting exposure attributes, mixed in with browsing exposures attributes mixed in with attributes added to aid in debugging and diagnostics. A not entirely unrealistic scenario – and there are probably lots more uses for “attributes” that people will rush to embrace without considering the implications.

      Not only do the declarations quickly become more about attributes than the business problem and actual code, but interrogating attributes becomes a hugely expensive business (just ask the .NET guys).

      e.g. a class with 15 member variables, each with 3 attributes… to find the persistence info you now need (as many as) 45 “InheritsFrom” calls. If you were saving 10 of those “TContact” objects using this TStorable approach, with 3 attributes per member, that’s 450 InheritsFrom() calls.

      Yeah, yeah, machines get faster every day, but software gets SLOWER every day precisely because of this sort of thing and an attitude that says “let the hardware take the strain”, and the result is that users still complain about poor performance, whilst we lap up language “enhancements” that don’t actually serve us all that well and yet STILL can’t deliver software on time or bug free.

      I seriously think we software development practitioners should concentrate on learning and applying our craft well, instead of leaning on our tools and expecting them to compensate for our own laziness and other shortcomings.

      • Unless you have extremely long inheritance chains, the combined performance hit of InheritsFrom is most likely a lot less severe than the query execution, which you can’t escape even if you implement the persistence logic using traditional means. This is not likely to be something that significantly slows down your program execution. However, you would be right if a similar procedure was used for performance sensitive in-memory operations, such as searching for a particular record in a very large list of TStorable objects. In such case you could however cash the TRTTIField and TCustomAttribute objects you are interested in, so that you would only have to look them up once for each class.

  3. This looks very interesting. Do the old RTTI functions and data structures still function? We have *a lot* of code that use the old RTTI data structures and build extensively on top of that.

  4. @Ossian -> RTTI can be very handy for creating more flexible frameworks – It allows you to bind the description of the framework directly in, instead of having to write extra code helpers.

    There are many ways you can benefit from this. The way that Delphi loads forms and ties them to code is just a basic example. Much more sophisticated scripting, logging, error reporting and other scenarios are possible with a richer RTTI than we have seen before this.

  5. Interesting, but not exactly a viable “real world” example.

    Building an SQL string where the values are all string literals? That’s potentially going to fail on some (all?) SQL systems (inserting AGE = ’10’ where AGE is an INTEGER column!). Not to mention the potential for SQL injection. Use of parameterised SQL would of course require discovery of type information for each field too, adding yet more overhead to the process.

    It’s also very disappointing to see that RTTIContext isn’t exposed as an interface, necessitating yet more try/finally housekeeping that could easily have been avoided.

    • FYI – This shouldn’t be seen as “nit-picking” imho …. a persistence framework using “old fashioned” RTTI could easily adapt to the type of any published members and use parameterised SQL, which is safer and vastly more efficient for operations involving static SQL and variations in parameter values only.

      So, when presenting these new attributes as an alternative it is important to present them as a viable alternative.

      i.e. if they are to be anything other than a language gimmick, playing yet another round of “me too” and Keeping Up With The Jones.NET, then we need to see a compelling example (not necessarily feature complete, but at least a convincing “this is how you could…” type discussion) of actual use, not abstract/impractical examples that quite obviously “do not fly” outside of the language laboratory.

      • In tiopf, one of the persistance mechanisms is auto-mapping.

        Mapping is done like this,
        1. // Class, Table, Property, Column, Special Info
        2. gTIOPFManager.ClassDBMappingMgr.RegisterMapping(TEmployee, ‘Employee’, ‘OID’, ‘EmpNo’, [pktDB] );
        3. gTIOPFManager.ClassDBMappingMgr.RegisterMapping(TEmployee, ‘Employee’, ‘LastName’, ‘LastName’ );

        7. gTIOPFManager.ClassDBMappingMgr.RegisterMapping(TEmployee, ‘Employee’, ‘Salary’, ‘Salary’ ) ;
        8. gTIOPFManager.ClassDBMappingMgr.RegisterCollection(TEmployees, TEmployee);

        I suspect that I could rewrite that to use attibutes pretty quickly.

    • FWIW, TRttiContext is a record, and is actually a wrapper around an interface.

      It initializes itself on first use and disposes itself when it goes out of scope. The TRttiContext.Create and TRttiContext.Free methods are available for familiarity, but their use is not required.

      • Aha! Thanks for pointing this out. But we need to take it out from here (nobody will look at these comments anymore) and make a new post. Please stay tunned and correct me if necessary.

  6. @Jolyon,

    The sql code shown is (I hope) demo only, not what anyone would use in practice. Certainly no one in the right mind would do that in real code.

    However using attributes for persistance meta data is a fairly common use. Look at Linq for example.

    Existing rtti is insufficent. The only data you can reliably extract is the field type. You cannot rely on the field name being the same as the property name, nor on the table name being the same as the class name. If you are generating a database from the object, you also need to know the field type and size.

    There are obviously solutions, tiOpf requires explicit mapping code, and hibernate (I think) uses xml configuration files. However using attributes is certainly easier.

    • The sql code shown is (I hope) demo only, not what anyone would use in practice. Certainly no one in the right mind would do that in real code.

      Sure. My focus is to write clear code (to explain clearly the phenomenon) and to move as fast as I can to another topic. There is a plenty of material to write about.

  7. (*Just random thoughts*)
    At last we have EJB 3.x in Delphi. Should it be called EDB?
    Now, what’s about EDB server with server-side persistence?
    We would definetly need AOP or at least setter and getter interceptors…
    And much more… Really not the easiest path to go…
    Would we need auto GC (memoty management in general) for all this?
    Is DHibernate on the horizon?

  8. It would be nice if other useful examples of the use of attributes are given except of the ubiquitous DB persistence example.

    Anyone who has read Scott Amblers papers on designing persistence layers knows that the storage specifications should be strictly separated from the business classes, and that using attributes is therefor definitely the wrong technique – something which was clearly understood by the people who designed tiOPF

    • Nothing prevents you from adding a business layer on top of the classes that descend from TStorage in the example. Regardless of which technique you use for persistence, you need to both implement the storage specific persistence mechanism, and to store the application specific mapping somewhere and somehow. What the example demonstrates is just how you could do it with relatively little effort in Delphi 2010 source code.

  9. Pingback: More Attributes in Delphi 2010 | 资料收集站

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s