Bevezetés a LINQ-lekérdezések használatába c nyelven#
A lekérdezés olyan kifejezés, amely adatokat kér le egy adatforrásból. A különböző adatforrások különböző natív lekérdezési nyelvekkel rendelkeznek, például sql relációs adatbázisokhoz és XQuery XML-hez. A fejlesztőknek új lekérdezési nyelvet kell elsajátítaniuk minden olyan adatforrás- vagy adatformátumhoz, amelyet támogatniuk kell. A LINQ leegyszerűsíti ezt a helyzetet azáltal, hogy konzisztens C#-nyelvi modellt kínál különféle adatforrásokhoz és formátumokhoz. A LINQ-lekérdezésekben mindig C# objektumokkal dolgozik. Ugyanezekkel az alapszintű kódolási mintákkal kérdezhet le és alakíthat át adatokat XML-dokumentumokban, SQL-adatbázisokban, .NET-gyűjteményekben és bármilyen más formátumban, ha elérhető linq-szolgáltató.
A lekérdezési művelet három része
Minden LINQ-lekérdezési művelet három különböző műveletből áll:
- Szerezze be az adatforrást.
- Hozza létre a lekérdezést.
- Hajtsa végre a lekérdezést.
Az alábbi példa bemutatja, hogy a lekérdezési művelet három része hogyan van kifejezve a forráskódban. A példa egy egész számtömböt használ adatforrásként a kényelem érdekében; azonban ugyanezek a fogalmak vonatkoznak más adatforrásokra is. Erre a példára a cikk további részében hivatkozunk.
// The Three Parts of a LINQ Query:
// 1. Data source.
int[] numbers = [ 0, 1, 2, 3, 4, 5, 6 ];
// 2. Query creation.
// numQuery is an IEnumerable<int>
var numQuery =
from num in numbers
where (num % 2) == 0
select num;
// 3. Query execution.
foreach (int num in numQuery)
{
Console.Write("{0,1} ", num);
}
Az alábbi ábrán a teljes lekérdezési művelet látható. A LINQ-ban a lekérdezés végrehajtása különbözik magától a lekérdezéstől. Más szóval nem kér le adatokat lekérdezésváltozó létrehozásával.
Az adatforrás
Az előző példában szereplő adatforrás egy tömb, amely támogatja az általános IEnumerable<T> felületet. Ez a tény azt jelenti, hogy lekérdezhető a LINQ-val. A lekérdezés egy foreach
utasításban lesz végrehajtva, és foreach
megköveteli IEnumerable vagy IEnumerable<T>. Az olyan típusokat, amelyek támogatják IEnumerable<T> vagy egy származtatott felületet, például az általánostIQueryable<T>, lekérdezhető típusoknak nevezzük.
A lekérdezhető típus nem igényel módosítást vagy speciális kezelést a LINQ-adatforrásként való ellátáshoz. Ha a forrásadatok még nincsenek a memóriában lekérdezhető típusként, akkor a LINQ-szolgáltatónak így kell ábrázolnia. A LINQ–XML például egy XML-dokumentumot egy lekérdezhető XElement típusba tölt be:
// Create a data source from an XML document.
// using System.Xml.Linq;
XElement contacts = XElement.Load(@"c:\myContactList.xml");
Az EntityFramework használatával objektum-relációs leképezést hozhat létre a C#-osztályok és az adatbázisséma között. A lekérdezéseket az objektumokra írja, és futásidőben az EntityFramework kezeli az adatbázissal folytatott kommunikációt. Az alábbi példában Customers
egy adott táblát jelöl az adatbázisban, és a lekérdezés eredményének IQueryable<T>típusa a következőből IEnumerable<T>származik.
Northwnd db = new Northwnd(@"c:\northwnd.mdf");
// Query for customers in London.
IQueryable<Customer> custQuery =
from cust in db.Customers
where cust.City == "London"
select cust;
A különböző LINQ-szolgáltatók dokumentációjában további információt talál arról, hogyan hozhat létre bizonyos típusú adatforrásokat. Az alapvető szabály azonban egyszerű: a LINQ-adatforrások minden olyan objektum, amely támogatja az általános IEnumerable<T> felületet, vagy általában egy tőle öröklő IQueryable<T>felületet.
Feljegyzés
Az olyan típusok, mint amelyek ArrayList támogatják a nem általános IEnumerable felületet, LINQ-adatforrásként is használhatók. További információ: Tömblista lekérdezése LINQ-val (C#).
A lekérdezés
A lekérdezés meghatározza, hogy milyen adatokat kér le az adatforrásból vagy forrásokból. A lekérdezések opcionálisan azt is meghatározzák, hogy az adatok hogyan legyenek rendezve, csoportosítva és formázva a visszaadás előtt. A lekérdezések egy lekérdezési változóban tárolódnak, és egy lekérdezési kifejezéssel inicializálva lesznek. A lekérdezések írásához C#-lekérdezési szintaxist használ.
Az előző példában szereplő lekérdezés az egész számtömb összes páros számát adja vissza. A lekérdezési kifejezés három záradékot tartalmaz: from
, where
és select
. (Ha ismeri az SQL-t, észrevette, hogy a záradékok sorrendje visszafordul az SQL-ben megadott sorrendből.) A from
záradék megadja az adatforrást, a where
záradék alkalmazza a szűrőt, a select
záradék pedig a visszaadott elemek típusát. Ebben a szakaszban részletesen tárgyaljuk az összes lekérdezési záradékot. Egyelőre az a fontos, hogy a LINQ-ban maga a lekérdezési változó nem hajt végre műveletet, és nem ad vissza adatokat. Csak tárolja azokat az információkat, amelyek szükségesek az eredmények létrehozásához, amikor a lekérdezést egy későbbi időpontban hajtják végre. A lekérdezések létrehozásának módjáról további információt a Standard lekérdezési operátorok áttekintése (C#) című témakörben talál.
Feljegyzés
A lekérdezések metódusszintaxissal is kifejezhetők. További információ: Lekérdezési szintaxis és metódusszintaxis a LINQ-ban.
Standard lekérdezési operátorok besorolása végrehajtás alapján
A standard lekérdezésoperátori metódusok LINQ to Objects implementációi két fő módszer egyikét hajtják végre: azonnali vagy halasztott. A késleltetett végrehajtást használó lekérdezési operátorok két kategóriába sorolhatók: streamelés és nem streamelés.
Azonnali
Az azonnali végrehajtás azt jelenti, hogy az adatforrás beolvassa az adatforrást, és a műveletet egyszer hajtja végre. A skaláris eredményt visszaadó összes szabványos lekérdezési operátor azonnal végrehajtja a elemet. Ilyen lekérdezések például a következőkCount
: , Average
Max
és First
. Ezek a metódusok explicit foreach
utasítás nélkül futnak, mert magának a lekérdezésnek kell használnia foreach
az eredmény visszaadásához. Ezek a lekérdezések egyetlen értéket adnak vissza, nem gyűjteményt IEnumerable
. Bármely lekérdezést kényszeríthet arra, hogy azonnal végrehajtsa a metódusok vagy Enumerable.ToArray metódusok Enumerable.ToList használatával. Az azonnali végrehajtás a lekérdezési eredmények újrafelhasználását biztosítja, nem pedig a lekérdezési deklarációt. Az eredmények lekérése egyszer történik, majd későbbi felhasználás céljából lesz tárolva. Az alábbi lekérdezés a forrástömb páros számainak számát adja vissza:
var evenNumQuery =
from num in numbers
where (num % 2) == 0
select num;
int evenNumCount = evenNumQuery.Count();
A lekérdezések azonnali végrehajtásának kényszerítéséhez és az eredmények gyorsítótárazásához meghívhatja azokat vagy ToArray metódusokatToList.
List<int> numQuery2 =
(from num in numbers
where (num % 2) == 0
select num).ToList();
// or like this:
// numQuery3 is still an int[]
var numQuery3 =
(from num in numbers
where (num % 2) == 0
select num).ToArray();
A végrehajtást úgy is kényszerítheti, hogy a ciklust közvetlenül a foreach
lekérdezési kifejezés után helyezi el. A hívással ToList
ToArray
vagy az összes adat gyorsítótárazásával azonban egyetlen gyűjteményobjektumban is.
Halasztott
A késleltetett végrehajtás azt jelenti, hogy a műveletet nem a kód azon pontján hajtják végre, ahol a lekérdezés deklarálva van. A műveletet csak akkor hajtja végre a rendszer, ha a lekérdezési változó számba van írva, például egy foreach
utasítás használatával. A lekérdezés végrehajtásának eredménye az adatforrás tartalmától függ a lekérdezés végrehajtásakor, és nem a lekérdezés definiálásakor. Ha a lekérdezési változó többször van enumerálva, az eredmények minden alkalommal eltérőek lehetnek. Szinte az összes szabványos lekérdezési operátor, amelynek visszatérési típusa késleltetett módon történik IEnumerable<T> vagy IOrderedEnumerable<TElement> fut. A halasztott végrehajtás lehetővé teszi a lekérdezések újbóli felhasználását, mivel a lekérdezés minden alkalommal lekéri a frissített adatokat az adatforrásból, amikor a lekérdezés eredményeinek iteratedálása megtörtént. Az alábbi kód egy halasztott végrehajtást szemléltet:
foreach (int num in numQuery)
{
Console.Write("{0,1} ", num);
}
Az foreach
utasításban a lekérdezés eredményei is lekérhetők. Az előző lekérdezésben például az iterációs változó num
a visszaadott sorozat minden értékét (egyenként) tartalmazza.
Mivel maga a lekérdezési változó soha nem tárolja a lekérdezési eredményeket, a frissített adatok lekéréséhez többször is végrehajthatja. Előfordulhat például, hogy egy külön alkalmazás folyamatosan frissíti az adatbázist. Az alkalmazásban létrehozhat egy lekérdezést, amely lekéri a legújabb adatokat, és időközönként végrehajthatja a frissített eredmények lekéréséhez.
A késleltetett végrehajtást használó lekérdezési operátorokat tovább lehet sorolni streamelésnek vagy nem streamelésnek.
Streamelés
A streamszolgáltatóknak nem kell beolvasniuk az összes forrásadatot, mielőtt elemeket adnak. A végrehajtáskor a streamszolgáltató minden forráselemen végrehajtja a műveletet az olvasás során, és adott esetben az elemet adja meg. A streamszolgáltató továbbra is olvassa a forráselemeket, amíg létre nem jön egy eredményelem. Ez azt jelenti, hogy egy eredményelem előállításához több forráselem is olvasható.
Nem streamelés
A nem streamelő operátoroknak be kell olvasniuk az összes forrásadatot, mielőtt eredményelemhez jutnának. Az olyan műveletek, mint a rendezés vagy a csoportosítás ebbe a kategóriába tartoznak. A végrehajtáskor a nem streamelt lekérdezési operátorok beolvassák az összes forrásadatot, adatstruktúrába helyezik, végrehajtják a műveletet, és az eredményül kapott elemeket eredményezik.
Besorolási táblázat
Az alábbi táblázat az egyes standard lekérdezési operátor-metódusokat a végrehajtási módszerének megfelelően sorolja be.
Feljegyzés
Ha egy operátor két oszlopban van megjelölve, két bemeneti sorozat vesz részt a műveletben, és az egyes sorozatok kiértékelése másképp történik. Ezekben az esetekben mindig ez az első sorozat a paraméterlistában, amelyet halasztott, streamelési módon értékelnek ki.
LINQ az objektumokhoz
A "LINQ to Objects" (LINQ to Objects) a LINQ-lekérdezések bármely vagy IEnumerable<T> gyűjteményhez IEnumerable való közvetlen használatát jelenti. A LINQ használatával lekérdezheti az enumerálható gyűjteményeket, például List<T>: , Arrayvagy Dictionary<TKey,TValue>. A gyűjtemény lehet felhasználó által definiált vagy .NET API által visszaadott típus. A LINQ-megközelítésben deklaratív kódot kell írnia, amely leírja, hogy mit szeretne lekérni. A LINQ to Objects nagyszerű bevezetést nyújt a LINQ-val való programozásba.
A LINQ-lekérdezések három fő előnyt kínálnak a hagyományos foreach
hurkokkal szemben:
- Tömörebbek és olvashatóbbak, különösen több feltétel szűrése esetén.
- Hatékony szűrési, rendezési és csoportosítási képességeket biztosítanak minimális alkalmazáskóddal.
- Ezek más adatforrásba is átadhatók, és csak kis módosítással vagy módosítás nélkül.
Minél összetettebb műveletet szeretne végrehajtani az adatokon, annál több előnyre lesz szüksége, ha a LINQ-t használja a hagyományos iterációs technikák helyett.
Lekérdezés eredményeinek tárolása a memóriában
A lekérdezés alapvetően az adatok lekérésére és rendszerezésére vonatkozó utasítások készlete. A lekérdezések lazán lesznek végrehajtva, mivel az eredmény minden további elemét kéri a rendszer. Az foreach
eredmények iterálásakor a rendszer az elemeket hozzáférésként adja vissza. Ha egy lekérdezést ki szeretne értékelni, és az eredményeket ciklus végrehajtása foreach
nélkül szeretné tárolni, egyszerűen hívja meg az alábbi módszerek egyikét a lekérdezési változón:
A visszaadott gyűjteményobjektumot egy új változóhoz kell hozzárendelnie a lekérdezés eredményeinek tárolásakor, ahogyan az alábbi példában látható:
List<int> numbers = [1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20];
IEnumerable<int> queryFactorsOfFour =
from num in numbers
where num % 4 == 0
select num;
// Store the results in a new variable
// without executing a foreach loop.
var factorsofFourList = queryFactorsOfFour.ToList();
// Read and write from the newly created list to demonstrate that it holds data.
Console.WriteLine(factorsofFourList[2]);
factorsofFourList[2] = 0;
Console.WriteLine(factorsofFourList[2]);