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.


Nabízíme Delphi školení na různá témata, primárně ve Vaší firmě.

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

Partial English version.

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 nebo burzy práce).

Pokud chcete podpořit tento server libovolnou částkou, můžete použít PayPal. Moc děkuji.

Delphi Certified Developer

O Delphi.cz

Delphi je jediný moderní RAD nástroj podporující tvorbu nativních aplikací pro platformu Win32, Win64 , Mac OSX a na iPhone a Android (s výhledem na další platformy díky FireMonkey) na současném trhu (včetně Windows 8.1).

V současnosti je světová komunita přes dva miliónů vývojářů.

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.

Anketa

Poslední komentáře

Comment RSS