Tüm Platformlar için Hızlı Uygulama Geliştirme --->    Kitabımız...      Delphi

Konuyu Paylaş : facebook gplus twitter

Konuyu Oyla:
  • Derecelendirme: 5/5 - 1 oy
  • 1
  • 2
  • 3
  • 4
  • 5
Attribute Bazlı ORM
#1
Linkleri Görebilmeniz İçin Giriş yap veya Üye Ol 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; 
Linkleri Görebilmeniz İçin Giriş yap veya Üye Ol kütüphaneleri, Linkleri Görebilmeniz İçin Giriş yap veya Üye Ol Linkleri Görebilmeniz İçin Giriş yap veya Üye Ol,Linkleri Görebilmeniz İçin Giriş yap veya Üye Ol,Linkleri Görebilmeniz İçin Giriş yap veya Üye Ol, ve Linkleri Görebilmeniz İçin Giriş yap veya Üye Ol(Linkleri Görebilmeniz İçin Giriş yap veya Üye Ol,Linkleri Görebilmeniz İçin Giriş yap veya Üye Ol,Linkleri Görebilmeniz İçin Giriş yap veya Üye Ol) gibi araçları diyebiliriz.

Delphi de reflection kütüphanesi Linkleri Görebilmeniz İçin Giriş yap veya Üye Ololup System.Rtti.pas uniti içersindedirLinkleri Görebilmeniz İçin Giriş yap veya Üye Ol.


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 Linkleri Görebilmeniz İçin Giriş yap veya Üye Ol.
 
  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.
One of the major software engineering challanges is managing change.
Cevapla
#2
Merhaba,
"Yeni Nesil Delphi Teknolojileri"  sloganı ile yola çıkan; Delphi Can'a yakışan bir bilgi paylaşımı olmuş.
Anlatım içerisinde yabancı terimlere referans vermeniz ve güncelliğini sürekli koruyan bir uygulama (projelerde sık kullanılan Personel yapısı/modülü) ile örneklendirmeniz konuya daha çok açıklık getirmiş. 
Bilgi ve deneyimlerinizin bu doğrultuda (Yetişen Yeni Nesil Türk Gençliği) sürekli olmasını dileyerek, teşekkür ederim.
While true do; Hayat döngüsü, kısır değildir! Yapılan bir yanlış, o döngünün dışına çıkmanızı sağlayacaktır.
WWW
Cevapla
#3
Çok güzel bir makale elinize sağlık...
Devart firmasının Linkleri Görebilmeniz İçin Giriş yap veya Üye Ol ürününü incelemenizi tavsiye ederim. 
Özellikle Linkleri Görebilmeniz İçin Giriş yap veya Üye Ol tarzında kullanımı ve Linkleri Görebilmeniz İçin Giriş yap veya Üye Ol ları çok güzel kullanmışlar.
Amatör Küme Bilgisayar Programcısı
WWW
Cevapla
#4
Burada ORM kavramını sadece herhangi bir veritabanı tablosunda(PERSON) ki verinin, nesnenin(APerson) ilgili alanlarına set etmek gibi kısıtlı bir düşünce içinde olmamakta fayda var.
Nitekim dilersek bu veri kaynağı bir tablo olabileceği gibi, XML, JSON yada herhangi bir veri formatı da olabilir.

Haliyle veri formatı çeşitliliği arttığında, ve biz bu çeşitliliğe cevap vermek durumunda kaldığımızda Map methodunda düzenlemeler yapmak, yada daha stratejik kodlamalar yapmak durumunda kalabiliriz...
One of the major software engineering challanges is managing change.
Cevapla
#5
Konu başlığının Attibute kısmına her ne kadar uymasada, yazının devamı niteliğinde olabilecek, bir arkadaşın sorusu üzerine record tipi için map örneğini paylaşmak isterim.

Record tipi :
 TBL_STOK_KART = record
   ID: Int64; // bigint
   KODU: string; // varchar
   ADI: string; // varchar
   TUR: Byte; // tinyint
   BIRIMTUR: SmallInt; // smallint
   BIRIM: string; // varchar
   KDVTUR: SmallInt; // smallint
   KDVORAN: Currency; // money
   OTVTUR: SmallInt; // smallint
   OTVORAN: Currency; // money
   ABF1: Currency; // money
   ABF1KDVDHL: Boolean; // bit
   ABF2: Currency; // money
   ABF2KDVDHL: Boolean; // bit
   SBF1: Currency; // money
   SBF1KDVDHL: Boolean; // bit
   SBF2: Currency; // money
   SBF2KDVDHL: Boolean; // bit
   BARKOD: string; // varchar
   BARKODTUR: string; // varchar
   BARKODTARTILI: Boolean; // bit
   KRITIKMIKTAR: Currency; // money
   GRUP_KODU: string; // varchar
   ALTGRUP_KODU: string; // varchar
   MARKA: string; // varchar
   PLU: string; // varchar
   ABF3: Currency; // money
   ABF3KDVDHL: Boolean; // bit
   ABF4: Currency; // money
   ABF4KDVDHL: Boolean; // bit
   SBF3: Currency; // money
   SBF3KDVDHL: Boolean; // bit
   SBF4: Currency; // money
   SBF4KDVDHL: Boolean; // bit
 end;

Map işlemi yapan method.
procedure Map(AInstance: Pointer; ATypeInfo: PTypeInfo; ADataset: TDataSet); overload;
var
 DbValue: TValue;

 context: TRttiContext;

 rttiField: TRttiField;
 rttiFields: TArray<TRttiField>;
begin
 if not ADataset.Active then
   raise Exception.Create('Bağlantıyı aç uleyn');

 context := TRttiContext.Create;
 rttiFields := context.GetType(ATypeInfo).GetFields;

 if not Assigned(rttiFields) then
   Exception.Create('Field Bilgileri alınamadı');

 for rttiField in rttiFields do
 begin
   DbValue := TValue.FromVariant(ADataset.FieldByName(rttiField.Name).value);
   rttiField.SetValue(AInstance, DbValue);
 end;

 context.Free;
end;

Kullanımı:
var
 AStokKart: TBL_STOK_KART;
begin
 Map(@AStokKart, TypeInfo(TBL_STOK_KART), ADOQuery2);
end;

Ek olarak Delphi deki record tiplerinin C/C++ daki Linkleri Görebilmeniz İçin Giriş yap veya Üye Ol tipine karşılık geldiğini belirtmekte fayda var.


Her zaman dediğim gibi eksikler/yanlışlar var.
Geliştirmesi size kalmış...
One of the major software engineering challanges is managing change.
Cevapla
#6
Merhaba, 

Uygulamalarımda DataBinding özelliğe sahip kontrol bileşenlerini (TDBEdit, TDBCombobox vs..) asla kullanmam.. Fakat tüm o sql cümlelerini elle yazmaya da üşenen bir adamım Smile  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..


Ek Dosyalar Resimler
           
Amatör Küme Bilgisayar Programcısı
WWW
Cevapla
#7
(30-10-2016, Saat: 02:27)barutali Adlı Kullanıcıdan Alıntı: Linkleri Görebilmeniz İçin Giriş yap veya Üye OlBenim 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.. 

Paylaşım için teşekkürler Ali bey;
Siz seçtiğim tablonun deyince, aklıma belki bir TDbObject attibute daha eklenebilir düşüncesi geldi.Şöyle ki ;
  TDbObject = class(TCustomAttribute)
 Private
   FName: string;
 public
   property Name: string read FName write FName;
   constructor Create(const AName: String);
 end;

{ TDbObject }
constructor TDbObject.Create(const AName: String);
begin
 FName := AName;
end;

Haliyle yeni class ve record'ların hali şöyle olabilir.
  [TDbObject('PERSON')]
 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;


[TDbObject('TBL_STOK_KART')]
 TBL_STOK_KART = record
 .
 .
 .
 end;


 Yağ, un ve şekerimiz artık mevcut olduğuna göre Linkleri Görebilmeniz İçin Giriş yap veya Üye Ol birileri çıkar herhalde...
One of the major software engineering challanges is managing change.
Cevapla
#8
Nullable type'lar dile entegre edildikten sonra ciddi ORM projeleri çıkabilir. Şimdilik Nullable type'ları simüle etmek icap ediyor.
Mal sahibi, mülk sahibi
Hani bunun ilk sahibi ?
Mal da yalan mülk de yalan
Var biraz da sen oyalan...
WWW
Cevapla
#9
(31-10-2016, Saat: 11:13)Tuğrul HELVACI Adlı Kullanıcıdan Alıntı: Linkleri Görebilmeniz İçin Giriş yap veya Üye OlNullable type'lar dile entegre edildikten sonra ciddi ORM projeleri çıkabilir. Şimdilik Nullable type'ları simüle etmek icap ediyor.

Ya sırf nullable type için Spring4D gibi koca framework'leri bu yapıya entegre etmek gerekiyor ya da beklemek gerekiyor şimdilik o da en az iki sene (komik)  Smile
Cevapla
#10
(31-10-2016, Saat: 16:34)edo Adlı Kullanıcıdan Alıntı: Linkleri Görebilmeniz İçin Giriş yap veya Üye Ol
(31-10-2016, Saat: 11:13)Tuğrul HELVACI Adlı Kullanıcıdan Alıntı: Linkleri Görebilmeniz İçin Giriş yap veya Üye OlNullable type'lar dile entegre edildikten sonra ciddi ORM projeleri çıkabilir. Şimdilik Nullable type'ları simüle etmek icap ediyor.

Ya sırf nullable type için Spring4D gibi koca framework'leri bu yapıya entegre etmek gerekiyor ya da beklemek gerekiyor şimdilik o da en az iki sene (komik)  Smile

 Benim aklımın almadığı hali hazırda olan bir çok şeyi nasıl oluyor da bu kadar uzun sürelere yayıyorlar.. Amerikayı tekrardan keşfetmeyecek ki.. 
Mesela Lambda yı çok uzun süredir bekliyorum.. WS-Security 1.1 standardını yine aynı şekilde.. 
illa ki oturup herşeyi kendimiz yazacaksak bunun RAD isminin ne anlamı kalıyor.
Yine aynı şekilde gömülü bir linq desteği de halen daha yok.. ya 3. partiye muhtaç kalacaksın yada oturacaksın önce kütüphanelerini yazacaksın..
Amatör Küme Bilgisayar Programcısı
WWW
Cevapla

Konuyu Paylaş : facebook gplus twitter





Konuyu Okuyanlar: 1 Ziyaretçi