Attributes in Delphi are only classes, when annotating a language construction with an attribute, what is done is to instantiate a class and link it to the construction for use by RTTI. Thus, you can avoid the interface (which will in fact be for you only headache) by means of an ancestor, as follows:
TFieldAttribute = class(TCustomAttribute)
private
FName: string;
FisPk: Boolean;
public
constructor Create(const aName: string; aIsPk: Boolean=False);
property Name:string read FName write FName;
property IsPk: Boolean read FIsPk write FIsPk;
end;
TFieldStringAttribute = class(TFieldAttribute)
end;
TFieldIntegerAttribute = class(TFieldAttribute)
end;
And so for all attributes, however, I don’t understand why you need specific attributes by type, since Rtti already gives you the typing of each Property. If I were you I would have only the ancestral attribute (TFieldAttribute
) to identify each of the mapped properties and use the type of that property itself to map to your persistence medium.
Now, if you have cases where there are different types between the Delphi property and the column in the database, then what you can do is create one more parameter in the attribute to say that. For example:
TFieldAttribute = class(TCustomAttribute)
private
FName: string;
FColumnType: string
FIsPk: Boolean;
public
constructor Create(const aName: string; aIsPk: Boolean=False); overload;
constructor Create(const aName, aColumnType: string; aIsPk: Boolean=False); overload;
property Name:string read FName write FName;
property ColumnType: string read FColumnType write FColumnType;
property IsPk: Boolean read FIsPk write FIsPk;
end;
In particular, I would have a proper attribute for PK. That would be:
TFieldAttribute = class(TCustomAttribute)
private
FName: string;
FColumnType: string
public
constructor Create(const aName: string); overload;
constructor Create(const aName, aColumnType: string); overload;
property Name:string read FName write FName;
property ColumnType: string read FColumnType write FColumnType;
end;
TPrimaryKeyAttribute = class(TFieldAttribute)
end;
There in your code what you need is to test the type of attribute using the operator is
. In the case of a person-type class (typical example) would be:
TPessoa = class
private
[PrimaryKey('Id')]
FId: Long;
[Field('Nome')]
FNome: string;
[Field('Dt_Nasc', 'VARCHAR')]
FNascimento: TDateTime;
public
property Id: Long read FId;
property Nome: string read FNome write FNome;
property Nascimento: TDateTime read FNascimento write FNascimento;
end;
I imagine this will suit you!
I don’t understand what your doubt is, it seems that you are on the right track. How about asking the question the code you have done so far to better understand?
– Maniero
Yesterday after researching a lot, I found this link http://stackoverflow.com/questions/5353656/delphi-inherit-from-a-class-and-an-interface-adapter-pattern a very similar question, and the answer was that to be able to do this (inherit an interface and a class), the 3 Queryinterface, _Addref and _Release methods of Tinterfacedobject should be implemented in the class(s) daughter(s). But I don’t know if this is right.
– Will Pires
Yes, that is correct. Every class that implements an interface needs to implement these three methods in order to be able to support the reference count memory management system and also so that the interface typing can be tested at certain times. In general, when implementing an Interface, the class is used as an ancestor
TInterfacedObject
, that already have these methods implemented, but you can also implement if necessary. Just be careful,TCustomAttribute
serves as an ancestor for creating new annotation attributes, not for the use you want to give.– AlexSC
If the idea is to use the descending classes of
TCustomAttribute
to annotate the properties of your persistent class, so I recommend that you follow the compiler convention and call, for example,TFieldStringAttribute
. Regarding your specific problem, it wasn’t clear to me because you need the interface. I could explain it better?– AlexSC