7. diel - LINQ v C # - revolúcia v dotazovanie
V minulej lekcii, Front a zásobník v C # .NET , sme sa venovali kolekciám front a zásobník. V dnešnom C# .NET tutoriálu sa zameriame na technológii LINQ, ktorá predstavuje súbor nástrojov pre dotazovanie sa na dáta. Jedná sa o veľmi revolučnú technológiu, ktorá prácu s akýmikoľvek dátami zjednodušuje a zovšeobecňuje.
Motivácia
Určite sme všetci doteraz pracovali s rôznymi typmi kolekciou 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. Predstavte si ale, keby existoval jeden unifikovaný spôsob, akým sa na dáta pýtať. Keby sme mohli ten istý dotaz spustiť ako na obyčajnom poli, tak na XML súboru alebo databázu. Asi tušíte, že LINQ nám poskytuje presne taký komfort. Ide o obrovskú abstrakcii, ktorá je vykúpená iba zanedbateľným znížením výkonu a ktorá vyhnala programovanie v C# do nových výšin.
LINQ ako jazyk
LINQ je pomerne sofistikovaná a rozsiahla technológie. 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 C #. Je teda jeho súčasťou a to od C# 3.0 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 C# je kontrola syntaxe dotazov pri preklade programu.
Urobme si malý príklad, ako pôjdeme ďalej. Založte si nový projekt, pôjde o konzolovú aplikáciu s menom LINQ. Vytvoríme si jednoduché poľa textových reťazcov.
string[] jmena = {"David", "Martin", "Dan", "Petr", "Vratislav", "Eliska"};
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íšte nasledujúci kód:
var dotaz = 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, najprv ale dokonca náš program a to tým, že si výsledok dotazu vypíšeme do konzoly:
// výpis výsledku foreach (string jmeno in dotaz) { Console.WriteLine(jmeno); } Console.ReadKey();
Výstup programu:
Ako vyzerá dotaz
Vráťme sa k nášmu dotazu, ktorý vyzeral takto:
var dotaz = from j in jmena where (j.Length > 5) select j;
Znalci SQL budú určite prekvapení, že je dotaz 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
dotaze reprezentovať prvok z kolekcie. Ďalej nasleduje kľúčové slovo
in
a samotná kolekcie. Je to podobné ako u cykle
foreach
. Otázky sa píšu na niekoľko riadkov, aby boli
prehľadnejšie. To oceníte najmä u tých zložitejších.
Pre opodmínkování 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 var
Otázka ukladáme do premennej typu var
, s týmto typom sme sa
ešte nestretli a vlastne to ani dátový typ nie je. Kľúčové slovo
var
nám umožňuje prenechať výber dátového typu na kompileru
(rozumejte, že ho za nás priradí C# sám pri preklade). Teoreticky by sme
var
mohli použiť aj inokedy, napr. Nasledovne:
var s = "C# pozná že toto je string a dá proměnné s typ string"; var i = 10;
Kód vyššie C# preloží v podstate na toto:
string s = "C# pozná že toto je string a dá proměnné s typ string"; int i = 10;
Kľúčové slovo var
teda umožňuje určiť typ údajov až
pri preklade programu a vlastne nás od dátového typu odtieňuje. V bežných
programoch by bolo var
naobtíž, pretože špecifikácia typov má
svoj zmysel. Typy teda budeme písať ďalej a v žiadnom prípade ich
nenahrádzajte slovom var
, ako sa to niektorí začiatočníci
úplne chybne naučí.
Kľúčové slovo var
bolo zavedené kvôli LINQ a to z troch
dôvodov:
- Po prvé sú dátové typy otázok pomerne zložité a bolo by komplikované je vždy explicitne špecifikovať.
- Po druhé keď zmeníme typ kolekcie, zmení sa aj typ otázky, čo by vyžadovalo zbytočnú editáciu kódu a znížilo všeobecnosť technológie.
- Po tretie s LINQ prichádza tzv. Anonymné typy, u ktorých sa bez var nezaobídeme, čoskoro sa k nim dostaneme.
Čo si pamätajte je, že var
má svoje miesto hlavne v
dotazoch a v bežnom kóde by sa nemal príliš vyskytovať, aj keď by
sa tam teoreticky dalo použiť.
Všeobecne platí poučka, že var
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 var
as var
:
int a = 10; List<Dictionary<string, string>> slovniky = new List<Dictionary<string, string>>(); IOrderedQueryable<Uzivatel> prazane = from u in db.Uzivatele where u.Mesto == "Praha" orderby u.Jmeno select u; int b = a;
Pomocou var
môžeme kód upraviť takto:
var a = 10; var slovniky = new List<Dictionary<string, string>>(); var prazane = from u in db.Uzivatele where u.Mesto == "Praha" orderby u.Jmeno select u; var b = a;
U prvej premennej nám var
neprinesie žiadne zjednodušenie. U
generického listu generických slovníkov sa jeho použitia naopak oplatí a
keďže z pravej strany priradenie aj vidíme akého typu je premenná
slovniky
, var
je tu aj dobrou voľbou. Rovnako by ale
bolo čistejšie napísať na niečo podobné triedu, ukladať kolekcia
kolekcií je skôr zlá praktika. U LINQ dotazu je typ zložitý a keby sme
napr. Zmazali orderby
, zmenil by sa na
IQueryable<Uzivatel>
. Takto nemusíme nad typom premýšľať
a ani ho meniť spolu s otázkou. Posledný použitia var
je
odstrašujúci, vôbec z riadky nespoznáme čo do premennej b
ukladáme.
Ďalšou častou chybou je, že si ľudia myslia, že var
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 fungovat var promenna = "Teď tam je text"; promenna = 10; // Teď tam je číslo
Program vyššie vytvorí premennú typu string
a potom spadne,
pretože sa do typu string
snažíme uložiť int
.
Pod pokrievkou
Ako že to celé funguje? Keď sa pozriete na začiatok vášho zdrojového
kódu, uvidíte, že obsahuje nasledujúce using
:
using System.Linq;
Ten je přednačtený u všetkých typov projektov. Skúsme ho zakomentovat,
v tej chvíli nám Visual Studio podčiarkne v dotaze premennú
jmena
.
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.
Skúsme si teraz (ešte sa zakomentovaným using
em) vyvolať
ponuku metód na našom poli. Napíšme jmena.
(jmena
a bodku)., Aby sme vyvolali zoznam metód na poli:
Teraz riadok opäť odkomentujte a urobme to isté znova:
Na obyčajnom poli máme naraz kvantum nových metód. Keď C# 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áš otázka:
var dotaz = from j in jmena where (j.Length > 5) select j;
teda C# prežuje a vygeneruje nasledujúci kód:
var dotaz = jmena.Where(j => j.Length > 5).Select(j => j);
Môžete 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 dotaze
najprv where
a až potom select
. Dáta sa musí 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žia onen záhadný
typ var
. Výsledný typ otázky na našom poli je:
System.Linq.Enumerable.WhereArrayIterator<string>
Keďže nemôžeme z hlavy vedieť aký typ LINQ zrovna vráti (presnejšie
povedané by sme od toho mali byť odtienené), bol zavedený typ
var
, ako už bolo povedané vyššie.
V budúcej lekcii, LINQ provideri, anonymné typy, radenie a zoskupovanie , budeme pokračovať v dopytovania.
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é 579x (23.17 kB)
Aplikácia je vrátane zdrojových kódov v jazyku C#