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:
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