_LinqOperators projekt

Ebben a projekben az egyes operátorokat vesszük sorra szemléletes példákkal. Az első a hagyományos select operátor, mellyel projekciót határozzuk meg. Az esetek többségében azonban általában több táblából projektálunk adatokat, ilyenkor használatos a selectmany operátor.

Select és SelectMany operátor

http://msdn.microsoft.com/en-us/library/system.linq.enumerable.select(v=vs.110).aspx

http://msdn.microsoft.com/en-us/library/system.linq.enumerable.selectmany(v=vs.110).aspx

A példákaként alkalmazott kódok előtt az alábbi kódrészletben hozzuk létre a context-et, és az output ablakba kiíró DebugWriter-t.

9-22. ábra -

kepek3/14fejezet-9-22.jpg


Ezek után a példakód az alábbi:

Mint látszik, az egyes kódolási technikákkal létrehozott halmazok azonos tartalmúak.

9-23. ábra -

kepek3/14fejezet-9-23.jpg


Join és JoinGroup operátor csoport

A join kissén pongyolán azt a lehetőséget biztosítja, amit az előző példában már tulajdonképpen megvalósítottunk, nevezetesen, hogy két táblát összekapcsol az alapján, hogy az egyik valamelyik mezőjének tartalma megegyezik a másik valamelyik mezőjének tartalmával. Ha Sql server környezetben szeretnénk egy ilyen összekapcsolásos lekérdezést a termékek és azok kategóriája között, akkor azt az alábbi sql mondattal valósíthatnánk meg.

Inner join

Vagyis a SalesLT.Product táblát az INNER JOIN összekapcsolja a SalesLT.Product.ProductCatergory táblával az alapján, hogy a SalesLT.Product tábla SalesLT.Product.ProductCategoryID mezőjének a tartalma azonos a SalesLT.ProductCategory tábla .ProductCategory.ProductCategoryID mezőjének tartalmával. Hogy ez még nyilvánvalóbb legyen, a projekciós részben tüntessük fel a két tábla azonos mezőinek tartalmát is, vagyis legyen a mondat az alábbi:

Vagyis a lekérdezés az INNER JOIN hatására az eredmény halmaz képzésekor minden SalesLT.Product.ProductCategoryID esetében megkeresi a SalesLT.ProductCategory.Category_ID szerint azonos tartalmú sorokat, és azokat az eredményhalmazhoz adja. Ezért lehetséges, hogy a projekcióban már hivakozhatunk erre a mezőre. Ha (ebben a példában nem, de máshol esetleg lehetséges) több CategoryID egyezést talál, akkor a „join-olt” táblának több sorát is az eredmény halmazhoz adja, és a projekcióban az első tábla sorelemeinek ismétlésével több sort generál.

9-24. ábra -

kepek3/14fejezet-9-24.jpg


Az INNER JOIN tehát olyan belső kapcsolatot hoz létre két tábla között, melyet a két tábla egyes mezőinek egyezősége határoz meg (innen a név: INNER JOIN).

9-25. ábra -

kepek3/14fejezet-9-25.jpg


A linq slq szintakszisú kódolási technikával a fenti feladat megoldása, ahol külön figyelmet érdemel az azonos mezőnevek átnevezésének technikája:

A kódhoz tartozó futási kép pedig:

9-26. ábra -

kepek3/14fejezet-9-26.jpg


Ugyanez a feladat az extension method-os kódolással az alábbi:

http://msdn.microsoft.com/en-us/library/bb534675(v=vs.110).aspx

9-27. ábra -

kepek3/14fejezet-9-27.jpg


A .Join extension methodnak négy (hasznos) paramétere van, hiszen az elsővel csak azt jelzi, hogy minek a kiterjesztésére alkották meg (this kulcsszó).

Az első azt mondja meg, hogy melyik táblával készül az összekapcsolás, itt egy IEnumerable-t megvalósító generizált típust vár. A következő két paraméter olyan delegate-eket vár, melyek egy-egy kulcsot adnak vissza outerKeySelector, és innerKeySelector szimbólikus névvel. Az utolsó paraméter (delegate) pedig az eredményhalmaz részhalmazait, mint bemeneti paramétereket dolgozza fel és hozza létre a resultSelector-t. Mivel ezen kívül csak a következőkben ismertetésre kerülő GroupJoin létezik ezért ezek felhasználásával lehet a outer join-okat és a cross joint is megvalósítani.

Left Outer Join

A left outer join a két tábla sorait kombinálja úgy, hogy a bal oldali tábla soraihoz keres a jobboldali táblában illeszkedést. Nézzük most ezt meg először egy Sql server környezetben.

9-28. ábra -

kepek3/14fejezet-9-28.jpg


9-29. ábra -

kepek3/14fejezet-9-29.jpg


A balról zárt összekapcsolásnak tehát az a feltétele, hogy a két OrderId azonos legyen. Ilyenkor a bal oldali (tehát az SalesOrderHeader, mint master) halmaz egy sorához keres illeszkedést a jobboldali (tehát SalesOrderDetails, mint details) táblából. Amikor talál egyet, akkor szemléletesen úgy képzelhetjük el, hogy a bal oldali tábla sorhoz hozzáfűzi a jobb oldali tábla sort. Persze ha az illesztés sikeres, akkor ehhez az eredményhez jutottunk volna egy egyszerű INNER JOIN alkalmazásával is.

Viszont ha például arra vagyunk kíváncsiak, hogy létezik-e olyan számlafej (SalesOrderHeader), melyhez nem tartoznak számla sorok, akkor az INNER JOIN nem hozná a bal oldali táblából mondjuk a 71776-os ID-jű master sort, hiszen a belső összekapcsoláskor nem talál illeszkedést a master-details táblákban. Ha preparáljuk a details sorokat és például a SalesOrderDetails táblából kitöröljük annak egyetlen hivatkozását a 71776-os fejrekordra, akkor szemléltethetjük, hogy az INNER JOIN nem hozza ezt a sort.

9-30. ábra -

kepek3/14fejezet-9-30.jpg


9-31. ábra -

kepek3/14fejezet-9-31.jpg


A törlés után pedig

9-31. ábra -

kepek3/14fejezet-9-31.jpg


Ezek után az előző lekérdezésünk INNER JOIN-nal:

A fenti Sql mondat alapján a következő eredményt szolgáltatja, vagyis nem látjuk a számlasorok nélküli (nyilván hibás) fejrekordot.

9-32. ábra -

kepek3/14fejezet-9-32.jpg


Ugyanakkor a már korábban megszerkesztett Left OuterJoin használatával mondatunk eredményén jól látható, hogy a fej rekordhoz csak egy null értékű DetailsOrderID és ProductID mező csatolható, vagyis nem tartozik hozzá sor.

9-33. ábra -

kepek3/14fejezet-9-33.jpg


Kicsit „beszédesebbre” alakítva a kimenetet, az alábbi mondattal nyerhetjük ki az összes létező számlafejet, és a hozzá tartozó sorokat, vagy a hiányzó sort, ha nem tartozik a fejhez sor.

9-34. ábra -

kepek3/14fejezet-9-34.jpg


Vagyis a left outer baloldali halmaz minden eleméhez keres a jobboldali táblában illeszkedést, és ha nem talál, akkor is megjeleníti a bal oldali halmazelem mellett a jobboldalit is a projekcióban, azonban a bal oldali halmazelem mezőiben null értékekkel. A linq parancsokkal megvalósított kódolás sql szintakszissal történő megvalósításáhot az alábbi context bővitést kell megvalósítani.

9-35. ábra -

kepek3/14fejezet-9-35.jpg


Mindezekkel a fenti left outer join linq megvalósítás sql szintakszissal:

9-36. ábra -

kepek3/14fejezet-9-36.jpg


Az extension method lambda expression-os kódolás azonos eredménnyel pedig a következő:

9-37. ábra -

kepek3/14fejezet-9-37.jpg


A fenti két példában tehát a join-ok paraméterezése azonos volt, és az első paraméter a kapcsolandó halmaz, a második a bal halmaz kapcsolási mezőjének kifejezése, a harmadik a jobb halmaz kapcsolási mezőjének kifejezése, végül az utolsó az eredmény halmaz.

Külön figyelmet érdemel az eredmény halmaz DefaultIfEmpty() függvényének használata, melyre azért van szükség, mert enélkül azok a bal halmazbeli sorok, melyekhez nem tartozik jobb halmazbeli sor, nem jönnek, és amelyben megadhatjuk a null értékekre való helyettesítést is.

Right Outer Join operátor csoport

A right outer join tulajdonképpen a left outer join szimmetrikus művelete, minden esetben átalakítható left outer joinra a forráshalmazok sorrendjének felcserélésével, ezért ennek kidolgozását az olvasóra bízzuk.

Group by operátor csoport

Mint sql tanulmányainkból tudjuk, a group by záradék azt teszi lehetővé, hogy valamilyen szempont szerint csoportosítsuk az eredményhalmaz elemeit, és a csoportra jellemző részhalmazokra valamilyen aggregát függvény (Sum, Max, Min, AVG) segítségével műveleteket végezzünk. Az ide való első példában először sql szintakszissal megmutatjuk példáli az INT típus összes metódusának összes polimorfját, majd csoportosítjuk az eredmény halmazt úgy, hogy az azonos nevű metódusok csak egyszer szerepeljenek. Az ezt megvalósító kód az alábbi.

9-38. ábra -

kepek3/14fejezet-9-38.jpg


Vagyis látszik, hogy például három ToString polimorfot tartalmaz az INT típus. Ugyanez extension method lambda kódolási technikával a következő.

9-39. ábra -

kepek3/14fejezet-9-39.jpg


Amikor a Group by záradékot használjuk, akkor arra utasítjuk lekérdezés motorját, hogy a csoportképzés feltételének megfelelő halmazelemeket csak egyszer vegye számba.

Az előző példában a ToString metódus négy előfordulását ne négyszer illessze be az eredmény halmazba, hanem csak egyszer. Lássuk most előszőr az sql szintakszisú lekérdezést.

9-40. ábra -

kepek3/14fejezet-9-40.jpg


Ugyanez extension method szintakszissal:

9-41. ábra -

kepek3/14fejezet-9-41.jpg


Mint látható, lehetőség van arra, hogy a csoportosított elemekre nézve műveleteket végezzünk, például megszámláljuk őket. Ezen kívül azonban még az alábbi beépített függvények állnak rendelkezésre (Min, Max, Sum stb.)

Mi most készítsünk egy összegzést a SalesOrderDetails táblából a rendelési azonosító szerint úgy, hogy az egyes sorokhoz tartozó LineTotal-okat összeadjuk. Lássuk legelőször a tiszta sql megvalósítást a management konzolon.

9-42. ábra -

kepek3/14fejezet-9-42.jpg


Most pedig a két kódolási technikával adott megvalósítást linq szintakszissal.

9-43. ábra -

kepek3/14fejezet-9-43.jpg


A beépített aggregáló függvények a fentiek szerint használhatók, mint azt a következő példában látjuk, ahol kiszámítjuk a sorösszegek átlagát is.

9-44. ábra -

kepek3/14fejezet-9-44.jpg


Hasonlóan alkalmazható a Max és Min aggregáló függvény is.

Ordering operátor csoport

Az ordering csoport használatára a korábban készített egyik példánkat ismételjük meg, melynek hatására a halmaz elemei az adott mező szerinti sorrendben listázhatók.

http://msdn.microsoft.com/en-us/library/system.linq.enumerable.orderby(v=vs.110).aspx

9-45. ábra -

kepek3/14fejezet-9-45.jpg


Aggregate operator csoport

http://msdn.microsoft.com/en-us/library/system.linq.enumerable.aggregate(v=vs.110).aspx

Az aggregálás gyakori feladat, amikor egy halmaz minden elemén azonos vagy esetszétválasztásos műveleteket szeretnénk végrehajtani (ez a két példa a fenti link-ről való)

Min Max operátorok

9-46. ábra -

kepek3/14fejezet-9-46.jpg


Particionálás operátor csoport

http://msdn.microsoft.com/en-us/library/bb503062.aspx

A particionálás lehetővé teszi, hogy a kiindulási halmaz tartalmát szűkítsük olyan inkrementális szelekcióval, melynek paramétere vagy egy feltétel függvény, vagy az elemeknek a halmazban elfoglalt indexe. Ide tartozik a Take, Skip, TakeWhile, SkipWhile metódusok.

Ezeket az operátorokat általában a projekció halmazának particionálására használjuk, mint az előző példában is ahol a legelső tíz elem megjelenítését valósítjuk meg.

9-47. ábra -

kepek3/14fejezet-9-47.jpg


9-48. ábra -

kepek3/14fejezet-9-48.jpg


Concatenáció operátor csoport

Amikor az egyes lekérdezési halmazok rendelkezésre állnak, akkor nagyon hasznos, ha ezek között konkatenációt lehet végrehajtani, vagyis az eredményhalmazok egyesítéséről van szó az alábbi példa szerint.

A két eredmény halmaz queryConcatExt_1 és queryConcatExt_2 konkatenált erdményhalmaza konstruálható melynek futási képe az alábbi:

9-49. ábra -

kepek3/14fejezet-9-49.jpg


Element operátor csoport

Az egyik leggyakrabban használt operátor csoport, mert segítségével érhetjük el a halmaz első, utolsó illetve az ezekkel kombinált default eredmény elemeket. A továbbiakban először képzünk egy halmazt, melynek előállítását az alábbi mondat végzi:

Ennek futási képe pedig az alábbi:

9-50. ábra -

kepek3/14fejezet-9-50.jpg


Ezt követően az előállított halmazra az alábbi element operátorokat alkalmazzuk:

9-51. ábra -

kepek3/14fejezet-9-51.jpg


Az element operátorok lehetővé teszik tehát, hogy az első, az utolsó, illetőleg az első vagy default elemeket keressünk vissza, vagy hozzuk létre. Külön figyelmet érdemel a DefaultIfEmtpy operátor, melyre gyakran van szükség, ha az eredmény halmaz üres elemeinek projekciójánál, az üres elem helyére meghatározott tartalmú helyettesítést szeretnénk megjeleníteni. Ennek bemutatására nézzük az alábbi kódrészletet:

9-52. ábra -

kepek3/14fejezet-9-52.jpg


Ebben példában a Left Outer Join kapcsolódáskor a jobb oldali halmaz üres sorai mezőinek helyére a kódban jelölt default értékeket (-1) helyettesítjük.

Generation operátor csoport

Ebben a csoportban azok az operációk találhatók melyekkel valamilyen halmaz, illetve elem felhasználásával további halmaz vagy elem generálható. Ide tartoznak az Empty, Range, Repeat operátorok.

http://msdn.microsoft.com/en-us/library/bb341042(v=vs.110).aspx

Empty operátor

Az Empty ismertetése előtt lássunk egy újabb példát az aggregálásra, hogy az azt következő Empty extension method–ot bemutathasssuk. Legyen a feladat egy sztringekből álló tömb elemei sorrendjének megfordítása.

9-53. ábra -

kepek3/14fejezet-9-53.jpg


9-54. ábra -

kepek3/14fejezet-9-54.jpg


A fenti aggregate tehát visszakeresi és halmozza azokat a List elemeket, melyek önmagukban, mint tömbök, nagyobb, vagy egyenlő elemszámmal bírnak 5-nél.

Range operátor

A következő operátor ebben a csoportban a Range, mely alkalmazásának célja az eredmény halmaz index alapú tartománynak előállítása, ahol a bementi paraméterek a kezdőszám és az elemszám.

9-55. ábra -

kepek3/14fejezet-9-55.jpg


Az előbbi példa egy változatát (int elemű halmazok feltételes unióját), újonnan szerzett operátorunk felhasználásával így is irhatnánk.

9-56. ábra -

kepek3/14fejezet-9-56.jpg


Repeat operátor

A repeat operátor egy adott minta adott számú ismétlésésével állít elő eredményhalmazt.

Mennyiségi operátorok:

All operátor

http://msdn.microsoft.com/en-us/library/bb548541(v=vs.110).aspx

Az All operátor a feltételes kiértékelés eredményét egy bool-két adja meg a halmaz elemeinek egyenkénti vizsgálata után.

9-57. ábra -

kepek3/14fejezet-9-57.jpg


Any operátor

http://msdn.microsoft.com/en-us/library/system.linq.enumerable.any(v=vs.110).aspx

Az Any operátor a halmaz elemeinek vizsgálata után igazzal tér vissza, ha legalább egy elemre a feltétel teljesült.

9-58. ábra -

kepek3/14fejezet-9-58.jpg


Contains operátor

http://msdn.microsoft.com/en-us/library/bb548541(v=vs.110).aspx

A contains operátor, mint azt a nevéből sejteni lehet, egy előfordulást vizsgál az adott halmaz elemei között.

9-59. ábra -

kepek3/14fejezet-9-59.jpg


SequenceEqual operátor (EqualAll)

A korábban EqualAll operátornak nevezett metódus a CTP 2007 januári átnevezése után a SequenceEqual néven érhető el.

9-60. ábra -

kepek3/14fejezet-9-60.jpg


Set operátor csoport

Distinct operátor

A Distinct operátor a halmaz ismétlődő elemeit csak egyszer emeli az eredményhalmazba.

9-61. ábra -

kepek3/14fejezet-9-61.jpg


Intersect operátor

A két bemeneti halmaz közös részhalmazát (metszetét) adja vissza.

9-62. ábra -

kepek3/14fejezet-9-62.jpg


Union operátor

A halmazműveleti unió az egyes halmazok egyesítését valósítja meg.

9-63. ábra -

kepek3/14fejezet-9-63.jpg


Except operátor

Az except operátor egy halmaz elemeiből egy másik halmaz elemei szerinti maradékot állítja elő.

9-64. ábra -

kepek3/14fejezet-9-64.jpg


Conversion operátor csoport

A konverziós csoportba azok az operátorok tartoznak, melyek vagy egy új típusú eredményhalmazt állítanak elő, vagy valamilyen típustól függő paraméterük, vagy generalizálási képességük van.

OfType operátor

Az OfType operátor a kiinduló halmazból az operátor paramétere szerinti típusú részhalmazzal tér vissza.

9-65. ábra -

kepek3/14fejezet-9-65.jpg


Cast operátor

A cast operátor első ránézésre hasonlónak tűnhet, mint az OfType operátor, de feladata más. A példa előtt megadjuk a Cast operátor eredeti definicióját.

valamint az OfType operátorét, hogy lássuk a különbséget.

Mint látható, a T-re generizált visszakeresés az OfType esetében a típus azonosságának feltételével valósul meg, míg a Cast esetében T-re cast-olt object visszaadása a típus azonosság feltétele nélkül valósul meg. Ez azt jelenti, (yield, mint tudjuk az iteráció elemenkénti visszatérését valósítja meg, majd az „emlékező” állpotú iteráció újrahívását kezdeményezi), hogy az OfType válogatás után cast-ol, a Cast pedig szűrés/filter nélkül.

Vagyis az alábbi példában kötelezően kivételt kapunk a második hívásra:

9-66. ábra -

kepek3/14fejezet-9-66.jpg


Míg az ezt követő sor már létrehozza a tomb2–t, a válogatott visszakeresés eredményével.

9-67. ábra -

kepek3/14fejezet-9-67.jpg


Mindezek alapján lássuk a cast alkalmazásának példáját.

9-68. ábra -

kepek3/14fejezet-9-68.jpg


ToSequence/AsEnumerable operátor

A ToSequence/AsEnumerable operátor valójában nem végez látható átalakítást a paraméterül kapott halmazon, csak annak típusát a IEnumerable<T>-re módosítja. Hasznos, ha valamilyen korábban létrehozott szekvenciális konténer osztályt a linq műveletek számára szeretnénk átalakítani.

Egy másik, talán szemléletesebb példa:

9-69. ábra -

kepek3/14fejezet-9-69.jpg


Ahol az Arunevek objektum típusa:

ToArray operátor

9-70. ábra -

kepek3/14fejezet-9-70.jpg


ToList operátor

9-71. ábra -

kepek3/14fejezet-9-71.jpg


ToDictionary operátor

A ToDictionary operátor egy kulcs-értékpár alapú konténerré konvertál.

9-72. ábra -

kepek3/14fejezet-9-72.jpg


ToLookup operátor

A ToLookup talán elsőre hasonlatosnak tűnhet a ToDictionary-hez, mivel mindkét kulcs- értékpár tartalmú eredményhalmazt generál, azonban nagy különség, hogy míg a ToDictionary-ban a kulcs csak a halmaz létező eleme lehet, addig a ToLookup-ban mi magunk határozhatjuk meg a kulcs előállítását.

9-73. ábra -

kepek3/14fejezet-9-73.jpg