8. diel - LINQ vo VB .NET - Revolúcia v dopytovaní
V minulej lekcii, Zásobník vo VB.NET , sme sa venovali kolekciu Zásobník.
V dnešnom VB .NET tutoriále sa zameriame na technológiu LINQ, ktorá predstavuje súbor nástrojov na dopytovanie sa na dáta. Ide o veľmi revolučnú technológiu, ktorá prácu s dátami zjednodušuje a zovšeobecňuje.
Motivácia
Určite sme všetci doteraz pracovali s rôznymi typmi kolekcií rôznym spôsobom. Inak sme hľadali prvok v poli, inak sme čítali dáta z XML súboru a inak by sme hľadali používateľa v databáze. Predstavme si, keby existoval jeden unifikovaný spôsob, akým sa na dáta pýtať.Keby sme mohli tú istú otázku spustiť ako na obyčajnom poli, tak na XML súbore alebo databáze. LINQ nám poskytuje presne taký komfort. Jedná sa o obrovskú abstrakciu, ktorá je vykúpená iba zanedbateľným znížením výkonu a ktorá vyhnala programovanie v VB do nových výšin.
LINQ ako jazyk
LINQ je pomerne sofistikovaná a rozsiahla technológia. Jej názov pochádza z anglického Language INtegrated Query. Ako názov napovedá, jedná sa o dotazovací jazyk, ktorý je integrovaný priamo do syntaxe jazyka VB. Je teda jeho súčasťou a to od VS 2008 a .NET frameworku 3.5. Od novších verzií beží dokonca na viacerých vláknach, čo zvyšuje efektivitu tejto technológie.LINQ je veľmi podobný jazyku SQL a je to teda jazyk deklaratívny. Programu oznámime, čo hľadáme a už nás veľmi nezaujíma, akým spôsobom pre nás dáta naozaj vyhľadá. Výhodou integrácie LINQ do VB je syntaktická kontrola otázok pri preklade programu.
Urobme si malý príklad, než pôjdeme ďalej. Založíme si nový projekt, pôjde o konzolovú aplikáciu s menom LINQ. Vytvoríme si jednoduché pole textových reťazcov.
Dim jmena() as String = {"David", "Martin", "Dan", "Peter", "Vroclav", "Eliška"}
Teraz si pomocou LINQ dotazu z tohto poľa vyberieme tie položky, ktorých dĺžka je väčšia ako 5 písmen. Do programu zapíšeme nasledujúci kód:
Dim dotaz As Object = From j In jmena Where (j.Length > 5) Select j
Otázka nápadne pripomína SQL, tí ktorí ho poznajú majú výhodu. Myslím, že SQL nad poľom ste ešte nevolali, že?:) Hneď si otázku podrobne popíšeme, najskôr ale dokončíme náš program a to tým, že si výsledok otázky vypíšeme do konzoly:
{VBNET_CONSOLE}
Dim jmena() as String = {"David", "Martin", "Dan", "Peter", "Vroclav", "Eliška"}
Dim dotaz() As String = 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()
{/VBNET_CONSOLE}
Výstup programu:
Konzolová aplikácia
Martin
Vroclav
Eliška
Ako vyzerá otázka
Vráťme sa k našej otázke, ktorá vyzerala takto:Dim dotaz As Object = From j In jmena Where (j.Length > 5) Select j
Znalci SQL budú určite prekvapení, že je otázka pospiatky. Má to svoje opodstatnenie, ku ktorému dôjdeme.
Najprv určujeme odkiaľ budeme dáta vyberať, slúži na to kľúčové
slovo From
. Za From
nasleduje
premenná, ktorá bude vo zvyšku dotazu reprezentovať
prvok z kolekcie. Ďalej nasleduje kľúčové slovo
In
a samotná kolekcia. Je to podobné ako pri cykle
For Each
. Otázky je možné písať na niekoľko riadkov, aby boli
prehľadnejšie. To oceníme najmä u tých zložitejších.
Pre opodmienkovanie môžeme uviesť riadok s kľúčovým slovom
Where
, za ním nasleduje podmienka. Podmienky v
tomto prípade píšeme úplne rovnako, ako sme to robili doteraz.
Na poslednom riadku nasleduje kľúčové slovo Select
, pomocou
ktorého určíme čo vyberáme. Tu vyberáme celý prvok z kolekcie, teda
j
. Rovnako tak by sme ale mohli vybrať napríklad len jeho dĺžku
j.Length
alebo čokoľvek iné.
Kľúčové slovo Object
Otázku ukladáme do premennej typu Object
, s týmto typom sme sa
ešte nestretli a vlastne to ani dátový typ nie je. Kľúčové slovo
Object
nám umožňuje prenechať výber dátového typu na
kompiléri (rozumajme, že ho za nás priradí VB sám pri preklade). Teoreticky
by sme Object
mohli použiť aj inokedy, napr. takto:
Dim s = "VB pozná že toto je string a dá premenné s typ string" Dim i = 10
Kód vyššie VB preloží v podstate na toto:
Dim s as String = "VB pozná že toto je string a dá premenné s typ string" Dim i as Integer = 10
Kľúčové slovo Object
teda umožňuje určiť
dátový typ až pri preklade programu a vlastne nás od
dátového typu odtieňuje. V bežných programoch by bolo Object
na obtiaž, pretože špecifikácia typov má svoj zmysel. Typy
teda budeme písať ďalej av žiadnom prípade ich nenahradzujte slovom
Object
, ako sa to niektorí začiatočníci úplne chybne
naučia.
Kľúčové slovo Object
bolo zavedené kvôli LINQ a to z troch
dôvodov:
- dátové typy dotazov pomerne zložité a bolo by komplikované ich vždy explicitne špecifikovať.
- keď zmeníme typ kolekcie, zmení sa aj typ dotazu, čo by vyžadovalo zbytočnú editáciu kódu a znížilo všeobecnosť technológie.
- s LINQom prichádzajú tzv. anonymné typy, pri ktorých sa bez varov nezaobídeme, čoskoro sa k nim dostaneme.
Object
má svoje miesto hlavne v
dotazoch av bežnom kóde by sa nemal príliš vyskytovať, aj
keď by sa tam teoreticky dalo použiť (napokon ak pri definícii premennej
neuvedieme typ, defaultne sa priradí práve Object
).
Všeobecne platí poučka, že Object
môžeme použiť v
prípade, že zjednoduší deklaráciu a pritom je stále jasné akého typu
premenná je. Ukážme si 4 príklady bez Object
as
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
Pomocou Object
môžeme kód upraviť 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
Pri prvej premennej nám Object
neprinesie žiadne
zjednodušenie. Pri generickom liste generických slovníkov sa jeho použitie
naopak oplatí a keďže z pravej strany priradenia aj vidíme akého typu je
premenná slovniky
, Object
je tu aj dobrou voľbou.
Rovnako by ale bolo čistejšie napísať na niečo podobné triedu, ukladať
kolekcie kolekcií je skôr zlá praktika. U LINQ dotazu je typ zložitý a keby
sme napr. odmazali Order By
, zmenil by sa na
IQueryable<Uzivatel>
. Takto nemusíme nad typom premýšľať
a ani ho meniť spolu s otázkou. Posledné použitie Object
je
odstrašujúce, vôbec z riadka nepoznáme čo do premennej b
ukladáme.
Ďalšou častou chybou je, že si ľudia myslia, že Object
deklaruje premennú dynamického typu, teda že do nej môžeme uložiť čo
chceme. Nie je tomu tak, typ sa napevno určí pri preklade a počas programu ho
nemožno meniť. Kód nižšie teda nebude fungovať:
' tento kód nebude fungovať Dim promenna As Object = "Teraz tam je text" promenna = 10
Program vyššie vytvorí premennú typu String
a
potom spadne, pretože sa do typu String
snažíme uložiť
Integer
.
Pod pokrievkou
Ako že to celé funguje? Keď sa pozrieme na začiatok nášho zdrojového kódu, uvidíme, že obsahuje nasledujúceImports
:
Imports System.Linq
Ten je prednačítaný pri všetkých typoch projektov. Skúsme ho
zakomentovať, v tej chvíli nám Visual Studio podčiarkne v dotaze premennú
jmena
. V posledných verziách VB sa Linq pripája automaticky a
Import netreba definovať. Našeptávač VS nám potom po napísaní bodky za
meno premennej jmena
vypíše množstvo nových metód:
LINQ funguje pomocou tzv. providerov, tých je niekoľko
typov a je aj možné si definovať vlastné. My teraz používame LINQ
To Objects, ktorý je implementovaný práve v mennom priestore
System.Linq
a ktorý rozšíri obyčajné kolekcie, ako sú napr.
polia a listy o ďalšie metódy navyše. Pod pokrievkou sú teda
rozširujúce metódy.
Na obyčajnom poli máme kvantum nových metód. Keď VB vykonáva LINQ dotaz, volá na pozadí na kolekciu tieto metódy. Tie sú riešené cez lambda výrazy, ktoré sme už stretli v OOP kurze.
Náš dotaz:
Dim dotaz As Object = From j In jmena Where (j.Length > 5) Select j
teda VB prežuje a vygeneruje nasledujúci kód:
{VBNET_CONSOLE}
Dim jmena() As String = {"David", "Martin", "Dan", "Petr", "Vratislav", "Eliska"}
Dim dotaz As Object = jmena.Where(Function(j) j.Length > 5).[Select](Function(j) j)
For Each jmeno As String In dotaz ' výpis výsledku
Console.WriteLine(jmeno)
Next
Console.ReadKey()
{/VBNET_CONSOLE}
Výstup:
Konzolová aplikácia
Martin
Vroclav
Eliská
Môžeme si vyskúšať, že otázka bude fungovať rovnako. Máme možnosť
s LINQ pracovať aj takto, ale pomocou SQL-like zápisu je to oveľa
stráviteľnejšie. Mimochodom, práve sme si vysvetlili, prečo je v otázke
najprv Where
a až potom Select
. Dáta sa musia najprv
nájsť metódou Where()
az výsledku sa až potom označí, čo
nás zaujíma metódou Select()
. Dôvodom je teda postupnosť
metód pod pokrievkou technológie.
Na záver si prezraďme, na čo sa v našom prípade preloží onen záhadný
typ Object
. Výsledný typ dotazu na našom poli je:
System.Linq.Enumerable.WhereArrayIterator(Of String)
Keďže nemôžeme z hlavy vedieť aký typ LINQ práve vráti (presnejšie
povedané by sme od toho mali byť odtienení), bol zavedený typ
Object
, ako už bolo povedané vyššie.
V budúcej lekcii, LINQ vo VB.NET - Anonymné typy , sa naučíme deklarovať anonymné typy.
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 4x (352.6 kB)
Aplikácia je vrátane zdrojových kódov v jazyku VB