IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 8 - LINQ ve VB.NET - Revoluce v dotazování

V předešlém cvičení, Řešené úlohy k 5.-7. lekci práce s kolekcemi ve VB.NET, jsme si procvičili nabyté zkušenosti z předchozích lekcí.

V dnešním VB .NET tutoriálu se zaměříme na technologii LINQ, která představuje soubor nástrojů pro dotazování se na data. Jedná se o velmi revoluční technologii, která práci s daty zjednodušuje a zobecňuje.

Motivace

Určitě jsme všichni doteď pracovali s různými typy kolekcí různým způsobem. Jinak jsme hledali prvek v poli, jinak jsme četli data z XML souboru a jinak bychom hledali uživatele v databázi. Představme si, kdyby existoval jeden unifikovaný způsob, jakým se na data dotazovat.

Kdybychom mohli ten samý dotaz spustit jak na obyčejném poli, tak na XML souboru nebo databázi. LINQ nám poskytuje přesně takový komfort. Jedná se o obrovskou abstrakci, která je vykoupena pouze zanedbatelným snížením výkonu a která vyhnala programování v VB do nových výšin.

LINQ jako jazyk

LINQ je poměrně sofistikovaná a rozsáhlá technologie. Její název pochází z anglického Language INtegrated Query. Jak název napovídá, jedná se o dotazovací jazyk, který je integrovaný přímo do syntaxe jazyka VB. Je tedy jeho součástí a to od VS 2008 a .NET frameworku 3.5. Od novějších verzí běží dokonce na více vláknech, což zvyšuje efektivitu této technologie.

LINQ je velmi podobný jazyku SQL a je to tedy jazyk deklarativní. Programu sdělíme, co hledáme a už nás moc nezajímá, jakým způsobem pro nás data opravdu vyhledá. Výhodou integrace LINQ do VB je syntaktická kontrola dotazů při překladu programu.

Udělejme si malý příklad, než půjdeme dál. Založíme si nový projekt, půjde o konzolovou aplikaci se jménem LINQ. Vytvoříme si jednoduché pole textových řetězců.

Dim jmena() as String = {"David", "Martin", "Dan", "Petr", "Vratislav", "Eliška"}

Nyní si pomocí LINQ dotazu z tohoto pole vybereme ty položky, jejichž délka je větší než 5 písmen. Do programu zapíšeme následující kód:

Dim dotaz As Object = From j In jmena Where (j.Length > 5) Select j

Dotaz nápadně připomíná SQL, ti kteří ho znají mají výhodu. Myslím, že SQL nad polem jste ještě nevolali, že? :) Hned si dotaz podrobně popíšeme, nejprve ale dokončeme náš program a to tím, že si výsledek dotazu vypíšeme do konzole:

Dim jmena() as String = {"David", "Martin", "Dan", "Petr", "Vratislav", "Eliška"}
Dim dotaz As Object = From j In jmena Where (j.Length > 5) Select j
For Each jmeno As String In dotaz ' výpis výsledku
    Console.WriteLine(jmeno)
Next
Console.ReadKey()

Výstup programu:

Konzolová aplikace
Martin
Vratislav
Eliška

Jak vypadá dotaz

Vraťme se k našemu dotazu, který vypadal takto:

Dim dotaz As Object = From j In jmena
                      Where (j.Length > 5)
                      Select j

Znalci SQL budou jistě překvapeni, že je dotaz pozpátku. Má to své opodstatnění, ke kterému dojdeme.

Nejprve určujeme odkud budeme data vybírat, slouží k tomu klíčové slovo From. Za From následuje proměnná, která bude ve zbytku dotazu reprezentovat prvek z kolekce. Dále následuje klíčové slovo In a samotná kolekce. Je to podobné jako u cyklu For Each. Dotazy lze psát na několik řádků, aby byly přehlednější. To oceníme zejména u těch složitějších.

Pro podmínkování můžeme uvést řádek s klíčovým slovem Where, za ním následuje podmínka. Podmínky v tomto případě píšeme úplně stejně, jako jsme to dělali doposud.

Na posledním řádku následuje klíčové slovo Select, pomocí kterého určíme co vybíráme. Zde vybíráme celý prvek z kolekce, tedy j. Stejně tak bychom ale mohli vybrat třeba jen jeho délku j.Length nebo cokoli jiného.

Klíčové slovo Object

Dotaz ukládáme do proměnné typu Object, s tímto typem jsme se ještě nesetkali a vlastně to ani datový typ není. Klíčové slovo Object nám umožňuje přenechat výběr datového typu na kompileru (rozumějme, že ho za nás přiřadí VB sám při překladu). Teoreticky bychom Object mohli použít i jindy, např. takto:

Dim s = "VB pozná že toto je string a dá proměnné s typ string"
Dim i = 10

Kód výše VB přeloží v podstatě na toto:

Dim s as String = "VB pozná že toto je string a dá proměnné s typ string"
Dim i as Integer = 10

Klíčové slovo Object tedy umožňuje určit datový typ až při překladu programu a vlastně nás od datového typu odstiňuje. V běžných programech by bylo Object na obtíž, jelikož specifikace typů má svůj smysl. Typy tedy budeme psát dále a v žádném případě je nenahrazujte slovem Object, jak se to někteří začátečníci zcela chybně naučí.

Klíčové slovo Object bylo zavedeno kvůli LINQ a to ze třech důvodů:

  • datové typy dotazů poměrně složité a bylo by komplikované je vždy explicitně specifikovat.
  • když změníme typ kolekce, změní se i typ dotazu, což by vyžadovalo zbytečnou editaci kódu a snížilo obecnost technologie.
  • s LINQem přichází tzv. anonymní typy, u kterých se bez var neobejdeme, brzy se k nim dostaneme.

 Object má své místo hlavně v dotazech a v běžném kódu by se neměl příliš vyskytovat, i když by se tam teoreticky dalo použít (ostatně pokud u definice proměnné neuvedeme typ, defaultně se přiřadí právě Object).

Obecně platí poučka, že Object můžeme použít v případě, že zjednoduší deklaraci a přitom je stále jasné jakého typu proměnná je. Ukažme si 4 příklady bez Object a s Object:

Dim a As Integer = 10
Dim slovniky As New List(Of Dictionary(Of String, String))()
Dim prazane As IOrderedQueryable(Of Uzivatel) = From u In db.Uzivatele Where u.Mesto = "Praha" Order By u.Jmenou
Dim b As Integer = a

Pomocí Object můžeme kód upravit takto:

Dim a As Object = 10
Dim slovniky As Object = New List(Of Dictionary(Of String, String))()
Dim prazane As Object = From u In db.Uzivatele Where u.Mesto = "Praha" Order By u.Jmenou
Dim b As Object = a

U první proměnné nám Object nepřinese žádné zjednodušení. U generického listu generických slovníků se jeho použití naopak vyplatí a jelikož z pravé strany přiřazení i vidíme jakého typu je proměnná slovniky, Object je zde i dobrou volbou. Stejně by ale bylo čistší napsat na něco podobného třídu, ukládat kolekce kolekcí je spíše špatná praktika. U LINQ dotazu je typ složitý a kdybychom např. odmazali Order By, změnil by se na IQueryable<Uzivatel>. Takto nemusíme nad typem přemýšlet a ani ho měnit spolu s dotazem. Poslední užití Object je odstrašující, vůbec z řádky nepoznáme co do proměnné b ukládáme.

Další častou chybou je, že si lidé myslí, že Object deklaruje proměnnou dynamického typu, tedy že do ní můžeme uložit co chceme. Není tomu tak, typ se napevno určí při překladu a během programu ho nelze měnit. Kód níže tedy nebude fungovat:

' tento kód nebude fungovat
Dim promenna As Object = "Teď tam je text"

promenna = 10

Program výše vytvoří proměnnou typu String a poté upadne, protože se do typu String snažíme uložit Integer.

Pod pokličkou

Jak že to celé funguje? Když se podíváme na začátek našeho zdrojového kódu, uvidíme, že obsahuje následující Imports:

Imports System.Linq

Ten je přednačtený u všech typů projektů. Zkusme ho zakomentovat, v tu chvíli nám Visual Studio podtrhne v dotazu proměnnou jmena. V posledních verzích VB se Linq připojuje automaticky a Import netřeba definovat. Našeptávač VS nám pak po napsání tečky za jméno proměnné jmena vypíše množství nových metod:

Kolekce a LINQ ve VB.NET

LINQ funguje pomocí tzv. providerů, těch je několik typů a je i možno si definovat vlastní. My nyní používáme LINQ To Objects, který je implementován právě ve jmenném prostoru System.Linq a který rozšíří obyčejné kolekce, jako jsou např. pole a listy o další metody navíc. Pod pokličkou jsou tedy rozšiřující metody.

Na obyčejném poli máme kvantum nových metod. Když VB vykonává LINQ dotaz, volá na pozadí na kolekci tyto metody. Ty jsou řešené přes lambda výrazy, které jsme již potkali v OOP kurzu.

Náš dotaz:

Dim dotaz As Object = From j In jmena
              Where (j.Length > 5)
                  Select j

tedy VB přežvýká a vygeneruje následující kód:

Dim dotaz As Object = jmena.Where(Function(j) j.Length > 5).[Select](Function(j) j)

Můžeme si vyzkoušet, že dotaz bude fungovat stejně. Máme možnost s LINQ pracovat i takto, ale pomocí SQL-like zápisu je to mnohem stravitelnější. Mimochodem, právě jsme si vysvětlili, proč je v dotazu nejprve Where a až poté Select. Data se musí nejprve najít metodou Where() a z výsledku se teprve poté označí, co nás zajímá metodou Select(). Důvodem je tedy posloupnost metod pod pokličkou technologie.

Na závěr si prozraďme, na co se v našem případě přeloží onen záhadný typ Object. Výsledný typ dotazu na našem poli je:

System.Linq.Enumerable.WhereArrayIterator(Of String)

Jelikož nemůžeme z hlavy vědět jaký typ LINQ zrovna vrátí (přesněji řečeno bychom od toho měli být odstíněni), byl zaveden typ Object, jak již bylo řečeno výše.

V příští lekci, LINQ ve VB.NET - Anonymní typy, se naučíme deklarovat anonymní typy.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 4x (352.6 kB)
Aplikace je včetně zdrojových kódů v jazyce VB

 

Předchozí článek
Řešené úlohy k 5.-7. lekci práce s kolekcemi ve VB.NET
Všechny články v sekci
Kolekce a LINQ ve VB.NET
Přeskočit článek
(nedoporučujeme)
LINQ ve VB.NET - Anonymní typy
Článek pro vás napsal Přemysl Šíma
Avatar
Uživatelské hodnocení:
Ještě nikdo nehodnotil, buď první!
APSima
Aktivity