Často se střetávám s dotazy na různé typy načítání – především s nástupem používání LINQu pro získávání dat z SQL databází – a jejich ne vždy přesné pochopení. Pokusím se tedy v tomto krátkém článku vysvětlit, jak se jednotlivé typy liší a na co (ne)jsou vhodné. Jejich názvy nebudu překládat, neboť podle mého názoru je pro vývojáře vhodné si osvojit původní termíny.
reklama
Často se střetávám s dotazy na různé typy načítání – především s nástupem používání LINQu pro získávání dat z SQL databází – a jejich ne vždy přesné pochopení. Pokusím se tedy v tomto krátkém článku vysvětlit, jak se jednotlivé typy liší a na co (ne)jsou vhodné. Jejich názvy nebudu překládat, neboť podle mého názoru je pro vývojáře vhodné si osvojit původní termíny.
Lazy loading
Začněme s typem, který je běžný v LINQ to SQL – lazy loading. Při tomto přístupu jsou data získávána tak, jak programátor přistupuje k proměnným a prochází grafem. Jako příklad uveďme:
foreach(Order o in customer.Orders)
{ ... }
a předpokládáme, že tento kód je zanořen ve vnějším cyklu, který prochází kolekci „Customers“. Ve chvíli, kdy program vstupuje to tohoto vnitřního cyklu, je úložiště (nejčastěji tedy relační databáze) dotázáno na vrácení všech „objednávek příslušejících zákazníkovi“. Výhodou tohoto přístupu je jednoduchost pro programátora. Nemusí se starat o to, zdali má data načtena atp. Na druhou stranu nemusí být vždy jasné, v jaké části programu skutečně k načtení dojde. Tento přistup je vhodný pro několik málo dotazů do třeba číselníků. Jakmile iterujeme přes větší kolekci a dotahujeme další údaje, plýtváme zbytečně prostředky a časem, neboť se dotazy do databáze posílají jednotlivě.
Eager loading
Pro tento případ je mnohem vhodnější eager loading. Data zde načteme „do foroty“ ihned při spuštění prvního příkazu. Např. v Entity Frameworku můžeme využít metody „Include“, která nám zajistí vytvoření spojení (join) tabulek a získání všech dat přímo.
foreach(Customer customer in entities.Customers.Include(„Orders“))
{
foreach(Order o in customer.Orders)
{ ... }
}
Díky tomu máme všechna data dostupná na klientovi a při procházení již není třeba dělat další dotazy do databáze. Není tedy těžké nahlédnout, že v případě procházení celé kolekce a přidružených objektů je tento přístup více než vhodný. Na druhou stranu je třeba velmi opatrně další tabulky přidávat, neboť je možné velmi jednoduše vytvořit „velký“ dotaz, který vrátí enormní množství dat a dojde k zahlcení linky nebo klienta.
Explicit loading
Lazy loading a eager loading šly tak trochu proti sobě. Naproti tomu explicit loading je pověstným cimrmanovským úkrokem stranou. Pokud se rozhodnete využívat tento postup řídíte si načítání dat sami, podobně jako u eager loading, nicméně získáváme pouze menší kousky dat podobně jako u lazy loading. Explicit loading je mj. standardní chování LINQ to Entities a Entity Frameworku. Jak to tedy funguje? Při procházení kolekce, v našem případě „Customers“, chceme-li dostat objednávky konkrétního (právě zpracovávaného) zákazníka, musíme před zpracováním této kolekce zavolat metodu „Load“.
foreach(Customer customer in entities.Customers)
{
if (!customer.Orders.IsLoaded)
customer.Orders.Load();
foreach(Order o in customer.Orders)
{ ... }
}
Zbytek zpracování se pak neliší. Všimněte si též, že díky schopnosti Entity Frameworku cacheovat načtené objekty není třeba data získávat znova, byla-li již jednou načtena (property IsLoaded). Výhodou tohoto postupu je jasná viditelnost, kdy k načítání dat dochází (resp. kdy se o to kód pokusí). Nevýhodou je naopak, při procházení velkých kolekcí, nutnost poslat velký počet dotazů (zde je vhodnější použít eager loading).
Z tohoto krátkého přehledu je vidět, že ani jeden přístup není ten nejlepší a na každé úkony se může hodit něco jiného. Důležité je vždy dobře rozmyslet postup a samozřejmě rozhodnutí ověřit testováním, aby byly zohledněny všechny aspekty (které nemusely být při rozhodování známy).