New Attributes in Delphi

I comprehend what you are thinking… ‘ascribes?? who needs them?!’.
I must be straightforward here… it took me some time to get used to credits as well. At the point when they were first presented in 2009, I resembled… ‘meh, I can complete that without ascribes… ‘. That feeling stayed with me for the following 4 years until that one decisive day… music taking you to the past 😉
On that day, I truly required an achievement. I was one of the early adopters of microservices improvement utilizing Delphi and I needed to escape Datasnap. Try not to misunderstand me. Datasnap is acceptable at what it does. I simply required something all the more light weight. Along these lines, I was playing with a few systems, for example, center web representative, mORMot, Delphi MVC (MARS and RAD Server were not created at this point). At long last, chosen to go Delphi MVC course.
When I began composing REST APIs in DMVC, I understood rapidly that I was rehashing a great deal of code again and again in light of the fact that most tables required similar CRUD offices having pretty much a similar SQL notwithstanding the field names and table names.
As any self regarding sluggish developer would have thought, I thought… ‘For what reason am I composing this again and again?! Borrrrring!’. So I chose to concoct an arrangement to robotize SQL age… however the inquiry was… how?? In the event that just there was some approach to conveniently tie up the Delphi class fields to the information base fields, I could most likely compose a strategy to do what I required. Well… what might have helped me?
And afterward it hit me — ATTRIBUTES! Or on the other hand as I like to call them… ATTRI-BEAUTES 😉 !!
Traits gave me the ideal method to attach data set data with the class fields. Here’s the ticket…
The arrangement was to interface each field of the item with its comparing field name in the table at configuration time. Properties gave me the ideal method of doing it. So I felt free to make a custom property class as follows
Data.DB;
TDatabaseFieldAttribute = class(TCustomAttribute)
private
FFieldName: string;
FFieldType: TFieldType;
public
constructor Create(FName:string; FType: TFieldType);
property FieldName: string read FFieldName write FFieldName;
property FieldType: TFieldType read FFieldType write FFieldType;
end;
implementation
{ TDatabaseFieldAttribute }
constructor TDatabaseFieldAttribute.Create(FName: string; FType: TFieldType);
begin
FFieldName := FName;
FFieldType := FType;
end;
The FieldName property will store the real name of the field in the information base table while the FieldType will store the sort of field it is. The FieldType property will prove to be useful when we are making the SQL, you will see.
Presently, that I had the above class made, I proceeded onward to characterizing the table through a Delphi class. This is an example of the business object class would take the traits:
TUser = class
private
FUID: Integer;
FUsername: string;
FFullName: string;
FEmail: string;
published
[TDatabaseFieldAttribute(‘UID’, ftInteger)]
property UID: Integer read FUID;
[TDatabaseFieldAttribute(‘User_Name’, ftString)]
property Username: string read FUsername write FUsername;
[TDatabaseFieldAttribute(‘Full_Name’, ftString)]
property FullName: string read FFullName write FFullName;
[TDatabaseFieldAttribute(‘Email_Address’, ftString)]
property Email: string read FEmail write FEmail;
end;
The above class is announced as copy of the table is in the information base. We ’embellished’ the properties with ascribes that tie them up with the table field names and we likewise put in the information kind of the field type. As referenced before, you will see that this will prove to be useful when we are creating the SQL.
Next, we can compose a strategy that acknowledges an example of the above class and produces the Insert SQL string. Yet, how?
RTTI to the salvage!
Because of RTTI, we can get to ‘improved’ custom ascribes for example of the class. That data gives us all that we have to make the SQL string. Here’s a rough yet extremely utilitarian technique for doing exactly that.
function GetInsertString(oInstance: TBasicFieldsClass; TableName: string): string;
var
ctx : TRttiContext;
t : TRttiType;
p : TRttiProperty;
a : TCustomAttribute;
Fields : string;
Values : string;
begin
ctx := TRttiContext.Create;
try
t := ctx.GetType(oInstance.ClassType);
for p in t.GetProperties do
for a in p.GetAttributes do
if a is TDatabaseFieldAttribute then
begin
Fields := Fields + TDatabaseFieldAttribute(a).FieldName + ‘,’;
case TDatabaseFieldAttribute(a).FieldType of
ftString:
Values := Values + QuotedStr(p.GetValue(oInstance).ToString) + ‘,’;
ftDateTime:
Values := Values + QuotedStr(FormatDateTime(‘yyyy-MM-dd hh:mm:ss’, StrToDateTime(p.GetValue(oInstance).ToString))) + ‘,’;
else
Values := Values + p.GetValue(oInstance).ToString + ‘,’;
end;
end;
finally
Fields := Fields.Remove(Fields.Length-1, 1);
Values := Values.Remove(Values.Length-1, 1);
ctx.Free;
end;
Result := ‘ insert into ‘ + TableName + ‘(‘ + Fields + ‘)’ + ‘ values (‘+Values+’)’;
end;
The above code sneaks up all of a sudden, isn’t that so? In any case, in the event that you get it, it’s truly basic. The above technique takes in 2 params. One the example of the item and the other the table name to which this occasion maps.
I won’t go into the low down of how RTTIContext functions as that is out of the extent of this blog. Nonetheless, accepting that you know how it functions, I will clarify the key explanations (the ones that utilization our custom credits) to effectively express the idea.
RTTIContext gives us admittance to properties of a class once the sort is set. We did that with ctx.GetType(oInstance.ClassType) proclamation. Utilizing this we can examine all the qualities of the class. We do that utilizing p.GetAttributes strategy. Presently, in light of the fact that we are just inspired by our custom trait (TDatabaseFieldAttribute), we possibly need to accomplish something on the off chance that we see that characteristic connected to any property that can use to get the incentive from.
Specifying through all ascribes (for a property of the class), we simply need to utilize the one that matches and once we get it, we can extricate the data we had set at configuration time by projecting the characteristic to our custom trait to get the quality property estimation utilizing TDatabaseFieldAttribute(a).FieldName to get the field name planned to the property. Next we utilize the TDatabaseFieldAttribute(a).FieldType quality property to see the sort of field it is. This causes us how to organize the line of the SQL (see, I disclosed to you it will prove to be useful!) for the supplement proclamation. Why? Since various information bases may require various types of string designing. The above code is a fairly straightforward model where string field types are cited, date field types are organized in yyyy-mm-dd hh:mm:ss and afterward cited while number field types are basically (with no statements) into the Insert SQL string.
At long last, we basically connect the factors into the SQL string that we need to produce and presto! we have a progressively created SQL dependent on the sort of class we go to it.
You can take this thought further and make your own Select, Delete, Update SQLs with Where conditions.
Note: You can likewise include the table name as a property to the whole class, in the event that you would prefer not to pass it as a param incentive to the SQL generator strategy. Simply use RTTIContext to peruse the quality of the class itself. 🙂
This is only one approach to utilize credits. Plainly, there are huge amounts of other use cases out there.