ABSTRAKT Cílem práce je seznámit čtenáře s problematikou objektově-relačního mapování. Teoretická část popisuje objektově-orientované a relační paradigmata a způsoby jejich mapování. Praktická srovnává objektově-relační frameworky pro .NET a zabývá se způsobem implementace hromadného vkládání dat pro tyto frameworky.

KLÍČOVÁ SLOVA Objektově-relační mapování, ORM, .NET, C#, Relační databáze, LINQ, Entity framework, NHibernate, OpenAccess, LLBLGen, hromadné vkládání dat, SQL

ABSTRACT The aim of the thesis is to explain issues of object-relational mapping. The theoretical part describes object-oriented and relational paradigmas and ways of their mapping. The practical part compares object-relational mapping frameworks for .NET and suggests how to implement bulk insert into these frameworks.

KEYWORDS Object-relational mapping, ORM, .NET, C#, relational database, LINQ, Entity framework, NHibernate, OpenAccess, LLBLGen, bulk insert, Microsoft SQL

Vais, Z. Frameworky pro mapování relační databáze na objekty vhodné pro platformu .NET. Brno: Vysoké učení technické v Brně, Fakulta elektrotechniky a komunikačních technologií, 2014. 59 s., 12 s. příloh. Bakalářská práce. Vedoucí práce: doc. Ing. Ivo Lattenberg, Ph.D. PROHLÁŠENÍ Prohlašuji, že svou bakalářskou práci na téma Frameworky pro mapování relační databáze na objekty vhodné pro platformu .NET jsem vypracoval samostatně pod vedením vedoucího bakalářské práce a s použitím odborné literatury a dalších informačních zdrojů, které jsou všechny citovány v práci a uvedeny v seznamu literatury na konci práce. Jako autor uvedené bakalářská práce dále prohlašuji, že v souvislosti s vytvořením této bakalářská práce jsem neporušil autorská práva třetích osob, zejména jsem nezasáhl nedovoleným způsobem do cizích autorských práv osobnostních a/nebo majetkových a jsem si plně vědom následků porušení ustanovení § 11 a následujících zákona č. 121/2000 Sb., o právu autorském, o právech souvisejících s právem autorským a o změně některých zákonů (autorský zákon), ve znění pozdějších předpisů, včetně možných trestněprávních důsledků vyplývajících z ustanovení části druhé, hlavy VI. díl 4 Trestního zákoníku č. 40/2009 Sb.

V Brně dne ...... (podpis autora)

PODĚKOVÁNÍ Děkuji vedoucímu bakalářské práce doc. ing. Ivo Lattenbergovi, Ph.D.. za účinnou metodickou, pedagogickou a odbornou pomoc a další cenné rady při zpracování mé bakalářské práce.

V Brně dne ...... (podpis autora)

OBSAH

Obsah v

Seznam obrázků ix

Seznam tabulek x

Úvod 1

1 Relační databáze a SQL 2 1.1 Systém řízení báze dat a SQL ...... 2 1.2 Základní prvky relačních databází ...... 2 1.3 Vazby mezi tabulkami ...... 3 1.3.1 Stupeň vazby ...... 3 1.3.2 Kardinalita ...... 3 1.3.3 Volitelnost ...... 3 1.4 Nejpoužívanější databázová řešení ...... 4

2 Microsoft SQL Server a SQL CE 5 2.1 Microsoft SQL Server ...... 5 2.2 Microsoft SQL Server Compact ...... 6 2.3 Rozdíly mezi SQL Serverem a SQL CE ...... 7

3 Platforma .NET a Programovací jazyk C# 8 3.1 Microsoft .NET ...... 8 3.1.1 Principy ...... 8 3.1.2 Architektura a kompilační proces ...... 9 3.2 Programovací jazyk C# ...... 9 3.2.1 Podporovaná paradigmata ...... 9 3.2.2 Verze a historie ...... 10 3.3 Ostatní .NET jazyky ...... 11

4 Objektově orientované programování 12 4.1 Třídy, instance a objekty ...... 12 4.2 Koncepty ...... 12

v 4.2.1 Zapouzdření ...... 12 4.2.2 Dědičnost ...... 13 4.2.3 Polymorfimus ...... 14 4.3 Vztahy mezi objekty ...... 14 4.3.1 Asociace ...... 14 4.3.2 Agregace ...... 15 4.3.3 Kompozice ...... 15

5 Funkcionální programování 16 5.1 Koncepty ...... 16 5.2 Prvky funkcionálního programování v C# ...... 16 5.2.1 Delegáti ...... 16 5.2.2 Anonymní funkce ...... 16 5.2.3 Lambda výrazy ...... 16 5.2.4 Language Integrated Query ...... 17

6 Objektově-relační mapování 18 6.1 Problém Impedance Mismatch ...... 18 6.2 Mapování základní databázových prvků ...... 19 6.3 Mapování databázových vazeb na objekty ...... 20 6.3.1 Mapování asociačních vazeb ...... 20 6.3.2 Mapování dědičnosti ...... 21 6.4 Výhody a nevýhody ...... 23

7 Objektově-relační frameworky pro .NET 24 7.1 Přehled nejvýznaměnjších projektů ...... 24 7.2 Podporované databáze ...... 26

8 Srovnání vybraných ORM 27 8.1 Parametry testování ...... 27 8.1.1 Funkcionalita ...... 27 8.1.2 Testovací databáze ...... 29 8.1.3 Testované dotazy ...... 29 8.1.4 Výkonnost ...... 31 8.2 LINQ to SQL ...... 31 8.2.1 Cena ...... 31 8.2.2 Dokumentace ...... 31

vi 8.2.3 Funkcionalita ...... 31 8.2.4 Použití ...... 32 8.2.5 Výkonnost ...... 33 8.3 Entity framework ...... 34 8.3.1 Cena ...... 34 8.3.2 Dokumentace ...... 34 8.3.3 Funkcionalita ...... 34 8.3.4 Použití ...... 36 8.3.5 Výkonnost ...... 37 8.4 NHibernate ...... 37 8.4.1 Cena ...... 37 8.4.2 Dokumentace ...... 38 8.4.3 Funkcionalita ...... 38 8.4.4 Použití ...... 40 8.4.5 Výkonnost ...... 40 8.5 OpenAccess ...... 40 8.5.1 Cena ...... 40 8.5.2 Dokumentace ...... 41 8.5.3 Funkcionalita ...... 41 8.5.4 Použití ...... 43 8.5.5 Výkonnost ...... 43 8.6 LLBLGen Pro ...... 43 8.6.1 Cena ...... 43 8.6.2 Dokumentace ...... 43 8.6.3 Funkcionalita ...... 44 8.6.4 Výkonnost ...... 45 8.6.5 Použití ...... 46 8.7 Srovnání ...... 48 8.7.1 Cena ...... 48 8.7.2 Dokumentace ...... 48 8.7.3 Funkcionalita ...... 48 8.7.4 Výkonnost ...... 50 8.7.5 Použití ...... 52 8.7.6 Vhodnost použití jednotlivých ORM ...... 52

vii 9 Hromadné vkládání dat 54 9.1 Microsoft SQL Server ...... 54 9.1.1 Bulk Copy Program ...... 54 9.1.2 Třída SqlBulkCopy ...... 54 9.2 SQL Compact Edition ...... 55 9.2.1 SQL Compact Bulk Insert Library ...... 55 9.3 Hromadné vkládání dat a ORM ...... 56 9.3.1 Čtení data z souborů CSV ...... 56 9.3.2 Čtení data z souborů XML ...... 56

10 Ukázková aplikace 57

11 Závěr 58

Literatura 59

Seznam příloh 61

A Databázové skripty 61 A.1 Skript k vytvoření testovací databáze ...... 61

B Zdrojový kód 63 B.1 IRepository rozhranní ...... 63 B.2 EntityFrameworkRepository ...... 64 B.3 LinqToSqlRepository ...... 66 B.4 NHibernateRepository ...... 69 B.5 OpenAccessRepository ...... 71 B.6 Aplikace na testování rychlosti ORM ...... 73

viii SEZNAM OBRÁZKŮ

Obr. 3.1: Kompilace a spuštění a) u .NET frameworku a b) u Javy...... 9 Obr. 4.1: Různé typy dědičnosti: a) více-úrovňová dědičnost b) vícenásobná dědičnost, c) vícenásobná více-úrovňová dědičnost ...... 13 Obr. 6.1: Single table agregation – dva objekty se mapují na jednu tabulku ...... 20 Obr. 6.2: Mapování pomocí cizího klíče – dva objekty se mapují do dvou tabulek, provázaných vazbou...... 21 Obr. 6.3: Mapování asociace pomocí tabulky...... 21 Obr. 6.4: Single table inheritance ...... 21 Obr. 6.5: Class table inheritance ...... 22 Obr. 6.6: Concrete table inheritance ...... 22 Obr. 8.1: Schéma testovací databáze ...... 29 Obr. 8.2: LINQ to SQL designer ...... 32 Obr. 8.3: Srovnání základní funkcionality Entity frameworku a LINQ to SQL ...... 34 Obr. 8.4: Entity framework designer ...... 37 Obr. 8.5: OpenAccess průvodce k vytvoření webových služeb ...... 41 Obr. 8.6: Designer LLBLGen Pro - vztahy mezi entitami (převzato z [26]) ...... 46 Obr. 8.7: Designer LLBLGen Pro - mapování sloupců na property (převzato z [26]) ...... 47 Obr. 8.8: Výkonnostní srovnání ORM pro Dotaz A ...... 50 Obr. 8.9: Výkonnostní srovnání ORM pro Dotaz B ...... 50 Obr. 8.10: Výkonnostní srovnání ORM pro Dotaz C ...... 51 Obr. 8.11: Výkonnostní srovnání ORM pro Dotaz D ...... 51

ix SEZNAM TABULEK

Tab. 1.1: Deset nejoblíbenějších relačních databází dle serveru db-engines.com ...... 4 Tab. 1.2: Osm nejepoužívanějších komerčně využívaných databází dle agentury Gartner ...... 5 Tab. 2.1: Verze Microsoft SQL Serveru ...... 6 Tab. 2.2: Verze Microsoft SQL Server Compact ...... 7 Tab. 3.1: Přehled verzí jazyka C#, měsíce jejich zveřejnění, odpovídajícího verze .NET frameworku a novinek...... 10 Tab. 3.2: Přehled některých z nejpoužívanějších CLI jazyků a jejich paradigmat. .... 11 Tab. 6.1: Mapování jednotlivých databázových objektů...... 19 Tab. 7.1: Odkazy na oficiální stránky nejpoužívanějších ORM pro .NET ...... 24 Tab. 7.2: Přehled aktivních a nejpoužívanějších ORM pro .NET a jejich vlastností (převzato z [6])...... 25 Tab. 7.3: Přehled ORM pro .NET a jimi podporovaných databází (převzato z [6]). .. 26 Tab. 8.1: Přehled funkcionality LINQ to SQL ...... 31 Tab. 8.2: Výsledky výkonostního testování pro LINQ to SQL...... 33 Tab. 8.3: Přehled funkcionality Entity frameworku ...... 36 Tab. 8.4: Výsledky výkonostního testování pro Entity Framework...... 37 Tab. 8.5: Přehled funkcionality NHibernate ...... 39 Tab. 8.6: Výsledky výkonostního testování pro NHibernate ...... 40 Tab. 8.7: Přehled funkcionality OpenAcces ...... 42 Tab. 8.8: Výsledky výkonnostního testování pro OpenAccess...... 43 Tab. 8.9: Přehled funkcionality LLBLGen Pro ...... 45 Tab. 8.10: Výsledky výkonnostního testování pro OpenAccess...... 45 Tab. 8.11: Srovnání ceny různých ORM ...... 48 Tab. 8.12: Srovnání funkcionality různých ORM ...... 49 Tab. 8.13: Srovnání výkonnosti různých ORM pro Insert-Update-Delete ...... 52

x ÚVOD

Cílem této práce je seznámit čtenáře s tématem objektově-relačního mapování, představit některé z nejvýznamějších obejktově-relačních frameworků pro platformu .NET, srovnat jejich vlastnosti a vytvořit jednoduché příklady použitelné ve výuce. Práce začíná teoretickým úvodem do problematiky (kapitola 1 až 7). Následuje představením ORM pro .NET (kapitola 7) a jejich srovnání (kapitola 8). V další části (kapitola 9) jsou diskutovány možnosti implementace hromadného vkládání dat do těchto ORM.

1 1 RELAČNÍ DATABÁZE A SQL

V současnosti nejpoužívanějším typem databází jsou databáze relační. Jedná se o databáze založené na tzv. Relačním modelu, který vychází z matematického aparátu teorie množin a predikátové logiky.

1.1 Systém řízení báze dat a SQL

V praxi taková databáze sestává z tabulek, které obsahují sloupce a řádky. V širším pojetí je součástí databáze i software, který umožňuje přístup k uloženým datům i práci s nimi, tzv. Systém řízení báze dat (SŘBD). Standardním jazykem poskytovaným SŘBD pro přístup k datům je Structured Query Language (SQL).

1.2 Základní prvky relačních databází

Tabulka (table) je základním objektem relačních databází, který slouží k ukládání dat. Jedná se o dvourozměrnou strukturu, která sestává z pevně daného počtu sloupců a proměnném počtu řádků. Sloupec (column) je dán svým jménem, datovým typem, rozsahem, výchozí hodnotou a dalšími podobnými atributy. Primární klíč (primary key) je jednoznačný identifikátor řádku tabulky. Primárním klíčem může být jeden či více sloupců tabulky. V praxi se často používají uměle vytvořené číselné identifikátory. Cizí klíč (foreign key) slouží pro vyjádření vztahů mezi tabulkami. Index, často též označovaný jako klíč, je databázová konstrukce, sloužící ke zrychlení vyhledávacích a dotazovacích procesů v databázi, definování unikátní hodnoty sloupce tabulky nebo optimalizaci fulltextového vyhledávání. Pohled (view) se z pohledu čtení dat chová jako samostatná tabulka, avšak jedná se o uložený dotaz, který zpravidla zahrnuje více tabulek. Uložená procedura (store procedure) je databázový objekt, který neukládá data a je velmi podobný funkcím z procedurálních programovacích jazyků. Funkce (function) jsou podobné uloženým procedurám, avšak nabízejí větší spektrum možností. Oproti uloženým procedurám je možné je použít v databázových dotazech select. Transakce umožňuje soubor databázových příkazů vykonat atomárně tj. buďto se provedou všechny nebo žádný. Mezi další často používané databázové objekty patří např. spouště (trigger), uživatelem definované datové typy, výchozí hodnoty sloupců apod. s těmi se však v ORM pracuje jen zřídka kdy.

2 1.3 Vazby mezi tabulkami

Vztahy neboli relace slouží ke svázání dat, které spolu souvisí a jsou umístěny v různých tabulkách. Každý vztah můžeme charakterizovat třemi vlastnostmi:  stupeň,  kardinalita,  volitelná součást.

1.3.1 Stupeň vazby Stupeň vazby označuje počet tabulek, mezi kterými daná vazba existuje. Obvykle se uvádí stupně:  Unární - tabulka má vazbu sama se sebou.  Binární - vazba mezi dvěmi tabulkami, nejčastější stupeň vazby.  Ternární - vazba mezi třemi tabulkami najednou.  Enární - jedná se o vazbu mezi n-tabulkami najednou.

Ternární a enárni stupně vazby se v praxi příliš často nevyskytují a obvykle se jim dá předejít změnou designu databáze nebo celé aplikace.

1.3.2 Kardinalita Máme-li vazbu mezi dvěmi tabulkami, kardinalita určuje kolik záznamů v obou tabulkách se může vyskytovat v jedné vazbě. Rozlišujeme tři různé kardinality:  1:1 - vztah, ve kterém se na obou stranách vyskytuje právě jeden záznam. Příkladem může být např. vztah řidič - automobil.  1:N - jednomu záznamu z jedné tabulky je přiřazeno více záznamů z tabulky druhé.  M:N - více záznamům z jedné tabulky může být přiřazeno více záznamů z tabulky druhé. V praxi se tento vztah realizuje pomocí třetí vazebné tabulky.

1.3.3 Volitelnost Volitelnost určuje, zda-li je vazba povinná nebo ne. Mějme kupříkladu vazbu můž - manaželka,v monogamní společnosti se bude jednat o vazbu s kardinalitou 1:1, avšak vazba je nepovinná, neboť né každý muž musí mít manželku. Opačným případem tj. vazbou povinnou může být např. vztah dílo - autor, protože každé dílo musí mít autora.

3 1.4 Nejpoužívanější databázová řešení

Tabulka 1.1 ukazuje deset nejoblíbenějších databází dle hodnocení popularity serveru db-engines.com: [21]

Tab. 1.1: Deset nejoblíbenějších relačních databází dle serveru db-engines.com

Pořadí v anketě Databázový systém Dodavatel 1. Oracle Oracle Corporation 2. MySQL Oracle Corporation 3. Microsoft SQL Server Microsoft 4. PostgreSQL Open source 5. DB2 IBM 6. Microsoft Access Microsoft 7. SQLite Open source 8. Sybase ASE SAP 9. Teradata Teradata 10. FileMaker Apple/FileMaker Inc.

Podle agentury Gartner osm nejpoužívanějších komerčních databázových systémů v roce 2011 byly [22]:

4 Tab. 1.2: Osm nejepoužívanějších komerčně využívaných databází dle agentury Gartner

Pořadí Databázový systém Relativní počet použití Dodavatel v různých společnostech1 1. Oracle Database 70% Oracle Corporation 2. Microsoft SQL Server 68% Microsoft 3. MySQL 50% Oracle Corporation 4. IBM DB2 39% IBM 5. IBM Informix 18% IBM 6. SAP Sybase Adaptive Server 15% SAP Enterprise 7. SAP Sybase IQ 14% SAP 8. Teradata 11% Teradata

2 MICROSOFT SQL SERVER A SQL CE

2.1 Microsoft SQL Server

Microsoft SQL Server (MSSQL) je databázové řešení od společnosti Microsoft. První verze tohoto databázového systému byla vydána již v roce 1989 a její vývoj stále pokračuje. Stejně jako u řady jiných komerčních databází (např. Oracle) nejedná se již o čistě relační databázi. MSSQL v posledních několik verzích přidála prvky z jiných databázových konceptů např. objektový (SQL CLR, uživatelem definované datové typy, datový typ hierachyid), NoSQL (možnost replikovat data s MongoDB), XML databáze (podpora XML datových typů) a nebo in-memory. Seznam verzí MSSQL je uveden v tabulce 2.1.

1 Jedna společnost může zároveň využívat více různých databázových systémů

5 Tab. 2.1: Verze Microsoft SQL Serveru

Číslo verze Rok vydání Název Kódové jméno

1.0 1989 SQL Server 1.0 (16 bit) -

1.1 1991 SQL Server 1.1 (16 bit) -

4.21 1993 SQL Server 4.21 SQLNT

6.0 1995 SQL Server 6.0 SQL95

6.5 1996 SQL Server 6.5 Hydra

7.0 1998 SQL Server 7.0 Sphinx

SQL Server 7.0 OLAP - 1999 Palato mania Tools

8.0 2000 SQL Server 2000 Shiloh

SQL Server 2000 64-bit 8.0 2003 Liberty Edition

9.0 2005 SQL Server 2005 Yukon

10.0 2008 SQL Server 2008 Katmai

10.25 2010 SQL Azure DB CloudDatabase

10.5 2010 SQL Server 2008 R2 Kilimanjaro

11.0 2012 SQL Server 2012 Denali

12.0 2014 SQL Server 2014 SQL14

2.2 Microsoft SQL Server Compact

Microsoft SQL Server Compact (SQL CE) je specializovanou edicí MSSQL dříve známou pod označením Mobile Edition. SQL CE je určeno pro aplikace s embedovanou databází nebo aplikace, které nepotřebují plnohodnotnou verzi SQL Serveru jako jsou desktopové nebo mobilní aplikace. [20] SDF soubory SQL CE databáze může být celá uložená v jednom souboru s příponou .sdf (SQL CE Database File). Maximální velikost takovéhoto souboru je 4GB. .NET aplikace využívající sdf soubory se k nim mohou připojit buďto pomocí cesty k SDF souboru a nebo pomocí connection string, stejně jak je tomu u MSSQL.

6 Přehled verzí SQL CE je uveden v tabulce 2.2:

Tab. 2.2: Verze Microsoft SQL Server Compact

Číslo verze Rok vydání Název 1.0 2000 SQL Server 2000 Windows CE Edition 1.1 2000 SQL Server 2000 Windows CE Edition 1.1 2.0 2003 SQL Server 2000 Windows CE Edition 2.0 3.0 2005 SQL Server Mobile Edition 3.1 2005 SQL Server Compact Edition 3.5 2007 SQL Server Compact 3.5 4.0 2010 SQL Server Compact 4.0

2.3 Rozdíly mezi SQL Serverem a SQL CE

Dle [19] SQL Server 2008 nabízí celou paletu funkcionality, které v SQL CE 4.0 chybí, zde je stručný přehled těch nejdůležitějších z nich:  Uložené procedury a spouště  Pohledy (views)  Agregační funkce  BULK INSERT a podpora hromadného vkládání dat  DISTINCT v kombinaci s agregačními funkcemi  HAVING může obsahovat více sloupců a podmínek  ORDER BY <číslo sloupce>  MERGE příkaz  Vytváření aliasů pro databázové objekty s použitím '='.  Modulo na typech s desetinou čárkou (real, float, money, ...).  Agregační funkce mohou používat datové typy ntext a image  ORDER BY umí pracovat s velkými databovými typy jako ntext nebo image  Změna sloupců s datovým typem ntext nebo image.  Service Broker  Podpora SQL CLR  Filtrované indexy a spatial indexy  Komprese dat  Možnost nastavení case-sensitive databáze  Datové typy přidané do SQL Server 2008: Ordpath, sparse columns

7 3 PLATFORMA .NET A PROGRAMOVACÍ JAZYK C#

V následujícím textu bude představena platforma .NET, její základní prvky a programovací jazyk C#.

3.1 Microsoft .NET

.NET framework je soubor nástrojů a knihoven vyvinutý společností Microsoft pro snažší tvorbu aplikací. Ačkoliv Microsoft sám nikdy nevyvinul .NET framework pro jiný operační systém než Windows, tak ve spolupráci s dalšími společnostmi vytvořil otevřenou specifikaci Common Language Infrastructure (CLI) standardizovanou organizacemi ISO a ECMA. Můžeme proto na .NET pohlížet pouze jako na standard podobný např. standardu C++11. [13]

3.1.1 Principy Na začátku vývoje .NET frameworku a potažmo i programovacího jazyka C# si vývojový tým stanovil pět bodů, který by měla jejich nová platforma splňovat: 1. Obecné použití 2. Podpora více programovacích jazyků 3. Jednoduchý vývoj 4. Typová bezpečnost 5. Automatizovaná správa paměti

ad 1) .NET framework měl umožňovat vývoj maximálně široké škály aplikací. Nyní je možné tuto platformu využít k tvorbě windows aplikací, aplikací pro mobilní zařízení, webových stránek a služeb, ale dokonce i takových zařízení jako jsou roboti či některé mikrokontrolery apod. ad 2) .NET nyní podporuje více jak třicet programovacích jazyků počínaje objektově orientovanými jako C#, VB.NET nebo C++/CLI, dynamicky typovanými jako IronPython, IronRuby či PowerShell 3 až po funckionální jako F# a IronScheme. ad 3) Jedním z cílů vývojářského týmu bylo předejít problémům, které se v té době běžně objevovaly a komplikovaly práci tvůrcům softwaru. Mezi takovéto problémy patřily např. "DLL hell" - problémy s lokací a linkováním různých knihoven, obtížná spolupráce s COMem a nebo nutnost manuální správy paměti. ad 4) CLR (viz kapitola 3.1.2), které tvoří jádro .NETu je silně typované, a proto .NET jazyky, na něm postavené, mohou být taktéž pouze silně typované.2 ad 5) Automatizovaná správa paměti je zajištěna pomocí tzv. garbage collectoru,

2 Později bylo Microsoftem vyvinuté i tzv. DLR () umožňující podporu i dynamicky typovaných jazyků jako je např. IronPython nebo IronRuby.

8 který se stará o automatickou alokaci a dealokaci paměti.3

3.1.2 Architektura a kompilační proces Kompilační proces u .NET frameworku a jazyka C# je velmi podobný jazyku Java. Zdrojový kód není překládán přímo do strojového kódu, jako je to například u jazyků jako například Assembler, C či C++, ale do jazyku zvaného Common Intermediate Language (CIL nebo též MSIL), který je do strojového kódu přeložen až při spuštění samotné aplikace pomocí tzv. (CLR).

Obr. 3.1: Kompilace a spuštění a) u .NET frameworku a b) u Javy.

Samotný .NET framework můžeme rozdělit do dvou základních součástí:  Common Language Infrastructure (CLI)  Framework Class Library (FCL)

3.2 Programovací jazyk C#

C# je multi-paradigmatický programovací jazyk vyvinutý exkluzivně pro CLI. C# 2.0 stejně tak jako CLI má otevřenou specifikaci standardizovanou organizacemi ISO (ISO/IEC 23270:2006) a ECMA (ECMA-334). Specifikace vyšších verzí C# doposud nebyly standardizovány.

3.2.1 Podporovaná paradigmata Různé zdroje se v definici a rozdělení programovacích paradigmat liší. V rámci dalšího výkladu si vystačíme s tvrzením, že paradigma je takový styl tvorby aplikací vhodný k řešení určitého typu problémů. Například objektově orientované paradigma je vhodné pro systému sestávající z velkého množství entit, které spolu navzájem komunikují. Funkcionální styl programování je naopak vhodný pro úkoly s velkým počtem matematických výpočtů. Od první verze 1.0 byl C# navržen především jako objektově-orientovaný a silně typový jazyk. V dalších verzích byly do C# přidávány i prvky dalších paradigmat. Například ve verzi C# 3.0 byla přidána podpora lambda výrazů a tzv. Language

3 Ani zde se nejedná o novou myšlenku. Garbage collectory můžeme najít v celé řadě jiných programovacích jazyků jako je např. Java. První garbage collector byl dokonce vyvinut již v roce 1959 Johnem McCarthym jako součást programovacího jazyku LISP.

9 Integrated Query (LINQ) rozšiřující možnosti funkcionálního programování nebo ve verzi 4.0 přibyla možnost dynamického typování. I přestože však poslední verze C# obsahuje řadu prvků i z jiných konceptů než je OOP, je potřeba si uvědomit, že CLI je často nepodporuje, a proto jsou prekompilerem nebo kompilerem nebo CLR převáděny na ty, které jsou podporované. Jedním takovým případem jsou lambda výrazy, které jsou převáděny na třídy. Dochází tak k převodu z jednoho paradigmatu na jiný.

3.2.2 Verze a historie První verze C# s označením 1.0 byla zveřejněna spolu s první verzi .NETu v lednu 2002. Ačkoliv je teoreticky možné zveřejnit novou verzi C# bez nové verze .NET frameworku, Microsoft tak zatím neučinil a doposud byly nové verze jazyka publikovány společně s novými verzemi .NETu, viz tabulka 3.1.

Tab. 3.1: Přehled verzí jazyka C#, měsíce jejich zveřejnění, odpovídajícího verze .NET frameworku a novinek.

Verze Měsíc zveřejnění .NET framework Novinky 1.0 Leden 2002 1.0  Prvotní verze .NETu 1.2 Říjen 2003 1.1  Oprava řady chyb 2.0 Září 2005 2.0  Generické typy  Anonymní metody  Nullable types 3.0 Srpen 2007 3.0, 3.5  Extension metody  Lambda výrazy  LINQ 4.0 Duben 2010 4.0  Kovarinace a kontravariance  Nepovinné parametry  Dynamické typování 5.0 Červen 2013 4.5  Asynchronní metody

10 3.3 Ostatní .NET jazyky

Dle Wikipedie [14] dnes existuje více jak 30 programovacích jazyků postavených na platformě .NET. Mezi nejpoužívanější však stále patří C#, Visual Basic.NET a C++/CLI. Některé z nich a jimi podporovaná paradigmata můžete nalézt v tabulce 3.2.

Tab. 3.2: Přehled některých z nejpoužívanějších CLI jazyků a jejich paradigmat.

Programovací jazyk Podporovaná paradigmata C# OOP Silně-typovaný Funkcionální Visual Basic.NET OOP Silně-typovaný Funkcionální C++/CLI OOP Funkcionální Silně-typovaný F# Funkcionální Silně-typovaný OOP IronPython OOP Dynamicky-typovaný IronRuby OOP Dynamicky-typovaný IronLisp Funkcionální Dynamicky-typovaný Powershell Dynamicky-typovaný OOP

11 4 OBJEKTOVĚ ORIENTOVANÉ PROGRAMOVÁNÍ

Objektové programování je paradigma, které klade důraz na rozložení logiky programu na jednotlivé objekty. Objekty jsou schopny uchovávat svůj vlastní stav a navenek poskytují určitou funkcionalitu. Komunikace mezi objekty je umožněna pomocí tzv. zpráv, které si objekty navzájem posílají. Výše zmíněné objekty mohou odrážet věci z reálného světa jako je např. člověk, firma, akcie či technická součástka. Mohou však reprezentovat i zcela abstrakní struktury jako je např. prioritní fronta nebo (matematická) matice.

4.1 Třídy, instance a objekty

Než se pustíme do dalšího výkladu, je potřeba objasnit si pojmy třída (class), instance třídy (instance) a objekt (object). Třída funguje jako šablona pro vytváření nových objektů. Můžeme např. definovat třídu Osoba. Takováto třída může obsahovat celou řadu atributů jako např. pohlaví, jméno, výška, barva očí apod., avšak nebude obsahovat žádná konkrétní data. Instance v okamžiku, kdy budeme požadovat pracovat s nějakou konkrétní osobou, musíme vytvořit tzv. instanci třídy Osoba, která již má všechny vlastnosti vyplněné. Instance jsou v paměti počítače ukládány na haldě (heap) a předávány referencí, mluvíme proto o tzv. referenčních typech. Objekt je pojem pod nímž je obvykle myšlena instance libovolné třídy. V C# jsou navíc za objekty považovány nejenom instance tříd ale i hodnotové typy.

4.2 Koncepty

Většina zdrojů včetně [1] uvádí tři základní koncepty OOP:  Zapouzdření  Dědičnost  Polymorfismus

Tyto tři koncepty budou dále rozvedeny v následujících podkapitolách.

4.2.1 Zapouzdření Zapouzdření (encapsulation) se využívá k skrytí vnitřního stavu a metod objektu. Hlavním důvodem k zavedení zapozdření je snaha o znemožnění změny stavu objektu jinými neautorizovanými objekty.

12

Obvykle je zapouzdření realizováno pomocí tzv. accessorů - klíčových slov určující viditelnost prvků objektu. V C# jich můžeme nalézt pět:  public - Členy jsou viditelné všem ostatním objektům  private - Viditelnost pouze z vlastního objektu  protected - Viditelnost v vlastního objektu a všech jeho poděděných objektů.  internal - Viditelnost v rámci vlastní  protected internal - Viditelné v rámci assembly nebo poděděného objektu.

C# dále umožňuje pomocí tzv. property nadefinovat vnitřní proměnné objektu s rozdílnou viditelností pro čtení a zápis:

// Promenna ze kterou mohou cist vsechny objekty, // zapisovat vsak jenom vlastni trida. public object Promenna { get; private set; }

4.2.2 Dědičnost Dědičnost (Inheritance) je jedním z základních kamenů OOP. Dědičnost umožňuje jedné třídě získávat a potažmo i modifikovat vlastnosti a metody jiné třídy. Použití dědičnosti nám dále umožňuje vytvářet hierarchické struktury s objekty. Dědičnost můžeme podle vzniklé hierarchie objektů rozdělit na tři typy:  Více-úrovňová dědičnost (multi-level inheritance)  Vícenásobná dědičnost (multiple inheritance)  Vícenásobná více-úrovňová dědičnost (multiple multi-level inheritance)

Obr. 4.1: Různé typy dědičnosti: a) více-úrovňová dědičnost b) vícenásobná dědičnost, c) vícenásobná více-úrovňová dědičnost

13 C# však dle principů komponentově-orientovaného programování umožňuje pouze více-úrovňovou dědičnost. K definování dedičnosti mezi dvěmi třídami je v C# využita dvojtečka:

// Rodicovska trida class Rodic { // Implementace rodicovske tridy }

// Trida Potom dedi vlastnosti tridy Rodic class Potomek : Rodic { // Implementace potomka }

4.2.3 Polymorfimus Dědičnost umožňuje objektům sdílet část své funkcionality pomocí rodičovských dřív. Občas se však dostaneme do situace, kdy potřebujeme, aby na danou zprávu byly schopny zareagovat dvě nebo více různý tříd, každá však jinak. Jedná se proto o sdílení určitého rozhranní mezi objekty nezávislého na implementaci těchto objektů. V C# existuje více způsobů, jak polymorfismus realizovat:  rozhranní (interfaces)  abstraktní třídy  klíčová slova virtual a override

4.3 Vztahy mezi objekty

Na základě závislosti dvou objektů a způsobu jeji vzájemné komunikace můžeme zavést tři typy vztahů mezi objekty. Je potřeba však podotknout, že se jedná spíše o teoretické rozdělení a v praxi není vždy možné rozhodnout, o který typ vazeb se jedná. [15] V programovacím jazyku C# jsou realizovány pomocí referencí - jedna třída obsahuje referenci na třídu jinou.

4.3.1 Asociace Asociace je typ vazby mezi objekty, které spolu logicky souvisí, avšak nedá se říct, že by jeden byl vlastníkem toho druhého. Asociace umožňuje jednomu objektu pracovat s metodami a vlastnosti druhého objektu. Typickým příkladem asociace je vztah Učitel - Student. [16]

14 4.3.2 Agregace Agregace rozšířením asociace. U agregace můžeme jeden objekt prohlásit za vlastníka jiného objektu. Pokud z jakéhokoli důvodu dojde k odstranění vlasníka, tak objekty připojené agregační vazbou zůstávají a nejsou odstraněny. Jako příklad agregace může sloužit vazba Katedra - Učitel. [16]

4.3.3 Kompozice Kompozice je rozšířením agregace. U komopozice při odstranění vlastníka dochází i k odstranění vázaných objektů. Tento druh vazby se obvykle vyskytuje u objektů, kteří bez dceřiných objektů nemohou vůbec existovat nebo ani vzniknout. Příkladem kompozice může být např. vztah Dům - Mistnosti. Samotný dům nemůže bez místností vznikout ani zaniknout.[16]

15 5 FUNKCIONÁLNÍ PROGRAMOVÁNÍ

Jak již bylo vysvětleno v kapitole 3.2, podpora funkcionálního programování se stala nedílnou součástí programovacího jazyka C# a dnes již zasahuje téměř do všech součástí .NETu včetně objektově-relačního mapování. ORM pro .NET framework, který by dnes nevyužíval funkcionálních prvků jako je LINQ nebo lambda výrazy, by byl nejspíše považován za nemoderní a nebyl by využívám.

5.1 Koncepty

Funkcionální programování (FP) původně vychází z lambda kalkulu určeného ke studiu funkcí a rekurze. Stejně tak jako u OOP i u FP můžeme stanovit několik základních konceptů:  neměnnost proměných,  rekurze (recursion)  líné vyhodnocování (lazy evaluation)  funkce vyššího řádu (high-order function)

5.2 Prvky funkcionálního programování v C#

5.2.1 Delegáti Delegát je takový datový typ, který zapouzdřuje funkci nebo metodu popř. více funkcí či metod. Delegáti jsou určitou obdobnou ukazatelů na funkce, které známe z C nebo C++. Hlavní rozdíl mezi delegáty v C# a ukazateli na funkce z C a C++ je v tom, že C# zajišťuje typovou bezpečnost a delegát tedy nemůže ukazovat na jinou funkci nebo objekt, který by odporoval jeho definici. [2] S použitím delegátů je snadno možné předávat funkce jako argumenty jiným funkcím, a nebo je vracet jako výslednou hodnotu. Přidávají tedy do C# podporu jednoho ze základních konceptů FP tzv. funkcí vyšších řádů.

5.2.2 Anonymní funkce Anonymní funkce jsou formou delegátů přidanou do C# 2.0 umožňují zkrátit čas jejich syntaxe. Anonymní funkce je možné definovat i uvnitř jiných funkcí, jsou tedy jakousi obdobnout tzv. inline funkcí z C++.

5.2.3 Lambda výrazy Lambda výrazy (Lambda expressions) jsou syntaktickou novinkou umožňující tvorbu anonymních funkcí, přidanou do C# 3.0. Lambda výrazy můžeme znát z předních funkcionálních jazyků jako je Lisp, Haskell, Scheme nebo F#. Následující příklad ukazuje, jak lze lambda výrazy použít k definování funkce vracející součet dvou čísel:

16 (double x, double y) => x + y;

V rámci použití pro ORM nás však budou především zajímat výrazy s booleavskou návratovou hodnotou, kterýése často používá na specifikaci logických podmínek do where části SQL dotazů např.: (double x, double y) => x > y; nebo (string x) => x.Contains("text")

Dalším druhem výrazů, který pro nás bude zajímavý, jsou výrazy, které mají na vstupu objekt a vracejí nějakou z jeho vlastností např.: (Osoba x) => x.Jmeno

Tento typ lambda výrazů se používá zejmnéna k mapování sloupců tabulek na vlastnosti tříd.

5.2.4 Language Integrated Query Jazyky odvozené od jazyka C (tedy i C#) jsou svojí povahou imperativní, což znamená, že kladou důraz na stav systému, který se během provádění programu mění. Jazyky pro získávání dat, jako je např. SQL, mají spíše funkcionální povahu, což znamená, že důraz je kladen na operace a během zpracování SQL příkazů se téměř nepoužívají měnitelná data. Language Integrated Query (LINQ) je další z novinek v C# 3.0 určených k propojení imperativního s funkcionálního stylu programování. Součástní .NETu je několik různých implementací LINQu např. LINQ to Objects, LINQ to XML nebo LINQ to SQL. Vzhledem k podobnosti své syntaxe s SQL LINQ implementuje celá řada ORM. V takovém případě musí každý ORM obsahovat tzv. LINQ provider, který zajišťuje překlad LINQ příkazů do SQL.

17 6 OBJEKTOVĚ-RELAČNÍ MAPOVÁNÍ

Objektově-orientované programování umožňuje tvorbu aplikací z objektů, které mají jak data tak chování. Na druhou stranu relační technologie ukládají data v tabulkách a umožňují s nimi manipulovat pomocí SQL. Oba tyto přístupy se staly dominantní. Relační model dominuje na poli ukládání a objektově-orientovaný přístup naopak ve tvorbě logiky aplikací. [2]

6.1 Problém Impedance Mismatch

Je zřejmé, že propojení těchto dvou přístupů může být často komplikované. Tento problém je obecně označován jako tzv. Impedance Mismatch. Scott Wambler vysvětluje hlavní příčiny tohoto problému takto: OO paradigma je založeno na vyzkoušených principech softwarového inženýrství. Relační paradigma je však založeno na matematických principech. Impedance mismatch se stává více zřejmý, když se podíváme jak na přístup k datům. U OO paradigmatu obvykle procházíme objektovou strukturu pomocí vazeb mezi objekty, kdežto u relačního paradigmatu dochází k slučování (join) řádků tabulek.[7][2]

Existuje více pokusů řešení tohoto fundamentálního problému. Jedním z nich jsou tzv. objektově-relační databáze, které umožňují práci s objekty již na úrovni databáze. V dnešní době se určitá podpora objektového paradigmatu začíná objevovat u většiny velkých databází jako jsou např. Oracle, PostgreSQL a Microsoft SQL. Druhým přístupem jsou objektově-relační frameworky a nástroje, které fungují jako jakýsi most mezi oběma paradigmaty.

18 6.2 Mapování základní databázových prvků

V této kapitole se pokusíme popsat, jakým obvyklým způsobem probíhá mapování základních databázových stavebních prvků z kapitoly 1.2:  Tabulka bývá obvykle mapována na jednu nebo více tříd.  Sloupec je standardně mapován na vlastnost jedné třídy.  Primární klíč je pouze určitou vlastností jednoho či více sloupců a nemá v OOP odpovídající protipól. Většinou bývají vlastnosti primárního klíče zajištěny logikou celé aplikace.  Cizí klíč je stejně tak jako primární klíč vlastností jedno nebo více sloupců a lze ho ukládat jako vlastnost třídy. Avšak mapování vazeb jako takových je komplikovanější  Index nemá v OOP obdobu, je potřeba zajistit aplikační logikou.  Pohled je možné vytvořit jednu třídu reprezentující celý pohled a nebo z více tříd, kde každá třída bude odpovídat jedné tabulce z pohledu.  Uložená procedura nebo funkce - je možné namapovat na funkci.  Transakce jako taková nesouvisí s objektově-relačním mapováním jako spíše se způsobem připojení do databáze a způsobem vykonání příkazů na databázi.

Mapování je shrnuto v tabulce 6.1:

Tab. 6.1: Mapování jednotlivých databázových objektů.

Databázový objekt Ekvivalent v OOP Tabulka Třída nebo více tříd Sloupec Atribut třídy Primární klíč Není. Řeší aplikační logika Cizí klíč Více způsobů Index Není Pohled Třída nebo více tříd Uložená procedura Metoda třídy, procedura nebo funkce Funkce Metoda třídy, procedura nebo funkce Transakce Není

19 6.3 Mapování databázových vazeb na objekty

Chceme-li v relačním modelu vytvořit vazbu mezi dvěma tabulkami, jedinou možností, jak toho dosáhnout, je vytvořit vazbu pomocí cizích klíčů. OOP nám nabízí více možností, můžeme použít asociaci a nebo dědičnost (která nemá v RM obdobu) a nebo dokonce jejich kombinaci. Existuje několik nejrůznějších vzorů určených k mapování dědičnosti, asociace a agregace. [2]

6.3.1 Mapování asociačních vazeb Single table aggregation je vzor použitelný, pokud mezi objekty existuje vazba 1:0 nebo 1:1. U tohoto vzoru probíhá mapování všech mapovaných objektů do jedné tabulky, viz obrázek 3.1.

Obr. 6.1: Single table agregation – dva objekty se mapují na jednu tabulku

Foreign key mapping je naopak vzor, vhodný pro vazby 1:N. Tato vazba je v C# obvykle implementována pomocí kolekcí - jeden objekt obsahuje kolekci jinách objektů. Na úrovni databáze je kolekce naharazena vazbou 1:N mezi dvěmi tabulkami, viz obrázek 3.2.

20 Obr. 6.2: Mapování pomocí cizího klíče – dva objekty se mapují do dvou tabulek, provázaných vazbou. Association table mapping se využívá u vazeb M:N. Jak již bylo zmíněno v kapitole 1.3.2, tato vazba bývá v databázi obvykle implementována pomocí vazební tabulky, viz Obr. 6.3

Obr. 6.3: Mapování asociace pomocí tabulky.

6.3.2 Mapování dědičnosti Single table inheritance ukládou objektovou hierarchii vzniklou dědičností do jedné tabulky.

Obr. 6.4: Single table inheritance

21 Class table inheritance je přístupem zcela opačným, neboť ukládá každou tabulku do jiné třídy.

Obr. 6.5: Class table inheritance

Concrete table inheritance stojí někde mezi oběmi zmíněnými vzory. Pro každou třídu je sice vytvořena oddělená tabulka, avšak obsahuje sloupec i všechny atributy z rodičovských tříd.

Obr. 6.6: Concrete table inheritance

22 6.4 Výhody a nevýhody

Mezi výhody použití ORM patří:  Menší množství kódu - ORM část kódu automaticky vygeneruje, proto není nutné tento kód psát nebo dále udržovat.  Rychlejší vývoj - Jak již bylo řečeno, ORM redukuje množství kódu, které musí programátor napsat, čímž zrychluje vývoj dané aplikace.  Změny je možné provést na jednom místě - Rozhodneme-li se upravit tabulku v databázi, není nutné úpravy provádět i v aplikačním kódu. ORM je často provede za nás.  Čistě objektově orientovaný přístup - ORM obstarává práci s databázi, proto v aplikačním kódu můžeme pracovat čistě s objekty. Nedochází tak k míchání paradigmat a výsledný kód je čitelnější.  Nezávistlost na použité databázi - Dnešní ORM často podporují více jak jeden typ databáze. Výsledná aplikace je proto částečně nezávislá na použitém úložišti dat.  Menší znalost databáze - Tím, že ORM obstarává většinu práce s databází, tvůrce aplikace nemusí mít detailní znalost databázových technologií.

Mezi hlavní nevýhody použití ORM patří:  Horší výkonost - Žádný ORM není dokonalý a ne všechny databázové dotazy, které vygeneruje, jsou optimální.  Neúplná kontrola nad vygenerovaným kódem - Ačkoliv většina ORM poskytuje široké možnosti přizpůsobení vzhledu výsledných objektů, né vždy je snadné provést potřebné úpravy.  Ladění - Vygenerovaný kód na jedné straně snižuje množství programátorské práce, na druhou stranu lze zpravidla hůře zjistit, jaké dotazy jdou do databáze apod.  Větší znalost ORM frameworku - Při použití ORM programátor nemusí mít detailní znalosti databází, avšak musí znát daný ORM framework, jeho nastavení a API.

23 7 OBJEKTOVĚ-RELAČNÍ FRAMEWORKY PRO .NET

Dle Wikipedie [17] a internetového vyhledávání existuje několik desítek ORM frameworků pro .NET. V této kapitole uvedeme přehled většiny dostupných frameworků pro .NET a podrobněji rozebereme nejdůležitější z nich.

7.1 Přehled nejvýznaměnjších projektů

Tabulka 7.1 uvádí přehled nejvýznamnějších aktivních projektů pro .NET spolu s odkazem na jejich oficiální stránky. Jedná se pouze o aktivní projekty, jedinou výjimkou je LINQ to SQL, který je v tabulce zahrnut díky své rozšířenosti. Tabulka 7.2 shrnuje jejich základní vlastnosti. [17][1]

Tab. 7.1: Odkazy na oficiální stránky nejpoužívanějších ORM pro .NET

Název Autor/Vlastník Oficiální stránky a dokumentace Business Logic Open source http://bltoolkit.net Toolkit Castle ActiveRecord Open source http://www.castleproject.org/projects/activerecord Dapper.NET Open source https://github.com/SamSaffron/dapper-dot-net DatabaseObjects.NET HI Systems http://www.hisystems.com.au/databaseobjects DataObjects.NET Xtensive http://dataobjects.net Entity Framework Microsoft http://msdn.microsoft.com/en-us/data/ef.aspx EntitySpaces EntitySpaces http://www.entityspaces.net LLC Genome TechTalk http://www.genom-e.com GmbH LightSpeed Mindspace http://www.mindscapehq.com/products/lightspeed LightConnect Devart http://www.devart.com/linqconnect LLBLGen Pro Solution http://www.llblgen.com Design LINQ to SQL Microsoft http://msdn.microsoft.com/en- us/library/bb386976(v=vs.110).aspx MicroLite Open source https://github.com/TrevorPilley/MicroLite NHibernate Open source http://nhforge.org NPA (.NET Open source http://www.npersistence.org Persistence API) OpenAccess ORM Telerik http://www.telerik.com/products/orm.aspx

24 OrmLite Open source http://www.servicestack.net PetaPoco Top ten http://www.toptensoftware.com/petapoco software Vici CoolStorage Open source http://viciproject.com/wiki/projects/coolstorage/home XPO Devexpress http://www.devexpress.com/Products/NET/ORM

Tab. 7.2: Přehled aktivních a nejpoužívanějších ORM pro .NET a jejich vlastností (převzato z [1]).

Název Zdarma Aktivní NuGet Podpora Autor/Vlastník vývoj LINQ Business Logic Toolkit Ano Ano Ne Ano Open source Castle ActiveRecord Ano Ano Ano Ano Open source Dapper.NET Ano Ano Ano Ne Open source DatabaseObjects.NET Ano Ano Ne Ne HI Systems DataObjects.NET Částečně Ano Ano Ano Xtensive Entity Framework Ano Ano Součást Ano Microsoft .NET EntitySpaces Ano Ano Ne Ano EntitySpaces LLC Genome Ne Ano Ne Ano TechTalk GmbH LightSpeed Částečně Ano Ne Ano Mindspace LightConnect Ne Ano Ne Ano Devart LLBLGen Pro Částečně Ano Ne Ano LLBLGen LINQ to SQL Ano Ne Součást Ano Microsoft .NET MicroLite Ano Ano Ano Ne Open source NHibernate Ano Ano Ano Ano Open source NPA Ano Ano Ano Ne Open source OpenAccess ORM Ne Ano Ano Ano Telerik OrmLite Ano Ano Ano Ne Open source PetaPoco Ano Ano Ano Ne Top ten software Vici CoolStorage Ano Ano Ano Ne Open source XPO Ne Ano Ne Ano Devexpress

25 7.2 Podporované databáze

Jedním z nejdůležitějších faktorů při volbě ORM je výčet podporovaných databází. Takovýto přehlem je vidět v tabulce 7.3. Nejvíce databází a to 11 podporuje ORM eXpress Persistent Objects (XPO) od společnosti DevExpress. Deset databází podporuje Entity framework od společnosti Microsoft. Devět LLBLGen od společnosti Solution Design, Open Access od Teleriku a EntitySpaces od stejnojmenné společnosti. NHibernate, který podporuje osm různých databází, je v tomto výčtu jediným zástupcem, za kterým nestojí žádná společnost a jedná se čistě o open source projekt. Tabulka 7.3 obsahuje přehled podporovaných databází:

Tab. 7.3: Přehled ORM pro .NET a jimi podporovaných databází (převzato z [1]).

SQLServer SQLite MySQL Oracle Firebird SQLCE VistaDB DB2 Informix PostgreSQL Sybase Access Microsoft Pervasive databází Počet

Business Logic Toolkit 2

 

Castle ActiveRecord 6

     

Dapper.NET 5

    

DatabaseObjects.NET 5

    

DataObjects.NET 6

     

Entity Framework 10

         

EntitySpaces 9

        

Genome 4

   

LightSpeed 7

      

LightConnect 6

     

LLBLGen Pro 9

        

LINQ to SQL 2

 

MicroLite 4

   

NHibernate 8

       

NPA 6

     

OpenAccess ORM 9

        

OrmLite 5

    

26

PetaPoco 5

    

Vici CoolStorage 5

    

XPO 11

          

8 SROVNÁNÍ VYBRANÝCH ORM

8.1 Parametry testování

U vybraných ORM pro .NET budeme srovnávat tyto parametry:  Cena  Funkcionalita  Použití  Dokumentace  Podporované databáze  Výkon

Použitelnost a výkon bude ověřována na stejných příkladech definovaných v kapitole 8.1.3.

8.1.1 Funkcionalita Pod bodem funkciolita je myšleno množství možností, které daný ORM jeho uživateli nabízí. Zaměříme se především na následující body: 1. Podporované styly mapování (viz. kapitola 6) 2. Možnost ovlivnit vzhled a funkcionalitu výsledných objektů 3. Podpora různých databázových objektů 4. Podpora DTO objektů 5. Porpora Self-tracking objektů 6. Podpora code-first přístupu 7. Podpora cachování 8. Možnost vygenerovat databázi 9. Lazy-loading a eager loading 10. Práce ve více vláknovém prostředí 11. Runtime úpravy schématu

ad 3) Různými databázovými objekty jsou myšleny struktury, které lze v databázi vytvořit a dále s nimi pracovat, viz kapitola 1.2. Né všechny ORM však nabízejí stejné možnosti práce se všemi objekty a některé databázové objekty nemusejí být podporovány vůbec. Všechny z porovnávaných ORM porporují minimálně tabulky, pohledy a uložené procedury.

27 ad 4) Data Transfer Object (DTO) je objekt, který postrádá jakoukoli funkcionalitu, neboli obsahuje pouze atributy. Takovéto objekty jsou vhodné např. pro serializaci pro webové služby nebo na přenos informací mezi jednotlivými vrstvami aplikace. Jedním z kritérií hodnocení ORM otázka, zda-li ORM umí vygenerovat DTO a dále s nimi pracovat. U EF se tyto objekty nazývají Plain Old CLR Objects (POCO). ad 5) Self-tracking objekty (popř. Self-tracking entities - STE) jsou objekty, které dokáží uchovávat změny na nich provedené. Uplatnění nacházejí obdobně jako DTO u více-vrstvých aplikací. Např. v situaci, kdy prezenční nebo business vrstva provádí změny těchto objektů, aniž by pracovala se samotným ORM. V takovém případě ORM není schopen zjistit, jaké změny byly na objektech provedeny, a proto tyto změny musí uchovavány samotnými objekty. ad 6) Code-first, database-first a model-first jsou tři přístupy pro ORM. U database-first je nejprve databáze a na jejím základě se vygenerují třídy. Database-first je u ORM standardem. U code-first naopak nejprve vytvoříme strukturu tříd a z ní teprve vygenerujeme databázi. Code-first podporují jen některé ORM. Třetím a nejméně používaným je model-first. Model-first je založen na sestavení modelu, který nezávisí na použité databázi ani programovacím jazyce. Z tohoto modelu se následně vygeneruje, jak celá databáze, tak i zdrojový kód s objekty. ad 7) Cachování můžeme rozdělit na dva druhy:  First-level caching (L1 caching) je obvykle spojeno s session, transakcí nebo databázovou dávkou. Hlavním účelem first-level cachingu je redukovat počet dotazů jdoucích do databáze. Pokud se např. pokusíme v jedné session získat jeden řádek dvakrát, do databáze ve skutečnosti jde pouze jeden dotaz. Implementace se pro různé ORM liší.  Second-level caching (L2 caching) naopak umožňuje přístup k cache z jakékoli session, nebo dávky.

ad 8) Vygenerování databáze - jedná se o vlastnost, která je často dostupná jako součást code-first, avšak některé ORM ji nabízejí i pro database-first nebo model-first. Tato vlastnost např. umožňuje aplikaci vytvořit vlastní databázi během prvního startu aplikace apod. ad 9) Lazy loading neboli líne vyhodnocování je návrhový vzor a jeden ze základních kamenů funkcionálního programování. V případě ORM se umožňuje oddálit incializaci objektů a jejich načtení z databáze až do okamžiku, kdy jsou skutečně potřeba. Eager loading je opakem lazy loading. Objekty nebo jejich vlastnosti využívající eager loading jsou z databáze načteny během inicialize objektu. ad 10) Práce ve vícevláknovém prostředí - Současné aplikace zpravidla pracují ve více vláknech. U tohoto bodu se hodnotí, zda-li jsou objekty vygenerované daným ORM vláknově bezpečné a jestli podporují asynchronní komunikaci s databází. ad 11) Pod pojmem runtime úpravy schématu jsou myšleny změny v mapování a databázi v průběhu běhu aplikace.

28 8.1.2 Testovací databáze Abychom mohli jednotlivé ORM porovnávat, je nutné veškeré testování provádět na stejných databázových objektech. Za tímto účelem byla vytvořena jednoduchá databáze ukládající informace o ústavech vysoké školy, jejich zaměstnancích a předmětech. Schéma této databáze je na vidět na vidět na Obr. 8.1.

Obr. 8.1: Schéma testovací databáze Databáze obsahuje čtyři jednoduché tabulky:  Ustav - obsahuje informace o ústavu jako je např. název nebo zkratka  Ucitel - nese informace o pedagogovi zaměstnávaného ústavem. Mezi tabulkami Ustav a Ucitel je vazba 1:N tj. učitel může být zaměstnancem pouze jednoho ústavu.  Predmet uchováva informace o předmětech jako je napč. název, zkratka nebo počet kreditů. Každý předmět má přiřazený ústav, který jeho výuku zajišťuje tj. vazba 1:N.  UcitelPredmet je vazební tabulka mezi entitami Ucitel a Predmet. Jeden předmět může mít více vyučujících a zároveň jeden vyučující může učit více předmětů. Tabulka proto realizuje vazbu M:N.

Skript pro vytvoření této databáze je v příloze A.1.

8.1.3 Testované dotazy U každého z vybraných ORM byl za účelem testování použitelnosti a výkonosti implementován C# kód provádějící stejnou funkcionalitu jako následující SQL dotazy nad testovací databází. Některé z níže uvedených SQL dotazů jsou určeny pouze k výkonnostnímu testování, jiné naopak mají srovnání použitelnosti.

29

Dotaz A. - Jednoduchý dotaz nad tabulkou SELECT * FROM Ucitel

Dotaz B. - Dotaz s dvěmi podmínkami SELECT * FROM Ucitel WHERE Prijmeni LIKE '%ha%' OR Plat > 100000

Dotaz C. - Dotaz nad vazbou 1:N pomocí inner join SELECT * FROM Ustav AS U INNER JOIN Predmet AS P ON U.Id = P.UstavId

Dotaz D. - Dotaz nad vazbou M:N pomocí dvou inner join SELECT * FROM Ucitel AS U INNER JOIN UcitelPredmet AS UP ON U.Id = UP.UcitelId INNER JOIN Predmet AS P ON UP.PredmetId = P.Id

Dotaz E. - Vytvoření nového ústavu INSERT INTO Ustav (Nazev, Zkratka) VALUES ('Ustav elektroptiky', 'UEO')

Dotaz F. - Přejmenování ústavu UPDATE Ustav SET Nazev = 'noveJmeno' WHERE Id = id

Dotaz G. - Smazání ústavu DELETE Ustav WHERE Id = id

Za účelem větší přehlednosti a sjednocenní kódu od různých ORM bylo vytvořeno rozhranní IRepository, které se nachází v příloze B.1, můžeme ho však také nalézt v elektronické příloze v C# projektu ORM.Interfaces. Implementace těchto rozhaní pro jednotlivé ORM se nacházejí v přílohách B.2, B.3, B.4 a B.5. Jsou také součástí elektronické přílohy.

30 8.1.4 Výkonnost Výkonnostní testování bylo prováděno na dotazech A, B, C a D (viz kapitola 8.1.3) a dále komplexní příklad na změnu dat, který kombinuje dotazy E, F a G tj. dojde k vytvoření nového ústavu, následně jeho přejmenování, a poté smazání. Testování probíhalo s využitím Microsoft SQL 2012 Express a .NETu ve verzi 4.5. U všech testovaných ORM byla použita jejich poslední verze.

8.2 LINQ to SQL

Součástí .NET 3.5 byl vedle LINQ to Object, LINQ to XML a jiných implementací LINQu také LINQ to SQL, který umožňuje pracovat s Microsoft SQL databázemi pomocí LINQu. V dnešní době je již vývoj LINQ to SQL uzavřený, Microsoft se svoji pozornost rozhodl soustředit na vývoj jiného ORM a to konkrétně Entity frameworku.

8.2.1 Cena LINQ to SQL je od verze 3.5 součástí .NETu, a je proto zdarma.

8.2.2 Dokumentace Vzhledek k faktu, že LINQ to SQL je součástí .NETu, tak oficiální dokumentace je součástí MSDN. Dokumentace na MSDN je velmi kvalitní, navíc je možné na internetu najít velmi mnoho článků a příkladů na toto téma.

8.2.3 Funkcionalita Microsoft původně zamýšlel LINQ to SQL použít pouze jako součást některých interních projektů. Z toho důvodu LINQ to SQL neoplývá bohatou funkcionalitou.

Tab. 8.1: Přehled funkcionality LINQ to SQL

Funkcionalita Podpora Mapování  Single table inheritance Ano  Class table inheritance Ne  Concrete table inheritance Ne  Single table aggregation Ano, pomocí tzv. Complex types  Foreign key mapping Ano  Association table mapping Ne Změna vzhledu objektů Názvy a modifikátory přístupu Designer Ano DTO objekty Ne Selft-tracking objekty Ne Code-first Ne

31 Model-first Ne Výčtové typy Ne Transakce Ano Uložené procedury Ano Vstavěné SQL funkce Ano Uživatelské SQL funkce Ano L1 Cachování Ne L2 Cachování Ne Vygenerování prázdné databáze Ano Lazy loading a eager loading Oba Více vláknové prostředí Není vláknově bezbečné Dynamické úpravy modelu Ne Události 2

8.2.4 Použití Visual Studio obsahuje grafický editor tzv. designer, který umožňuje práci s databázovými objekty a jejich mapování na objekty, viz Obr. 8.2.

.

Obr. 8.2: LINQ to SQL designer

Z databázových objektů viditelných v desingeru je přímo vygenerován zdrojový kód. U LINQ to SQL se oproti Entity frameworku nebo NHibernate nevyskytuje žádná mapovací vrstva. Nejdůležitější částí vygenerovaného kódu je tzv. data context, který funguje jako vstupní bod pro jakoukoli práci s databází. Použití LINQ to SQL je demonstrováno na několika srovnáních s SQL:

32 Dotaz A. - Jednoduchý dotaz nad všemi tabulkami: using (var db = new LinqDataContext()) { var ustavy = db.Ustavs; var predmety = db.Predmets; var ucitele = db.Ucitels; var ucitelePredmety = db.UcitelPredmets }

Dotaz B. - Dotaz s dvěmi podmínkami Existuje více způsobů implementace. Můžeme využít lambda výrazy a extension metody: using (var db = new LinqDataContext()) { var ucitele = db.Ucitels .Where(x => x.Prijmeni.EndsWith("ová") || x.Plat > 40000); } další možností je použít LINQu: using (var db = new LinqDataContext()) {

var ucitele = from u in db.Ucitels where u.Prijmeni.EndsWith("ová") || u.Plat > 40000 select u; }

Dotaz D. - Dotaz nad vazbou M:N pomocí dvou inner join Zde opět máme více možností implementace. Můžeme použít SelectMany extension metodu: var predmetySVyucujicimi = database.Predmets.SelectMany(x => x.UcitelPredmets, (x, y) => new { Ucitel = y.Ucitel, Predmet = x }); a nebo specifikovat vazby manuálně: var predmetySVyucujicimi = from predmet in _database.Predmets join vazba in _database.UcitelPredmets on predmet.Id equals vazba.PredmetId join ucitel in _database.Ucitels on vazba.UcitelId equals ucitel.Id select new { Ucitel = ucitel, Predmet = predmet };

8.2.5 Výkonnost Výsledky testování jsou vedeny v tabulce 8.2:

Tab. 8.2: Výsledky výkonostního testování pro LINQ to SQL.

33 Počet dotazů 1000 10000 30000 50000 70000 100000 Dotaz A. [ms] 32 96 285 393 588 942 Dotaz B. [ms] 37 93 233 395 638 976 Dotaz C. [ms] 105 757 2268 3288 4958 7851 Dotaz D. [ms] 20 77 249 341 446 790

8.3 Entity framework

8.3.1 Cena Stejně tak jako LINQ to SQL, je EF součástí .NET frameworku, a je proto zdarma.

8.3.2 Dokumentace Hlavním zdrojem dokumentace je MSDN. Dokumentace na MSDN je velmi kvalitní. O EF navíc existuje mnoho knih a článků na internetu.

8.3.3 Funkcionalita Mapování a T4 šablony Oproti LINQ to SQL Entity framework negeneruje zdrojový kód z databáze, ale zavádí mapovací vrstvu, která sestává z šablony pro generování kódu a modelu databáze, viz Obr. 8.3.

Obr. 8.3: Srovnání základní funkcionality Entity frameworku a LINQ to SQL

Přidání mapovací vrstvy Entity frameworku umožňuje poskytnout více možností mapování. Mapovací vrstva mimo jiné sestává z modelu databáze T4 šablony. Úpravou

34 této šablony je možné docílit libovolné změny ve výsledných objektů. Součástí posledních verzí EF jsou dokonce i šablony pro tvorbu DTO (POCO) objektů a nebo tzv. self-tracking entities, viz. kapitola 8.1.1.. Je potřeba si však uvědomit, že změna T4 šablon může být často zdlouhová a neintuitivní. Jednou z výhod EF oproti LINQ to SQL je podpora Association table mapping, jak můžete vidět např. na Obr. 8.4, model EF již neobsahuje žádnou mapovací tabulku. Code-first a model-first Další výhodou mapovací vrsty je generování použití code-first a model-first. Jedná se však o řešení poměrně nová, která né vždy nabízí optimální řešení. Neumožňují např. vytvářet uložené procedury, a nebo neřeší migraci dat při změně kód (změna v kódu vede ke změně databáze). Události EF nabízí pouze dvě události pro práci s databázi:

 ObjectMaterialized vyvolaný po načtení entity z databáze a  SavingChanges před ukládáním dat

Cachování EF obsahuje vestavěnou podporu First-level cachování, neobsahuje však podporu Second-level cachování. Na internetu však lze najít mnoho návodů, jak ji implementovat.

35 Tab. 8.3: Přehled funkcionality Entity frameworku

Funkcionalita Podpora Mapování  Single table inheritance Ano  Class table inheritance Ano  Concrete table inheritance Ano  Single table aggregation Ano, pomocí tzv. Complex types  Foreign key mapping Ano  Association table mapping Ano Změna vzhledu objektů Libovolné změny pomocí T4 šablon Designer Ano DTO objekty Ano Self-tracking objekty Ano Code-first Ano Model-first Ano Výčtové typy Ano Transakce Ano Uložené procedury Ano Vstavěné SQL funkce Ano Uživatelské SQL funkce Ano L1 Cache Ano L2 Cache Ne Vygenerování prázdné databáze Ano Lazy loading a eager loading Oba Více vláknové prostředí Podpora asynchronních metod Dynamické úpravy schématu Ne Události 2

8.3.4 Použití Použití je velmi podobné jako u LINQ to SQL. I EF má svůj designer (viz. Obr. 8.4), který je standardní součástí Visual studia. Oproti LINQ to SQL však nabízí bohatší funkcionalitu.

36

Obr. 8.4: Entity framework designer Na dotazu D, můžete vidět, jak se s použitím EF zjednodušil výraz, jak oproti původnímu SQL tak i oproti LINQu: Dotaz D. - Dotaz nad vazbou M:N pomocí dvou inner join Pomocí SelectMany extension metody: database.Predmets.SelectMany(x => x.Ucitels, (x, y) => new { Predmet = x, Ucitel = y });

8.3.5 Výkonnost Výsledky testování jsou vedeny v tabulce 8.4:

Tab. 8.4: Výsledky výkonostního testování pro Entity Framework.

Počet dotazů 1000 10000 30000 50000 70000 100000 Dotaz A. [ms] 340 1042 2751 3450 4745 9008 Dotaz B. [ms] 342 1402 2241 3651 5038 7112 Dotaz C. [ms] 407 2031 7282 8947 11875 21487 Dotaz D. [ms] 70 434 1378 1855 2826 4277

8.4 NHibernate

Projekt Hibernet vznikl roku 2001. NHibernate (NH) vznikl roku 2003, kdy byl Hibernate migrován na platformu .NET. Do roku 2006 probíhal vývoj NH pod záštitou společnosti Red Hat. Od roku 2006 je NH dále vyvíjen open-source komunitou.

8.4.1 Cena NHibernate je šířen jako open-source, a je proto zdarma. Je však potřeba si uvědomit, že open-source řešení neobsahuje designer modelu a tříd, tak jak je tomu u EF nebo LINQ to SQL. Designer je možné dokoupit od třetích stran. Mezi

37 nejrozšířenější patří NHibernate designer od společnosti Mindspace, Entity developer od společnosti Devart nebo ORM LLBGen PRO od firmy Solution Design.

8.4.2 Dokumentace Oficiální dokumentace se nachází na stránkách projektu, viz. kapitola 7.1. Na trhu je celá řada publikací věnující se dané tematice. Zaměříme-li se však čistě na internetové zdroje, zjistíme, že dokumentace je často roztříštěná nebo se v ní obtížně vyhledává, což je částečně dáno i řadou různých možností, které NHibernate podporuje, a také řadou dalších nástrojů pro NH vyynutých třetími stranami.

8.4.3 Funkcionalita Co se funkcionality týče je NH jedním z nejbohatších ORM. Mapování NH podporuje všechny typy mapování zmíněné v kapitole 6. U asociačních vazeb navíc umožňuje volbu typu použité kolekce, což ani EF nenabízí. Mapování je možné několika různými způsoby a dokonce je kombinovat (EF opět něco takového neumožňuje). Mezi nejpoužívanější způsoby patří mapování pomocí XML souborů s koncovkou .hbm.xml a nebo definicí v kódu pomocí tzv. Fluent NHhibernate. Srovnání můžete vidět na následujícím příkladu: public class PredmetMap : ClassMap { Table("Predmet"); LazyLoad(); Id(x => x.Id) .GeneratedBy.Identity().Column("Id"); References(x => x.Ustav) .Column("UstavId"); Map(x => x.Nazev) .Column("Nazev").Not.Nullable(); Map(x => x.Zkratka) .Column("Zkratka").Not.Nullable(); Map(x => x.Pocetkreditu) .Column("PocetKreditu").Not.Nullable(); Map(x => x.Popis) .Column("Popis").Not.Nullable(); HasMany(x => x.UcitelPredmet).Inverse(); } }

38 Události NH ve srovnání s EF nabízí kolem dvaceti událostí pro práci s databází včetně následujících:

 Pre/Post-Load  Pre/Post-Delete  Pre/Post-Insert  Pre/Post-Update  Pre/Post-Flush

Cachování Součástí NH je podpora First-level i Second-level cachování. U Second-level cache lze dokonce nalézt více implementací.

Tab. 8.5: Přehled funkcionality NHibernate

Funkcionalita Podpora Mapování  Single table inheritance Ano  Class table inheritance Ano  Concrete table inheritance Ano  Single table aggregation Ano  Foreign key mapping Ano  Association table mapping Ano Změna vzhledu objektů Ano Designer Ne DTO objekty Ano Self-tracking objekty Ne Code-first Ano Model-first Ne Výčtové typy Ano Transakce Ano Uložené procedury Ano Vstavěné SQL funkce Ano Uživatelské SQL funkce Částečně (není možné vložit do DB objektů) L1 Cache Ano L2 Cache Ano Vygenerování prázdné databáze Ne Lazy loading a eager loading Oba Více vláknové prostředí Není Dynamické úpravy modelu Částečně Události 20

39 8.4.4 Použití Díky absenci designeru entit je tvorba tříd poměrně zdlouhová a je velmi snadné udělat chybu v mapování. Na druhou stranu tento přístup přináší větší flexibilitu v umístění mapování a databázových objektů, je např. možné oddělit mapování od databázových objektů. NHibernate stejně jako EF nebo LINQ to SQL také podporuje LINQ, a proto je většina databázových dotazů totožná jako u EF. Vedle LINQu však lze použít i starší způsob dotazování pomocí objektu s kritérii vyhledávání. Oba přístupy můžete vidět v následujícím příkladě, filtrujícím předměty, které začínají písmenem K: Dotaz s použitím LINQ: _session.Query() .Where(x => x.Nazev.StartsWith("K"));

Dotaz s použitím objektu kritérií: _session.CreateCriteria() .Add(Restrictions.Like("Nazev", "K%")) .List();

8.4.5 Výkonnost Výsledky testování jsou vedeny v tabulce 8.6:

Tab. 8.6: Výsledky výkonostního testování pro NHibernate

Počet dotazů 1000 10000 30000 50000 70000 100000 Dotaz A. [ms] 1040 1673 2859 3069 3799 6918 Dotaz B. [ms] 1256 1876 2569 3749 4029 6032 Dotaz C. [ms] 283 1613 5882 9495 10501 23530 Dotaz D. [ms] 125 467 1261 1934 2612 8994

8.5 OpenAccess

8.5.1 Cena Telerik nabízí čtrnáctidenní trial verzi OpenAcces (OA) ORM zdarma. Není však možné zakoupit samostatný ORM, ale pouze celý balíček nástrojů pro vývojáře DevCraft, který mimo samotného ORM OpenAccess zahrnuje následující:  UI komponenty pro WPF, WinForms, Silverlight, ASP.NET, Sharepoint atd.,  Nástroje pro unit tesování jako např. JustMock a Testing framework,  Profiler JustTrace,  Decompiler JustDecompile,  Plugin do Visual Studia JustCode.

40 Balíček se vyskytuje ve dvou variantách: Complete za $1499 a Ultimate za $1999, který má rozšířenou uživatelskou podporu.

8.5.2 Dokumentace Hlavním zdrojem dokumentace jsou oficiální stránky projektu a společnosti Telerik. Dokumentace je dostačující, ale místy popisuje starší verzi. OpenAccess není zdaleka tak rozšířen jako EF nebo NH, a tudíž neexistuje mnoho internetových zdrojů zabývajících se tímto ORM. Knihy o tomto ORM nejsou žádné. Nedostatek dokumentace je však kompenzován online podporou

8.5.3 Funkcionalita OpenAccess poskytuje bohatou funkcionalitu v mnoha ohledech velmi podobnou EF. Telerik navíc zveřejňuje nové verze každého čtvrt roku. Migrace z EF nebo LINQ to SQL OA je schopen převést database-first modelů z EF nebo LINQ to SQL. Takovouto vlastností neoplývá žádný jiný z srovnávaných ORM. WCF a ASP.NET Web API Další vlastností, kterou oplývá pouze OA, je schopnost vygenerovat nejenom databázové objekty, ale také webové služby WCF nebo Web API a to pomocí jednoduchého průvodce.

Obr. 8.5: OpenAccess průvodce k vytvoření webových služeb

41 Události OA poskytuje osm událostí pro databázové změny:  Added  Adding  Changed  Changing  Refreshed  Refreshing  Removed  Removing

Přehled základní funkcionality je vidět v tabulce 8.7.

Tab. 8.7: Přehled funkcionality OpenAcces

Funkcionalita Podpora Mapování  Single table inheritance Ano  Class table inheritance Ano  Concrete table inheritance Ano  Single table aggregation Ano  Foreign key mapping Ano  Association table mapping Ano Změna vzhledu objektů Ano Designer Ano DTO objekty Ano Self-tracking objekty Ne Code-first Ano Model-first Ano Výčtové typy Částečně Transakce Ano Uložené procedury Ano Vstavěné SQL funkce Ano Uživatelské SQL funkce Ano L1 Cache Ano L2 Cache Ano Vygenerování prázdné databáze Pomocí průvodce Lazy loading a eager loading Oba Více vláknové prostředí Není Dynamické úpravy modeluS Přidání sloupců Události 8

42 8.5.4 Použití OA nabízí velmi podobný designer jako EF. Jedinou možnou, jak přistupovat k datům v databázi, je LINQ, a tudíž je použití OA velmi podobné EF a LINQ to SQL.

8.5.5 Výkonnost Výsledky testování jsou vedeny v tabulce 8.8:

Tab. 8.8: Výsledky výkonnostního testování pro OpenAccess.

Počet dotazů 1000 10000 30000 50000 70000 100000 Dotaz A. [ms] 1822 1935 2682 2453 2568 3680 Dotaz B. [ms] 1991 2209 2502 2604 3676 4238 Dotaz C. [ms] 543 3777 14723 22938 30289 42212 Dotaz D. [ms] 104 144 383 1031 1842 5205

8.6 LLBLGen Pro

LLNLGen Pro je řešení of firmy Solutions Design . Samotné řešení sestává ze dvou částí:  Visual Designer a  framework pro objektově-relační mapování.

Designer slouží k tvorbě modelu entit i doménového modelu a generování zdrojového kódu pro různé ORM nejenom řešení od firmy Solutions Design.

8.6.1 Cena LLBLGen Pro je možne dostupný v následujících třech verzích:  Lite: Verze je zdarma, avšak umožňuje práci maximálně s osmi entitami.  Standard: Cena za licenci pro jednoho uživatele je 299€ bez daně. Při nákupu 21 a více licencí je 239€ bez daně.  Plus: U této varianty je cena o 99€ vyšší než-li u varianty Standard. K samotnému ORM zákazník dostane navíc nástroj ORM Profiler sloužící k sledování dotazů jdoucích do databáze.

U verze Standard i Plus Solutions Design garantuje aktualizace zdarma a slevu na nové verze. V ceně je zahrnuta i podpora a řešení případných bugů.

8.6.2 Dokumentace Dokumentace je dostupná od verze 2.6 online na adrese http://www.llblgen.com/documentation. Zejména od verze 4.0 začíná být dokumentace velmi podrobná a kvalitní. Designer navíc sám poskyuje dokumentaci k typickým

43 scénářům použití. Na webu výrobce je přístupné fórum, které obsahuje více jak sto tisíc příspěvků. Součástí instalace je řada praktických ukázek použití LLBLGen Pro. Jedinou velkou nevýhodou je, že literatura v tištěné podobě k tomuto produktu prakticky neexistuje. K placené verzi Pro Solutions Design nabízí podporu na fóru a po telefonu.

8.6.3 Funkcionalita Podpora více ORM Designer umožňuje generování nejenom entit pro LLBLGen ale také i pro LINQ to SQL, Entity Framework i NHibernate. Umožňuje tím častečný přechod mez těmito oddělenými technologiemi. Model-first a database-first LLBLGen pro nenabízí podporu Code-first přístupu, avšak má podporu Database- first a Model-first.. Podpora více verzí .NET frameworku Jednou z dalších vlastností tohoto ORM, která se nevyskytuje u ostatních srovnávaných ORM, je podpora více verzí .NET frameworku. Např. Oproti EF LLBLGen i v poslední verzi umožňuje vygenerovat entity pro různé verze .NETu a to konkrétně: 3.0, 3.5, .4.0 a 4.5, čímž se tento nástroj jeví jako vhodný pro zařízení, která mají starší verzi .NETu nebo operačního systému, jakou jsou různá embedded zařízení apod. Události LLBLGen poskytuje šest událostí pro databázové změny a práci s vygenerovanými entitami:  Initializing  Initialized  EntityRemoving  EntityRemoved  EntityAdding  EntityAdded

Přehled funkcionality LLBLGen Pro je uvededen v tabulce 8.9:

44 Tab. 8.9: Přehled funkcionality LLBLGen Pro

Funkcionalita Podpora Mapování  Single table inheritance Ano  Class table inheritance Ano  Concrete table inheritance Ano  Single table aggregation Ano  Foreign key mapping Ano  Association table mapping Ano Změna vzhledu objektů Ano Designer Ano DTO objekty Ne Self-tracking objekty Ne Code-first Ano Model-first Výčtové typy Ano Transakce Uložené procedury Ano Vestavěné SQL funkce Ano Uživatelské SQL funkce Ano L1 Cache Ano L2 Cache Ne Vygenerování prázdné databáze Ano Lazy loading a eager loading Oba Více vláknové prostředí Ano Dynamické úpravy modelu Ne Události 6

8.6.4 Výkonnost Výsledky testování jsou vedeny v tabulce 8.10:

Tab. 8.10: Výsledky výkonnostního testování pro OpenAccess.

Počet dotazů 1000 10000 30000 50000 70000 100000 Dotaz A. [ms] 289 341 540 936 1004 1767 Dotaz B. [ms] 1292 1455 1627 1712 1781 1826 Dotaz C. [ms] 423 3523 7052 8936 11094 18755 Dotaz D. [ms] 225 523 329 611 1939 4732

45 8.6.5 Použití Designer Designer ve verzi LLBLGen Pro se jeví jako velmi podařený a uživatelsky přívětivý, viz Obr. 8.6 a Obr. 8.7. Umožňuje velmi pestrou paletu mapování a nastavení vlastností tabulek i sloupců, což vychází z faktu, že podporuje více různých ORM. Avšak designer ve verzi Lite se oproti všem ostatním porovnávaným ORM liší a práce s ním je neintuitivní.

Obr. 8.6: Designer LLBLGen Pro - vztahy mezi entitami (převzato z [25])

Vygenerovaný kód LLBLGen se oproti ostatním ORM liší množstvím vygenerovaného zdrojového kódu. LLBLGen negeneruje pouze datový kontext a samotné objekty reprezentující třídy, ale dokonce i celé projekty obsahující řadu statických tříd, factory, pomocných

46 tříd apod.. Některé z vygenerovaných souborů jsou však prázdné, což znepřehledňuje jeho použití.

Obr. 8.7: Designer LLBLGen Pro - mapování sloupců na property (převzato z [25])

Zdrojový kód Dotazy do databáze lze provádět dvěmi různými způsoby:  QuerySpec – je potřeba vytvořit třídu QueryFactory, která umožňuje specifikovat podmínky vyhledávání:

var qf = new QueryFactory(); var q = qf.Customer.Where(CustomerFields.Country == "Germany"); var customers = new CustomerCollection(); customers.GetMulti(q);

 LINQ - standardní implementace datazového jazyka, bohužel nepodporuje ukládání změn do databáze:

47

LinqMetaData metaData = new LinqMetaData(); var q = from c in metaData.Customer where c.Country.Equals("Germany") select c;

8.7 Srovnání

8.7.1 Cena Co se týče ceny nejlépe vychází Entity framework a LINQ to SQL, neboť jsou zdarma. NHibernate je také zdarma, avšak postrádá designer entit, který je zpravidla nabízen ke koupi třetími společnostmi. LLBL Gen je dostupný v placené verzi a velmi omezené verzi zdarma. OpenAccess je dostupný pouze v placené verzi. Srovnání ceny jednotlivých ORM je uvedeno v tabulce 8.11.

Tab. 8.11: Srovnání ceny různých ORM

ORM Cena Poznámky LINQ to SQL zdarma Entity Framework zdarma NHibernate zdarma Chybějící designer OpenAccess Complete $1499 Verze Ultimate $1999 LLBLGen Pro $299 bez daně Verze Lite zdarma

8.7.2 Dokumentace Jak u LINQ to SQL, EF nebo NH můžeme najít celou řadu internetových zdrojů, knih i velmi kvalitní dokumentaci. OpenAccess ani LLBLGen takto bohatou dokumentací nedisponuje, avšak poskytují online i telefonní podporu, kterou ani jeden z předchozích ORM nenabízí.

8.7.3 Funkcionalita Jak je vidět, zdaleka nejméně funkcionality má LINQ to SQL. U ostatních ORM můžeme najít jak klady tak výhody. NHibernate poskytuje nejbohatší mapovací funkcionalitu. OpenAccess naopak má nejvíce business a enterprise orientovaných funkci jako je Second-level caching a generování webových služeb.

48 Srovnání funkcionality jednotlivých ORM je uvedeno v tabulce 8.12:

Tab. 8.12: Srovnání funkcionality různých ORM

Funkcionalita LINQ Entity NHibernate OpenAccess LLBLGen to SQL Framework Mapování  Single table Ano Ano Ano Ano Ano inheritance  Class table Ne Ano Ano Ano Ano inheritance  Concrete table Ne Ano Ano Ano Ano inheritance  Single table Ano Ano Ano Ano Ano aggregation  Foreign key Ano Ano Ano Ano Ano mapping  Association Ne Ano Ano Ano Ano table mapping Změna vzhledu Částečně Ano (T4) Ano Ano Ano objektů Designer Ano Ano Ne Ano Ano DTO objekty Ne Ano Ano Ano Ne Self-tracking objekty Ne Ano Ne Ne Ne Code-first Ne Ano Ano Ano Ano Model-first Ne Ano Ne Ano Výčtové typy Ne Ano Ano Částečně Ano Transakce Ano Ano Ano Ano Uložené procedury Ano Ano Ano Ano Ano Vestavěné SQL Ano Ano Ano Ano Ano funkce Uživatelské SQL Ano Ano Částečně Ano Ano funkce L1 Cache Ne Ano Ano Ano Ano L2 Cache Ne Ne Ano Ano Ne Vygenerování Ano Ano Ne Částečně Ano prázdné databáze Lazy loading a eager Oba Oba Oba Oba Oba loading Více vláknové Ne Částečně Ne Ne Ano prostředí Dynamické úpravy Ne Ne Částečně Částečně Ne modelu Události 2 2 20 8 6

49 8.7.4 Výkonnost Srovnání rychlosti ORM pro různé dotazy můůžeme vidět na obrázcích 7.6 až 7.9. Ve všech testech dopadl nejrychleji LINQ to SQL, na druhém místě s velmi slušnými výsledky se umístil LLBLGen Pro. EF a NH často dosahují velmi podobných výsledků. EF se zdá být efektivnějším než-li NH pro 30000 a méně záznamů. OpenAccess vykazuje velmi dobrou efektivitu pro dotazy A a B, avšak je nejhorším pro dotaz C a pro dotaz D má pro 10000 záznamů neočekávanou ztrátu efektivity. Dotaz A 10000 9000 8000 7000 LINQ 6000 Čas [ms] 5000 EF 4000 NH 3000 2000 OpenAcces 1000 LLBLGen 0 1000 10000 30000 50000 70000 100000 Počet záznamů v tabulkách

Obr. 8.8: Výkonnostní srovnání ORM pro Dotaz A

Dotaz B 8000 7000 6000 5000 LINQ Čas [ms] 4000 EF 3000 NH 2000 OpenAcces 1000 LLBLGen 0 1000 10000 30000 50000 70000 100000 Počet řádků v tabulce

Obr. 8.9: Výkonnostní srovnání ORM pro Dotaz B

50 Dotaz C 45000 40000 35000 30000 25000 LINQ Čas [ms] 20000 EF 15000 10000 NH 5000 OpenAcces 0 LLBLGen

Počet záznamů

Obr. 8.10: Výkonnostní srovnání ORM pro Dotaz C

Dotaz D 10000 9000 8000 7000 6000 LINQ to SQL čas [ms] 5000 4000 EF 3000 NH 2000 1000 OpenAcces 0 LLBLGen

Počet řádků v tabulkách

Obr. 8.11: Výkonnostní srovnání ORM pro Dotaz D

51 Testování změn databázi (viz 8.1.4) dopadlo velmi vyrovnaně:

Tab. 8.13: Srovnání výkonnosti různých ORM pro Insert-Update-Delete

Počet databázových úprav 100 200 500 1000 1500 LINQ 1793 1952 5497 14056 26578 EF 1975 3749 7301 14467 19657 NH 2361 3621 6183 13450 19151 OpenAccces 2780 4384 6302 10613 16089 LLBLGen 1548 1788 4571 11012 18531

Insert - Update - Delete 30000

25000

20000 LINQ Čas [ms] 15000 EF

10000 NH OpenAccces 5000 LLBLGen 0 100 200 500 1000 1500 Počet úprav

Obr. 8.12: Výkonnostní srovnání ORM pro Inser-Update-Delete

8.7.5 Použití Tvorba databázových objektů a modelů je u LINQ to SQL, EF a OpenAccess díky designerům velmi podobná. NHibernate neobsahuje designer, a proto je tato fáze práce zdlouhavější než u předchozích tří. Všechny čtyři testované ORM umožňují práci pomocí LINQ, a tudíž je i výsledný kód všech velmi podobný. NHibernate však poskytuje i čistě objektové rozhraní pro tvorbu dotazů.

8.7.6 Vhodnost použití jednotlivých ORM LINQ to SQL je nejjednodušším, ale zároveň nejrychlejším z testovaných ORM. Jeho funkcionalita není dostačující na využití ve velkých aplikacích, avšak zcela postačuje pro malé projekty. OpenAccess nabízí nejvíce pokročilých funkcí, avšak je zároveň nejdražším testovaným ORM. Může najít uplatnění v rozsáhlejších komerčních aplikacích, kde je

52 vyžadována maximální rychlost vývoje a podpora ze strany dodatavele. NHibernate spolu s Entity frameworkem si jsou v mnoha ohledech velmi podobní: Jsou zdarma, májí podobnout efektivitu, rozsáhlou dokumentaci a řadu společných vlastností. Oba lze bez problému použít na aplikace jakéhokoli rozsahu. Entity framework má však v současnosti o něco málo lepší postavení - je součástí .NETu, obsahuje designer entit a stojí za ním Microsoft, který pravidelně přidává novou funkcionalitu. LLBLGen se jeví jakou vhodnou variantou pro projekty, které vyžadují jak bohatou funkcionalitu i rychlý přístup do databáze. LLBLGen není zadarmo, avšak jeho cena je oproti OpenAccessu čtvrtinová. Mezi jediné nevýhody LLBLGenu patří relativně slabá dokumentace a místy atypický designer, tudíž vývoj s tímto nástrojem může být pomalejší než-li např. s Entity frameworkem.

53 9 HROMADNÉ VKLÁDÁNÍ DAT

Součástí standardu SQL je i příkaz INSERT, který slouží k vložení řádku do tabulky. Použijeme-li příkaz INSERT, SŘBD se před vložením daného řádku snaží ověřit konzistenci dat pomocí cizích klíčů případně může provádět zamykání tabulek apod., což celý proces zpomaluje. Další nevýhodou příkazu INSERT je nutnost specifikovat pořadí vkládaných dat u tabulek s vazbami. Obě výše zmíněné nevýhody komplikují především proces importu dat např. z systémů třetích stran, CSV (Comma Separated Value) souborů, excelu, apod.. Z tohoto důvodu některé databázové systémy poskytují funkcionalitu k hromadnému importu a někdy i exportu dat.

9.1 Microsoft SQL Server

Microsoft SQL poskytuje širokou paletu způsobů na import dat. Mezi ty nejvýznamnější patří především tyto čtyři:  Nástroj Bcp.exe a BULK COPY,  T-SQL příkaz BULK INSERT,  T-SQL příkazy OPENDATASOURCE, OPENQUERY, OPENROWSET,  SQL Server Integration Services (SSIS) [23]

Všechny tyto možnosti jsou přistupné z nástroje Import and Export Wizard zabudované do Management studia.

9.1.1 Bulk Copy Program Bulk Copy Program (BCP) neboli bcp.exe je jednoduchá konzolová aplikace umožňující rychlý import a export dat ze souboru. Formát souborů vygenerovaný pomocí aplikace BCP může být použita kromě BCP samotné tak i T-SQL příkazem BULK INSERT a nebo pomocí (SSIS). [1]

9.1.2 Třída SqlBulkCopy Chceme-li hromadný import dat realizovat z .NET aplikace můžeme spustit proces programu bcp.exe, avšak .NET pro tyto případy od verze 2.0 obsahuje v jmeném prostoru System.Data.SqlClient třídu SqlBulkCopy.[1] Samotný import je proveden pomocí metody WriteToServer, data této metodě lze předat následujícími způsoby:  Pole DataRow[]  DataTable  Rozhraní IDataReader

Pokud bychom chtěli předávat data třídě SqlBulkCopy objektem typu DataRow[] nebo DataTable, znamenalo by to, že již všechny data musíme mít načteny v paměti, což se u větších souborů může projevit zvýšenými výkonostními nároky, oproti tomu

54 rozhraní IDataReader umožňuje postupné čtení a zpracování dat. Následující kód demostruje import data do jedné tabulky s použitím IDataReader: IDataReader reader = null; // Inicializace data readeru string connectionString = null; // Connection string databaze

using (var connection = new SqlConnection(connectionString)) { connection.Open();

var bulkCopy = new SqlBulkCopy(connection); bulkCopy.DestinationTableName = "DestinationTable";

bulkCopy.WriteToServer(reader); }

Abychom uskutečnili import se souboru musíme jeho obsah nejdříve načíst do objektu DataRow[], DataTable a nebo IDataRow.

9.2 SQL Compact Edition

Oproti plnohodnotné verzi SQL CE nepodporuje žádný ze čtyř uvedených možností, avšak na internetu je možné najít řešení, které hromadné vkládání dat alespoň částečně umožňují.

9.2.1 SQL Compact Bulk Insert Library Jedním z řešení dostupných na internetu je projekt SQL Compact Bulk Insert Library (SCBIL) dostupný na http://sqlcebulkcopy.codeplex.com/. Klíčovou třídou celého tohoto řešení je třída SqlCeBulkCopy, která je inspirováná třídou SqlBulkCopy pro MSSQL a její použití je takřka totožné. [24] SCBIL dosahuje dosahuje větší rychlosti vkládání, než-li vkládání pomocí příkazu INSERT v kombinaci s třídou SqlCeCommand díky použití třídy SqlCeResultSet z jmeného prostoru System.Data.SqlServerCe. Zjednodušená verze kódu metody WriteToServer zajišťující vkládání dat je vidět v následujícím příkladě:

using (var cmd = new SqlCeCommand(...)) { cmd.CommandType = CommandType.TableDirect; using (var rs = cmd.ExecuteResultSet(ResultSetOptions.Updatable)) { ... while (adapter.Read()) { ... rs.Insert(rec); } } }

55

9.3 Hromadné vkládání dat a ORM

Žádný z pěti porovnávaných ORM nástrojů nijak výrazně nepodporuje hromadné vkládání dat. Cílem této kapitoly je nastínit rozšíření ORM, tak aby podporovaly hromadné vkládání dat do databází MSSQL a SQL CE z XML a CSV souborů za použití tříd SqlBulkCopy (viz 9.1.2) a SqlCeBulkCopy (viz 9.2.1). Ukázka možného rozšíření EF v kombinaci s MSSQL umožňující hromadné vkládání dat je uvedena v elektronické příloze v třídě ObjectContextExtensions.

9.3.1 Čtení data z souborů CSV Implementaci rozhranní IDataReader pro čtení ze souborů CSV je možné již najít v databázi zdrojových kódů www.codeproject.com, viz [1]. Zdrojový kód z databáze codeproject.com je dostupný pod licencí MIT License umožňující komerční i nekomerční použití i úpravy zdrojového kódu. Zdrojový kód této knihovny je možné nalézt v elektronické příloze v projektu s názvem CsvReader. Pokud každý řádek CSV souboru je možné použít třídu s názvem CachedCsvReader, která provádí cachování struktury CSV souboru. Import data z CSV souboru by poté mohl vypadat následovně: string csvCesta = @"c:\soubor.csv"; using (var database = new DatabaseEntities()) { var reader = new CsvReader(csvCesta, true); database.Ucitel.BulkInsert(reader); }

9.3.2 Čtení data z souborů XML Import pomocí DataTable Nejjednoduším způsobem předání dat z XML souboru do SqlBulkCopy je načtení dat z XML souboru do třídy DataTable pomocí metody ReadXml třídy DataSet:

string xmlCesta = @"c:\soubor.xml"; using (var database = new DatabaseEntities()) { var dataSet = new DataSet(); dataSet.ReadXml(xmlCesta, XmlReadMode.ReadSchema);

database.Ucitel.BulkInsert(dataSet.Tables[0]); }

Tento přístup opět není vhodný pro velké XML soubory.

56

10 UKÁZKOVÁ APLIKACE

Součástí elektronické přílohy je i velmi jednoduchá aplikace zobrazující data z databáze uvedené v kapitole 8.1.2. Aplikace se nachází ve složce Ukazkova aplikace také a sestává z dvou projektů:  DatabazeStudentu.DataAccess obsahující objekty Entity frameworku a  DatabazeStudentu.Presentation zobrazující data z databáze.

Účelem aplikace je demonstrovat možnost ORM Entity framework pracovat nad dvěmi různými databázemi. Pouho záměnou connection stringu s názvem DatabazeStudentuEntities v souboru app.config projektu DatabazeStudentu.Presentation je možné zajistit, že aplikace jako zdroj dat využívá buďto databázi Microsoft SQL Server a nebo SQL Server Compact Edition. Aby takovéto chování bylo umožňěno, musí projekt s EF obsahovat referenci na patřičné poskytovatele připojení k databázi a navíc je mít uvedené v app.config:

57 11 ZÁVĚR

Cílem této práce bylo poskytnout komplexní pohled o objektově-relační mapování. Kapitoly 1 až 5 poskytují teoretický úvod do problematiky. Kapitola 7 poskytuje přehled ORM nástrojů pro .NET a kapitola 8 tyto nástroje srovnává. Ukazuje se, že LINQ to SQL se jeví jako optimální řešení pro malé projekty nebo aplikace vyžadující rychlou komunikaci s databází. Naopak LLBLGen Pro je vhodný pro větší aplikace, kde je zapotřebí jak rychlá komunikace s databázi tak i bohatá funkcionalita. Entity framework je oproti LLBLGenu pomalejší, avšak je zcela zdarma a díky své velké rozšířenosti, bohaté dokumentaci a přímé integrací s Visual Studiem bude vývoj s jeho použitím s největší pravděpodobností probíhat rychleji, než-li s LLBLGenem. Entity framework je možné využít na všechny projekty, které nekladou velké nárok na odezvy z databáze. V kapitole 9 jsou navržena možnosti rozšíření ORM k hromadnému vkládání dat pomocí metody Bulk Copy. Součástí práce jsou také zdrojové kódy a testovací databáze, které lze použít jako příklady ve výuce.

58 LITERATURA

[1] LATTENBERG, I. Objektově orientované programování. Brno: Vysoké učení technické v Brně, 2012. s. 1-141. ISBN: 978-80-214-4447-8 (cs) [2] CONOLLY T., BEGG C., HOLOWCZAK R. Mistrovství databáze. Praha: Computer Press, a.s., 2009. ISBN: 978-80-251-2328-7 (cs) [3] NASH T., C# 2010. Brno: Computer Press, a.s., 2010, ISBN: 978-80-251-3034-6 [4] PERKINS, B. Working with NHibernate 3.0. Indianapolis, Ind: John Wiley. ISBN 978- 111-8104-606. [5] FOWLER, Martin. Patterns of enterprise application architecture. Boston: Addison- Wesley, c2003, xxiv, 533 p. ISBN 03-211-2742-0. [6] STACH, T. Objektovo-relačné mapovanie a jeho použitie na platforme .NET. Brno: Masarykova univerzita v Brně, 2013. Dostupné z WWW: http://is.muni.cz/th/173124/fi_m/ [7] [online], [cit. 10. 12. 2013], The object-relational Impedance Mismatch WWW: http://www.objectarchitects.de/ObjectArchitects/orpatterns/index.htm?ObjectLayer/object_ layer.htm [8] [online],[cit. 10. 12. 2013], The object-relational Impedance Mismatch WWW: http://www.agiledata.org/essays/impedanceMismatch.html [9] [online], [cit. 20. 12. 2013], MSDN, Using Delegates (C# Programming Guide) Dostupné z WWW: http://msdn.microsoft.com/en-us/library/ms173172.aspx [10] [online], [cit. 14. 12. 2013], Wikipedie. Dostupné z WWW: http://cs.wikipedia.org/wiki/Relační_databáze [11] [online] [cit. 14. 12. 2013], Wikipedie. Dostupné z WWW: http://cs.wikipedia.org/wiki/Index_(databáze) [12] [online], [cit. 14. 12. 2013], Wikipedie. Dostupné z WWW: http://cs.wikipedia.org/WIKI/Funkcionální_programování [13] [online], [cit. 14. 12. 2013], Wikipedie. Dostupné z WWW: http://en.wikipedia.org/wiki/.NET_Framework [14] [online], [cit. 14. 12. 2013], Wikipedie. Dostupné z WWW: http://en.wikipedia.org/wiki/List_of_CLI_languages [15] [online], [cit. 14. 12. 2013], Blog René Stein. Dostupné z WWW: http://blog.renestein.net/UML+O+Agregaci+Kompozici+A+Asociaci+A+Jako+Bonus+Sp ole%C4%8Densk%C3%A1+Aktualitka.aspx [16] MAKESH. Association, Aggregation, Composition. In: Geeks with blogs.NET [online]. 2006-11-03 [cit. 10. 12. 2013]. Dostupne z: http://geekswithblogs.net/mahesh/archive/2006/11/03/95960.aspx [17] [online], [cit. 14. 12. 2013], Wikipedie. Dostupné z WWW: http://en.wikipedia.org/wiki/List_of_object-relational_mapping_software [18] [online], [cit. 1. 1. 2013], OpenAccess ORM introduction. Dostupné z WWW: http://documentation.telerik.com/openaccess-orm/openaccess-orm-introduction [19] [online], [cit. 1.6. 2014], Differences Between SQL Server Compact and SQL Server,

59 Dostupné z WWW: http://technet.microsoft.com/en-us/library/bb896140(v=sql.110).aspx [20] [online], [cit. 1. 6. 2014], Wikipedie. Dostupné z WWW: http://en.wikipedia.org/wiki/SQL_Server_Compact [21] [online], [cit. 1. 6. 2014], DB-Engines Ranking of Relational DBMS, Dostupné z WWW: http://db-engines.com/en/ranking/relational+dbms [22] http://itknowledgeexchange.techtarget.com/eye-on-oracle/oracle-the-clear-leader-in-24- billion-rdbms-market/ [23] [online], [cit. 1. 6. 2014], Bulk Import and Export of Data (SQL Server), Dostupné z WWW: http://msdn.microsoft.com/en-us/library/ms175937.aspx [24] [online], [cit. 1. 6. 2014], SQLCe Bulk Copy, dospuné z WWW: [24] http://sqlcebulkcopy.codeplex.com/ [25] [online], [cit. 1. 6. 2014], LLBLGen, Dostupné z WWW: http://www.llblgen.com/

60

SEZNAM PŘÍLOH

A Databázové skripty 61 A.1 Skript k vytvoření testovací databáze ...... 61

B Zdrojový kód 63 B.1 IRepository rozhranní ...... 63 B.2 EntityFrameworkRepository ...... 64 B.3 LinqToSqlRepository ...... 66 B.4 NHibernateRepository ...... 69 B.5 OpenAccessRepository ...... 71 B.6 Aplikace na testování rychlosti ORM ...... 73

A DATABÁZOVÉ SKRIPTY

A.1 Skript k vytvoření testovací databáze

USE [Testing] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO SET ANSI_PADDING ON GO CREATE TABLE [dbo].[Predmet]( [Id] [int] IDENTITY(1,1) NOT NULL, [Nazev] [nvarchar](50) NOT NULL, [Zkratka] [char](4) NOT NULL, [PocetKreditu] [tinyint] NOT NULL, [Popis] [nvarchar](max) NOT NULL, [UstavId] [int] NOT NULL, CONSTRAINT [PK_Predmet] PRIMARY KEY CLUSTERED (

61 [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] TEXTIMAGE_ON [PRIMARY]

GO SET ANSI_PADDING OFF GO /****** Object: Table [dbo].[Ucitel] Script Date: 2.1.2014 19:09:38 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[Ucitel]( [Id] [int] IDENTITY(1,1) NOT NULL, [Krestni] [nvarchar](50) NOT NULL, [Prijmeni] [nvarchar](50) NOT NULL, [DatumNarozeni] [datetime] NOT NULL, [Plat] [decimal](18, 0) NOT NULL, [ProstredniJmeno] [nvarchar](50) NULL, [UstavId] [int] NOT NULL, [NadrizenyId] [int] NULL, CONSTRAINT [PK_Ucitel] 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]

GO /****** Object: Table [dbo].[UcitelPredmet] Script Date: 2.1.2014 19:09:38 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[UcitelPredmet]( [UcitelId] [int] NOT NULL, [PredmetId] [int] NOT NULL, CONSTRAINT [PK_UcitelPredmet] PRIMARY KEY CLUSTERED ( [UcitelId] ASC, [PredmetId] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY]

GO /****** Object: Table [dbo].[Ustav] Script Date: 2.1.2014 19:09:38 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[Ustav]( [Id] [int] IDENTITY(1,1) NOT NULL, [Nazev] [nvarchar](150) NOT NULL, [Zkratka] [nchar](4) NOT NULL, CONSTRAINT [PK_Katedra] PRIMARY KEY CLUSTERED ( [Id] ASC

62 )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY]

GO ALTER TABLE [dbo].[Predmet] WITH NOCHECK ADD CONSTRAINT [FK_Predmet_Katedra] FOREIGN KEY([UstavId]) REFERENCES [dbo].[Ustav] ([Id]) GO ALTER TABLE [dbo].[Predmet] CHECK CONSTRAINT [FK_Predmet_Katedra] GO ALTER TABLE [dbo].[Ucitel] WITH NOCHECK ADD CONSTRAINT [FK_Ucitel_Ucitel] FOREIGN KEY([NadrizenyId]) REFERENCES [dbo].[Ucitel] ([Id]) GO ALTER TABLE [dbo].[Ucitel] CHECK CONSTRAINT [FK_Ucitel_Ucitel] GO ALTER TABLE [dbo].[Ucitel] WITH NOCHECK ADD CONSTRAINT [FK_Ucitel_Ustav] FOREIGN KEY([UstavId]) REFERENCES [dbo].[Ustav] ([Id]) GO ALTER TABLE [dbo].[Ucitel] CHECK CONSTRAINT [FK_Ucitel_Ustav] GO ALTER TABLE [dbo].[UcitelPredmet] WITH NOCHECK ADD CONSTRAINT [FK_UcitelPredmet_Predmet] FOREIGN KEY([PredmetId]) REFERENCES [dbo].[Predmet] ([Id]) GO ALTER TABLE [dbo].[UcitelPredmet] CHECK CONSTRAINT [FK_UcitelPredmet_Predmet] GO ALTER TABLE [dbo].[UcitelPredmet] WITH NOCHECK ADD CONSTRAINT [FK_UcitelPredmet_Ucitel] FOREIGN KEY([UcitelId]) REFERENCES [dbo].[Ucitel] ([Id]) GO ALTER TABLE [dbo].[UcitelPredmet] CHECK CONSTRAINT [FK_UcitelPredmet_Ucitel] GO

B ZDROJOVÝ KÓD

B.1 IRepository rozhranní

public interface IRepository { ///

/// Select s podminkou /// /// IEnumerable VypisUciteleDlePodminky();

///

63 /// Jednoduchy select ///

/// IEnumerable VypisVsechnyUcitele();

///

/// Vypise ustav se vsemi jeho predmety. /// Vazba 1:N - inner join /// IEnumerable VypisUstavySPredmety();

///

/// Vypise informace o predmetech a jejich vyucujicich /// Vazba M:N /// IEnumerable VypisPredmetySVyucujicimi();

///

/// Vytvori novy ustav /// /// Vraci ID nove vznikleho ustavu int NovyUstav(string nazev, string zkratka);

///

/// Zmeni jmeno existujiciho ustavu /// /// ID existujiciho ustavu, kteremu se ma zmeni jmeno /// Nove jmeno ustavu void ZmenJmenoUstavu(int id, string noveJmeno);

///

/// Smaze ustav /// /// ID ustavu, ktery se ma smazat void SmazUstav(int id);

B.2 EntityFrameworkRepository

using System; using System.Collections.Generic; using System.Linq; using ORM.Interfaces; namespace ORM.EntityFramework { public class EntityFrameworkRepository : IRepository { private readonly DatabaseEntities _database = new DatabaseEntities();

public IEnumerable VypisUstavySPredmety() { // SQL // SELECT * FROM Ustav AS U // INNER JOIN Predmet AS P ON P.UstavId = U.Id

64

// Ukazka 1 return _database.Predmet.Select(x => new { Predmet = x, Ustav = x.Ustav }); }

///

/// Vypise informace o predmetech a jejich vyucujicich /// Vazba M:N /// public IEnumerable VypisPredmetySVyucujicimi() { // SQL // SELECT U.Id as UcitelId, U.Kresni as UcitelKrestni, U.Prijmeni as UcitelPrijmeni, // P.Id as PredmetId, P.Nazev as NazevPredmetu, P.Zkratka as ZkratkaPredmetu // FROM Ucitel AS U // INNER JOIN UcitelPredmet AS UP ON U.Id = UP.UcitelId // INNER JOIN Predmet AS P ON UP.PredmetId = P.Id

// S pouzitim SelectMany return _database.Predmet.SelectMany(x => x.Ucitele, (x, y) => new { Ucitel = y, Predmet = x }); }

public int NovyUstav(string nazev, string zkratka) { // INSERT INTO Ustav (Nazev) VALUES ('Ustav elektroptiky')

var novyUstav = new Ustav { Nazev = nazev, // Napr. Ustav elektrooptiky Zkratka = zkratka };

_database.Ustav.Add(novyUstav);

try { _database.SaveChanges(); } catch (Exception e) { Console.WriteLine(e.Message); }

return novyUstav.Id; }

public void ZmenJmenoUstavu(int id, string noveJmeno)

65 {

// UPDATE Ustav // SET Nazev = 'noveJmeno' // WHERE Id = id

var ustav = _database.Ustav.First(x => x.Id == id); ustav.Nazev = noveJmeno;

try { _database.SaveChanges(); } catch (Exception e) { Console.WriteLine(e.Message); } }

public void SmazUstav(int id) { // DELETE Ustav WHERE Id = id

var ustavKeSmazani = _database.Ustav.First(x => x.Id == id); _database.Ustav.Remove(ustavKeSmazani);

try { _database.SaveChanges(); } catch (Exception e) { Console.WriteLine(e.Message); } }

public IEnumerable VypisVsechnyUcitele() { return _database.Ucitel; }

public IEnumerable VypisUciteleDlePodminky() { return _database.Ucitel .Where(x => x.Prijmeni.Contains("ha") || x.Plat > 100000); } } }

B.3 LinqToSqlRepository

66 using System; using System.Collections.Generic; using System.Linq; using ORM.Interfaces; namespace ORM.LinqToSQL { public class LinqToSqlRepository : IRepository, IDisposable { private readonly LinqToSqlDataContext _database = new LinqToSqlDataContext();

public IEnumerable VypisVsechnyUcitele() { return _database.Ucitels; }

public IEnumerable VypisUstavySPredmety() { // SQL // SELECT * FROM Ustav AS U // INNER JOIN Predmet AS P ON P.UstavId = U.Id

// Ukazka 1 return _database.Predmets.Select(x => new { UstavId = x.Ustav.Id, NazevUstavu = x.Ustav.Nazev, PredmetId = x.Id, NazevPredmetu = x.Nazev, ZkratkaPredmetu = x.Zkratka, PocetKreditu = x.PocetKreditu, PopisPredmetu = x.Popis }); }

///

/// Vypise informace o predmetech a jejich vyucujicich /// Vazba M:N /// public IEnumerable VypisPredmetySVyucujicimi() { // SQL // SELECT U.Id as UcitelId, U.Kresni as UcitelKrestni, U.Prijmeni as UcitelPrijmeni, // P.Id as PredmetId, P.Nazev as NazevPredmetu, P.Zkratka as ZkratkaPredmetu // FROM Ucitel AS U // INNER JOIN UcitelPredmet AS UP ON U.Id = UP.UcitelId // INNER JOIN Predmet AS P ON UP.PredmetId = P.Id

//// S pouzitim dvou joinu return from predmet in _database.Predmets join vazba in _database.UcitelPredmets on predmet.Id equals vazba.PredmetId join ucitel in _database.Ucitels on vazba.UcitelId equals ucitel.Id select new

67 { UcitelId = ucitel.Id, UcitelKrestni = ucitel.Krestni, UcitelPrijmeni = ucitel.Prijmeni, PredmetId = predmet.Id, NazevPredmetu = predmet.Nazev, ZkratkaPredmetu = predmet.Zkratka }; } public int NovyUstav(string nazev, string zkratka) { // INSERT INTO Ustav (Nazev) VALUES ('Ustav elektroptiky')

var novyUstav = new Ustav { Nazev = nazev, // Napr. Ustav elektrooptiky Zkratka = zkratka };

_database.Ustavs.InsertOnSubmit(novyUstav);

try { _database.SubmitChanges(); } catch (Exception e) { Console.WriteLine(e.Message); }

return novyUstav.Id; } public void ZmenJmenoUstavu(int id, string noveJmeno) {

// UPDATE Ustav // SET Nazev = 'noveJmeno' // WHERE Id = id

var ustav = _database.Ustavs.First(x => x.Id == id); ustav.Nazev = noveJmeno;

try { _database.SubmitChanges(); } catch (Exception e) { Console.WriteLine(e.Message); } } public void SmazUstav(int id) { // DELETE Ustav WHERE Id = id

var ustavKeSmazani = _database.Ustavs.First(x => x.Id == id); _database.Ustavs.DeleteOnSubmit(ustavKeSmazani);

68

try { _database.SubmitChanges(); } catch (Exception e) { Console.WriteLine(e.Message); } }

public void Dispose() { _database.Dispose(); }

public IEnumerable VypisUciteleDlePodminky() { return _database.Ucitels .Where(x => x.Prijmeni.Contains("ha") || x.Plat > 100000); } } }

B.4 NHibernateRepository

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using NHibernate; using NHibernate.Cfg; using NHibernate.Criterion; using NHibernate.Linq; using NHibernate.Linq.Util; using ORM.Interfaces; namespace ORM.NHibernate { public class NHibernateRepository : IRepository { private readonly Configuration _configuration; private readonly ISessionFactory _sessionFactory; private readonly ISession _session;

public NHibernateRepository() { _configuration = new Configuration() .Configure(); // Nacte data z app.config

_sessionFactory = _configuration.BuildSessionFactory();

69 _session = _sessionFactory.OpenSession(); }

public IEnumerable VypisVsechnyUcitele() { return _session.Query(); }

public IEnumerable VypisUstavySPredmety() { return _session.Query().Select(x => new { Predmet = x, Ustav = x.Ustav }); }

public IEnumerable VypisPredmetySVyucujicimi() { return from predmet in _session.Query() join vazba in _session.Query() on predmet.Id equals vazba.Predmetid join ucitel in _session.Query() on vazba.Ucitelid equals ucitel.Id select new { Predmet = predmet, Ucitel = ucitel };

}

public int NovyUstav(string nazev, string zkratka) { return (int)_session.Save(new Ustav { Nazev = nazev, Zkratka = zkratka }); }

public void ZmenJmenoUstavu(int id, string noveJmeno) { var ustav = _session.Query() .First(x => x.Id == id);

ustav.Nazev = noveJmeno;

_session.Update(ustav); }

public void SmazUstav(int id) { var ustav = _session.Query() .First(x => x.Id == id); _session.Delete(ustav); }

70 public IEnumerable VypisUciteleDlePodminky() { return _session.Query() .Where(x => x.Prijmeni.Contains("ha") || x.Plat > 100000); } } }

B.5 OpenAccessRepository

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using ORM.Interfaces; namespace ORM.OpenAccess { public class OpenAccessRepository : IRepository { private readonly TelerikEntitiesModel _database = new TelerikEntitiesModel(); public IEnumerable VypisUstavySPredmety() { // SQL // SELECT * FROM Ustav AS U // INNER JOIN Predmet AS P ON P.UstavId = U.Id

// Ukazka 1 return _database.Predmets.Select(x => new { Predmet = x, Ustav = x.Ustav }); }

///

/// Vypise informace o predmetech a jejich vyucujicich /// Vazba M:N /// public IEnumerable VypisPredmetySVyucujicimi() { // SQL // SELECT U.Id as UcitelId, U.Kresni as UcitelKrestni, U.Prijmeni as UcitelPrijmeni, // P.Id as PredmetId, P.Nazev as NazevPredmetu, P.Zkratka as ZkratkaPredmetu // FROM Ucitel AS U // INNER JOIN UcitelPredmet AS UP ON U.Id = UP.UcitelId // INNER JOIN Predmet AS P ON UP.PredmetId = P.Id

71 // S pouzitim SelectMany return _database.Predmets.SelectMany(x => x.Ucitels, (x, y) => new { Ucitel = y, Predmet = x }); } public int NovyUstav(string nazev, string zkratka) { // INSERT INTO Ustav (Nazev) VALUES ('Ustav elektroptiky')

var novyUstav = new Ustav { Nazev = nazev, // Napr. Ustav elektrooptiky Zkratka = zkratka };

_database.Add(novyUstav);

try { _database.SaveChanges(); } catch (Exception e) { Console.WriteLine(e.Message); }

return novyUstav.Id; } public void ZmenJmenoUstavu(int id, string noveJmeno) {

// UPDATE Ustav // SET Nazev = 'noveJmeno' // WHERE Id = id

var ustav = _database.Ustavs.First(x => x.Id == id); ustav.Nazev = noveJmeno;

try { _database.SaveChanges(); } catch (Exception e) { Console.WriteLine(e.Message); } } public void SmazUstav(int id) { // DELETE Ustav WHERE Id = id

var ustavKeSmazani = _database.Ustavs.First(x => x.Id == id); _database.Delete(ustavKeSmazani);

72

try { _database.SaveChanges(); } catch (Exception e) { Console.WriteLine(e.Message); } }

public IEnumerable VypisVsechnyUcitele() { return _database.Ucitels; }

public IEnumerable VypisUciteleDlePodminky() { return _database.Ucitels .Where(x => x.Prijmeni.Contains("ha") || x.Plat > 100000); } } }

B.6 Aplikace na testování rychlosti ORM

using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using ORM.EntityFramework; using ORM.Interfaces; using ORM.LinqToSQL; using ORM.NHibernate; using ORM.OpenAccess; namespace ORM.VykonostniTestovani { class Program { static void Main(string[] args) { var dictionary = new Dictionary { { "LINQ", new LinqToSqlRepository()}, { "EF", new EntityFrameworkRepository()}, { "NHibernate", new NHibernateRepository()}, { "OpenAccess", new OpenAccessRepository()} };

//IPriklad priklad = new LinqPriklad(); foreach (var pair in dictionary) { Console.WriteLine("{0}:", pair.Key);

73 var repository = pair.Value; var sw = new Stopwatch();

// Testovani dotazu sw.Start(); var array1 = repository.VypisVsechnyUcitele().ToArray(); sw.Stop(); array1 = null; Console.WriteLine("Dotaz A: {0}", sw.ElapsedMilliseconds); sw.Reset();

sw.Start(); var array4 = repository.VypisUciteleDlePodminky().ToArray(); sw.Stop(); array4 = null; Console.WriteLine("Dotaz B: {0}", sw.ElapsedMilliseconds); sw.Reset(); Console.WriteLine();

sw.Start(); var array2 = repository.VypisUstavySPredmety().ToArray(); sw.Stop(); array2 = null; Console.WriteLine("Dotaz C: {0}", sw.ElapsedMilliseconds); sw.Reset();

sw.Start(); var array3 = repository.VypisPredmetySVyucujicimi().ToArray(); sw.Stop(); array3 = null; Console.WriteLine("Dotaz D: {0}", sw.ElapsedMilliseconds); sw.Reset(); Console.WriteLine();

// Testovani zmen v databazi sw.Start(); for (int i = 0; i < 500; i++) { int id = repository.NovyUstav("Ustav optiky a optoelektroniky", "UOEO"); repository.ZmenJmenoUstavu(id, "Ustav optoelektroniky"); repository.SmazUstav(id); } sw.Stop(); Console.WriteLine("Databazove zmeny: {0}", sw.ElapsedMilliseconds);

}

Console.WriteLine("FINISHED!"); Console.Read();

} } }

74