EntityDAC

vložil Radek Červinka 2. března 2015 22:31

Nedávno zde Daniel popisoval ORM z praxe a já jsem se rozhodl, že se sám podívám na EntityDAC od DevArt s LINQ podporou. Považuji DevArt za hodně kvalitní vývojáře a proto jsem byl zvědav s čím mne překvapí. A není to špatné.

Úplně první co mne zaujalo, je že EntityDac podporuje 4 způsoby mapování dat na objekty DB a to vše ve vlastním designeru, který umí načíst model podle DB (nebo naopak vytvořit model a pak z něho vytvořit DB). EntityDac je dostupný v několika edicích včetně free express edice.

model.png

Na začátek ještě upozorním, že z hlediska přístupu k DB EntityDac podporuje přístup přes UniDAC, FireDac, ADO, DbExpress a další.

EntityDac.png

Takže když si vybereme šablonu (template) mapování (DevArt doporučuje pro pochopení první - na bázi TObject, mně se naopak zdá mnohem lepší Attribute mapped Entities), nástroj se připojí do DB, dá Vám vybrat tabulky, view, SP atd. a podle toho vygeneruje model.

EntityDac.png

Model se dá v nástroji upravovat a přizpůsobovat.

EntityDac.png

SQL ukázka

V demo programu jsou dvě tabulky:

CREATE TABLE DEPT (
    DEPTNO  INTEGER NOT NULL PRIMARY KEY,
    DNAME   VARCHAR(14),
    LOC     VARCHAR(13)
);
--
CREATE TABLE EMP (
    EMPNO     INTEGER NOT NULL PRIMARY KEY,
    ENAME     VARCHAR(10),
    JOB       VARCHAR(9),
    MGR       INTEGER,
    HIREDATE  TIMESTAMP,
    SAL       INTEGER,
    COMM      INTEGER,
    DEPTNO    INTEGER REFERENCES DEPT (DEPTNO)
);

Nyní můžeme přistoupit ke generování kódu. Podle vybrané šablony se vygeneruje několik tříd (jejich názvy se dají volit v dialogu na obrázku - viz. output name for Linq). EntityDac v mé případě (zvolená šablona) vygeneruje popis tabulky ve stylu:

 [Table('EMP')]
  [Model('DemoEntity')]
  [Key('FEmpno')]
  TEmp = class(TMappedEntity)
  private
    [Column('EMPNO')]
    FEmpno: Integer;
    [Column('ENAME', 10, [CanBeNull])]
    FEname: String;
    [Column('JOB', 9, [CanBeNull])]
    FJob: String;
    [Column('MGR', [CanBeNull])]
    FMgr: IntegerNullable;
    [Column('HIREDATE', [CanBeNull])]
    FHiredate: TDateTimeNullable;
    [Column('SAL', [CanBeNull])]
    FSal: DoubleNullable;
    [Column('COMM', [CanBeNull])]
    FComm: DoubleNullable;
    [Column('DEPTNO')]
    FDeptno: Integer;

    [Column]
    [Reference('TDept', 'FEmps', 'FDeptno', 'FDeptno', srCascade, drCascade)]
    FDept: TReferenceData;

    function GetEmpno: Integer; virtual;
    procedure SetEmpno(const Value: Integer); virtual;
    function GetEname: String; virtual;
    procedure SetEname(const Value: String); virtual;
    function GetJob: String; virtual;
    procedure SetJob(const Value: String); virtual;
    function GetMgr: IntegerNullable; virtual;
    procedure SetMgr(const Value: IntegerNullable); virtual;
    function GetHiredate: TDateTimeNullable; virtual;
    procedure SetHiredate(const Value: TDateTimeNullable); virtual;
    function GetSal: DoubleNullable; virtual;
    procedure SetSal(const Value: DoubleNullable); virtual;
    function GetComm: DoubleNullable; virtual;
    procedure SetComm(const Value: DoubleNullable); virtual;
    function GetDeptno: Integer; virtual;
    procedure SetDeptno(const Value: Integer); virtual;

    function GetDept: TDept;
    procedure SetDept(const Value: TDept);

  protected
    procedure Register; override;

    constructor Create(AMetaType: TMetaType); overload; override;

  public
    constructor Create; overload; override;

    property Empno: Integer read GetEmpno write SetEmpno;
    property Ename: String read GetEname write SetEname;
    property Job: String read GetJob write SetJob;
    property Mgr: IntegerNullable read GetMgr write SetMgr;
    property Hiredate: TDateTimeNullable read GetHiredate write SetHiredate;
    property Sal: DoubleNullable read GetSal write SetSal;
    property Comm: DoubleNullable read GetComm write SetComm;
    property Deptno: Integer read GetDeptno write SetDeptno;

    property Dept: TDept read GetDept write SetDept;
  end;

Všimněte si toho GetDept - TDept. To je odkaz na druhou třídu (tabulka). Tím, že jsem zvolil atributy, jsou jednotlivé pole tagovány RTTI atributy. Přijde mi to takové správné moderní řešení :-).

Framework má podporu pro zobrazení v db komponentách přes TEntityDataset, TEntityTable, takže výsledky operací se dají normálně zobrazovat (nevím zda i ve free edici).

Linq

Ale mnohem zajímavější je LINQ:

var
  E: IEmpExpression;
  D: IDeptExpression;
  Query: ILinqQueryable;
begin
  E := Context.Emp; 
  D := Context.Dept;

  Query := Linq.From(E)
               .LeftJoin(D).On(D.DeptNo = E.DeptNo)
               .Select([E.EName, D.DName]);

  Result := Context.GetEntities(Query);

Context je instance modelu. Emp a Dept jsou ty tabulky v modelu (viz. třída výše), takže pak je Linq jednoduše pochopitelné. Celkem maso je ale to On(D.DeptNo = E.DeptNo), to není string! (ale může být).

Vygeneruje

SELECT t1.ENAME AS Ename, t2.DNAME AS Dname
FROM EMP AS t1
LEFT JOIN DEPT AS t2 ON t2.DEPTNO = t1.DEPTNO

IEmpExpression, IDeptExpression je vygenerováno programem

  IDeptExpression = interface(IMetaType)
  ['{4D873A5E-7D30-48FF-9B02-7697397C6A8E}']
    function Deptno: TExpression;
    function Dname: TExpression;
    function Loc: TExpression;

    function Emps: IDeptEmpsExpression;
    function Unique: IDeptExpression;
  end;
  
  IEmpExpression = interface(IMetaType)
  ['{D066A14A-4CE1-4EE0-8776-3977678D9810}']
    function Empno: TExpression;
    function Ename: TExpression;
    function Job: TExpression;
    function Mgr: TExpression;
    function Hiredate: TExpression;
    function Sal: TExpression;
    function Comm: TExpression;
    function Deptno: TExpression;

    function Dept: IDeptExpression;
    function Unique: IEmpExpression;
  end;

Další příklad:

  Query := Linq.From(E)
               .OrderBy(E.EName)
               .Select([E.EName, E.Job, E.Dept.DName]);


Všimněte si toho E.Dept.DName:

SELECT t1.ENAME AS Ename, t1.JOB AS Job, t2.DNAME AS Dname
FROM EMP AS t1
LEFT OUTER JOIN DEPT AS t2 ON t1.DEPTNO = t2.DEPTNO
ORDER BY t1.ENAME

Samozřejmě podpora generik (změna názvu u posledního zaměstance):

var
  Emp: TEmp;
begin
  Emp := DemoContext.GetEntities<TEmp>.Last;

  // Modify entity attributes
  Emp.Ename := 'Name ' + IntToStr(Round(Random(99)));
  // Save changes
  Emp.Save;
end;

Upřímně: je to mazec a je to dobře navržené, ale ještě v tom pořád plavu. Stáhněte si kompilované demo, je to za tisíce slov.

Tagy:

Komponenty

Komentáře

5.3.2015 11:14:00 #

Daniel Andrascik

Tak to vyzera hodne dobre, podla vsetkeho by to mala byt lepsia volba ako TMS Aurelius

Daniel Andrascik

Komentování ukončeno

Naše nabídka

MVP
Ing. Radek Červinka - Embarcadero MVP
profil na linkedin, Twitter:@delphicz

Nabízím placené poradenství a konzultace v oblasti programování a vývoje SW.
Dále nabízíme i vývoj speciálního software na zakázku.

Neváhejte nás kontaktovat (i ohledně reklamy).

love Delphi

O Delphi.cz

Delphi je moderní RAD nástroj podporující tvorbu nativních aplikací pro platformu Win32, Win64, Mac OSX, Linux a na iPhone a Android.

Delphi.cz je nezávislý portál pro uživatele Delphi. Portál není koncipován pro úplné začátečníky, i když i ti se zde nebudou nudit, ale spíše na programátory, kteří již něco znají a chtějí své znalosti dále rozvíjet a sledovat novinky.

Poslední komentáře

Comment RSS

Dle měsíců