01-10-2016, Saat: 11:56
Reflection kütüphaneleri yazılım dünyasında pek çok framework ve aracın alt yapısında aktif olarak kullanılıyor.
Gerçek hayatta en bilindik kullanım alanlarına gelince;
ORM(Object relation mapping) kütüphaneleri, IOC(inversion of control) Spring4D,Spring,Unity, ve Unit Test(DUnit,JUnit,NUnit) gibi araçları diyebiliriz.
Delphi de reflection kütüphanesi RTTI olup System.Rtti.pas uniti içersindedir.Çalışma zamanında(Run-Time) tipler hakkında bilgi edinip üzerinde işlem yapmak mümkündür.
Peki biz "çok basit olarak", Attribute bazlı kendi ORM'mizi geliştirmek istersek;
PERSON tablosu(SQL SERVER):
TDbField Attibute.
TPerson sınıfı.
Map işlemi yapan method.
Form.dfm
Kullanım
Tamamı.
En basit anlamıyla delphi de RTTI kullanarak ORM'yi bu şekilde geliştirmek mümkün.
Bunun dışında kodda bilinçli olarak bırakılan eksikler var.Bu eksikleri de code review yapabiliriz.
Gerçek hayatta en bilindik kullanım alanlarına gelince;
ORM(Object relation mapping) kütüphaneleri, IOC(inversion of control) Spring4D,Spring,Unity, ve Unit Test(DUnit,JUnit,NUnit) gibi araçları diyebiliriz.
Delphi de reflection kütüphanesi RTTI olup System.Rtti.pas uniti içersindedir.Çalışma zamanında(Run-Time) tipler hakkında bilgi edinip üzerinde işlem yapmak mümkündür.
Peki biz "çok basit olarak", Attribute bazlı kendi ORM'mizi geliştirmek istersek;
PERSON tablosu(SQL SERVER):
CREATE TABLE [dbo].[PERSON]( [ID] [int] IDENTITY(1,1) NOT NULL, [FIRST_NAME] [varchar](20) NULL, [LAST_NAME] [varchar](20) NULL, [AGE] [tinyint] NULL, CONSTRAINT [PK_PERSON] PRIMARY KEY CLUSTERED ( [ID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY]
TDbField Attibute.
TDbField = class(TCustomAttribute) Private FName : string; public property Name: string read FName write FName; constructor Create(const AName: String); end; constructor TDbField.Create(const AName: String); begin FName := AName; end;
TPerson sınıfı.
TPerson = class
private
FId : Integer;
FFirstName : string;
FLastName : string;
FAge : Byte;
public
[TDbField('FIRST_NAME')]
property FirstName: string read FFirstName write FFirstName;
[TDbField('LAST_NAME')]
property LastName: string read FLastName write FLastName;
[TDbField('AGE')]
property Age: Byte read FAge write FAge;
end;
Map işlemi yapan method.
procedure Map(ADataset: TDataSet; AObject: TObject); var context: TRttiContext; dbFieldName: string; value: TValue; Attribute : TCustomAttribute; Attributes:TArray<TCustomAttribute>; rttiProperty : TRttiProperty; rttiProperties : TArray<TRttiProperty>; begin context := TRttiContext.Create; rttiProperties := context.GetType(AObject.ClassType).GetProperties; for rttiProperty in rttiProperties do begin Attributes := rttiProperty.GetAttributes; for Attribute in Attributes do begin if not (Attribute is TDbField) then continue; dbFieldName := (Attribute as TDbField).Name; value := TValue.From(ADataset.FieldByName(dbFieldName).Value); rttiProperty.SetValue(AObject, value); end; end; context.Free; end;
Form.dfm
object Form2: TForm2 Left = 0 Top = 0 Caption = 'Form2' ClientHeight = 203 ClientWidth = 455 Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'Tahoma' Font.Style = [] OldCreateOrder = False OnCreate = FormCreate PixelsPerInch = 96 TextHeight = 13 object ADOConnection1: TADOConnection Connected = True ConnectionString = 'Provider=SQLOLEDB.1;Password=123;Persist Security Info=True;User' + ' ID=sa;Initial Catalog=TEST;Data Source=.\SQLExpress' LoginPrompt = False Provider = 'SQLOLEDB.1' Left = 148 Top = 64 end object ADOQuery1: TADOQuery Active = True Connection = ADOConnection1 CursorType = ctStatic Parameters = <> SQL.Strings = ( 'SELECT ID,FIRST_NAME,LAST_NAME,AGE FROM PERSON WHERE ID=1') Left = 56 Top = 64 end end
Kullanım
procedure TForm2.FormCreate(Sender: TObject); var APerson : TPerson; begin APerson := TPerson.Create; Map(ADOQuery1,APerson); ShowMessage(APerson.FirstName); ShowMessage(APerson.LastName); ShowMessage(IntToStr(APerson.Age)); APerson.Free; end;
Tamamı.
unit UnitORMExample;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs,System.Rtti,Data.DB, Data.Win.ADODB;
type
TForm2 = class(TForm)
ADOConnection1: TADOConnection;
ADOQuery1: TADOQuery;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
TDbField = class(TCustomAttribute)
Private
FName : string;
public
property Name: string read FName write FName;
constructor Create(const AName: String);
end;
TPerson = class
private
FId : Integer;
FFirstName : string;
FLastName : string;
FAge : Byte;
public
[TDbField('FIRST_NAME')]
property FirstName: string read FFirstName write FFirstName;
[TDbField('LAST_NAME')]
property LastName: string read FLastName write FLastName;
[TDbField('AGE')]
property Age: Byte read FAge write FAge;
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
{ TDbField }
constructor TDbField.Create(const AName: String);
begin
FName := AName;
end;
procedure Map(ADataset: TDataSet; AObject: TObject);
var
context: TRttiContext;
dbFieldName: string;
value: TValue;
Attribute : TCustomAttribute;
Attributes:TArray<TCustomAttribute>;
rttiProperty : TRttiProperty;
rttiProperties : TArray<TRttiProperty>;
begin
context := TRttiContext.Create;
rttiProperties := context.GetType(AObject.ClassType).GetProperties;
for rttiProperty in rttiProperties do
begin
Attributes := rttiProperty.GetAttributes;
for Attribute in Attributes do
begin
if not (Attribute is TDbField) then continue;
dbFieldName := (Attribute as TDbField).Name;
value := TValue.From(ADataset.FieldByName(dbFieldName).Value);
rttiProperty.SetValue(AObject, value);
end;
end;
context.Free;
end;
procedure TForm2.FormCreate(Sender: TObject);
var
APerson : TPerson;
begin
APerson := TPerson.Create;
Map(ADOQuery1,APerson);
ShowMessage(APerson.FirstName);
ShowMessage(APerson.LastName);
ShowMessage(IntToStr(APerson.Age));
APerson.Free;
end;
end.
En basit anlamıyla delphi de RTTI kullanarak ORM'yi bu şekilde geliştirmek mümkün.
Bunun dışında kodda bilinçli olarak bırakılan eksikler var.Bu eksikleri de code review yapabiliriz.
Bu yüzden kendime küçük bir tool yazdım. Benim yerime bağlantısını yaptığım db den seçtiğim tablonun kodlarını delphi kodu olarak ayarlayıp veriyor ben sadece parametre karşılıklarını dolduruyorum.. Şimdi buna paylaşmış olduğunuz ORM yi de ekleyip kanynak kodlarını buradan paylaşacağım belki lazım olan birileri olur..