Dnes již si těžko dovedu představit psát aplikaci pracující s databází bez Linq To SQL či bez Entity Frameworku. Přes všechny nevýhody, které mají (moc jich naštěstí není), se s nimi pracuje velmi pohodlně a zvyšují nám produktivitu.
Pokud bychom srovnali jejich funkce, z 90% umí to samé. Jak Linq To SQL, tak i EF mají každý pár funkcí, které ten druhý nemá.
Linq To SQL je mnohými považován za takovou technologii pro začátečníky, hračku, “parodii na ORM” a já nevím, co ještě. To, že podporuje pouze jeden model dědičnosti, a to zrovna ten, který se mi moc nelíbí (Table Per Hierarchy - celá hierarchie tříd je v jedné tabulce a používají se z ní jen některé sloupce). Entity Framework je ve své druhé verzi (označené fikaně jako verze 4, aby to bylo kompatibilní s verzí .NETu) je naproti tomu téměř všemi uznávaný nástroj, který má sice širší možnosti modelování (struktura entit v EF se nemusí krýt přesně se strukturou tabulek v databázi), ale pár věcí, které jsou v Linq To SQL samozřejmostí zase neumí a člověk se jenom ptá “proč sakra”.
Poslední měsíc a půl trávím veškerý svůj volný čas psaním CMS s názvem Uraeus. V databázi se všechno motá kolem tabulky Items, která má jeden sloupeček typu HierarchyID. Každá položka může mít různé atributy, oprávnění (která se dědí) atd., jsou tam různé content-types, každý typ má svou vlastní tabulku, např. Articles, Categories atd., přičemž jsou s položkou provázány vazbou 1:1 přes sloupec ItemId.
Celý obsah CMSka je jeden velký strom a většina dotazů na data je typu “vytáhni mi potomky této položky, vyfiltruj je a nastránkuj je”. Inu napsal jsem si funkci vracející tabulku (fuj to je hrozný překlad “table-valued function”), které předám ID kořenové položky a ona mi vrátí všechny její potomky. Funkce se v SQL totiž používají jako tabulky, dají se filtrovat, řadit, stránkovat atd. Vybrat z funkce můžete třeba SELECT * FROM [dbo].[MojeFunkceVracejiciTabulku](parametr, parametr) atd.
V Linq To SQL je tohle naprosto triviální – přidáte si do datových tříd funkci, zavoláte ji a dostanete IQueryable, nad nímž můžete dělat psí kusy. V okamžiku vyhodnocení na jeden dotaz dostanete vše, co potřebujete.
Když jsem projekt začal psát, použil jsem Entity Framework. V první verzi by mě neudivilo, kdyby neuměl zavolat table-valued function, když neuměl ani volat stored procedury, které vrací cokoliv, ale jenom ty, co vracejí nějakou entitu, pro níž existuje tabulka atd. V jeho druhé verzi jsem to nečekal, když to umí zavolat jakoukoliv stored proceduru, tak to snad bude umět zavolat jakoukoliv funkci. Ale ono ne.
Nemůžu použít místo table-valud funkce třeba view – ten neumí parametry. Mohl bych použít stored proceduru, ale s těmi je spousta administrativních problémů. Vzhledem k tomu, že používám dědičnost a chci číst entity typu Articles, Categories atd. (je jich asi 10 a budou přibývat), musel bych udělat aspoň jednu proceduru pro každý content-type, což se mi nelíbí. Nastránkovat položky ve stored proceduře jde taky, i když to tu proceduru dost zkomplikuje. Ale dobře, řekněme. Horší je to s filtrováním a řazením – napsat tolik stored procedur pro každé řazení a každé filtrování, které kdy budu potřebovat, není pěkné a co do rozšiřitelnosti to není rozhodně moc elegantní.
Problém stored procedur je totiž to, že vrací hotový výsledek, s nímž už nic neuděláte. Stránkovat a řadit to na klientovi (resp. webserveru) samozřejmě nepřichází v úvahu.
Prozatím jsem si tedy na začátku napsal jednoduchou třídu, které jsem nasypal, co chci a jak to chci vyfiltrovat a seřadit, a ona si vygenerovala SQL dotaz a spustila ho. Bohužel se ukázalo, že když potřebuji dělat nějaké agregace či joiny s dalšími entitami, tak to sice jde, ale opět se mi to přestalo líbit. Udělal jsem tedy v Subversion branch, smazal Entity Data Model a přidal Linq To Sql Classes.
Linq To SQL neumí dědičnost Table per Class, kterou používám – třídy Article, Category atd. dědí z Item, každá má svou tabulku. Vzhledem k tomu, že mám poměrně pěkně napsanou datovou vrstvu v projektu, moc to nevadilo – udělal jsem rozhraní, které všechny třídy spojuje, a bylo hotovo.
Jak typické - speciálně mně se musí zrovna stát, že potřebuju takovou sadu funkcí, kterou mi ani jeden ORM nenabídne. To je také důvod, proč jsem se včera večer naštval a dnes odpoledne jsem v jednom dost rozsáhlém projektu přemigroval z Entity Frameworku na Linq To SQL. Naštěstí to nedalo tolik práce, ale jestli mi ještě někdo řekne, že je Linq To Sql hračka a EF pořádný nástroj, asi mu odpovím “jak kdy!”. Pro můj konkrétní projekt je Linq To SQL menší zlo.
Nechápu ale, proč Microsoft vyvíjí dvě technologie – kdyby Linq To SQL zavrhnul a všechno, co umí navíc (ale tady opravdu zdůrazňuji všechno!!!) přidal i do Entity Frameworku, mohl by mít skvělý a funkcemi nabitý ORM místo dvou podobných, z nichž každý umí něco extra, ale žádný neumí všechno, co člověk může potřebovat najednou. Anebo ať Entity Framework naučí datový typ HierarchyId, když už si ho vymysleli.