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:

  1. Szerezze be az adatforrást.
  2. Hozza létre a lekérdezést.
  3. 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.

A teljes LINQ-lekérdezési művelet diagramja.

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: , AverageMaxé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 ToListToArray 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.

Standard lekérdezési operátor Visszatérési típus Azonnali végrehajtás Késleltetett streamelés végrehajtása Késleltetett, nem streameléses végrehajtás
Aggregate TSource X
All Boolean X
Any Boolean X
AsEnumerable IEnumerable<T> X
Average Egyetlen numerikus érték X
Cast IEnumerable<T> X
Concat IEnumerable<T> X
Contains Boolean X
Count Int32 X
DefaultIfEmpty IEnumerable<T> X
Distinct IEnumerable<T> X
ElementAt TSource X
ElementAtOrDefault TSource? X
Empty IEnumerable<T> X
Except IEnumerable<T> X X
First TSource X
FirstOrDefault TSource? X
GroupBy IEnumerable<T> X
GroupJoin IEnumerable<T> X X
Intersect IEnumerable<T> X X
Join IEnumerable<T> X X
Last TSource X
LastOrDefault TSource? X
LongCount Int64 X
Max Egy numerikus érték, TSourcevagy TResult? X
Min Egy numerikus érték, TSourcevagy TResult? X
OfType IEnumerable<T> X
OrderBy IOrderedEnumerable<TElement> X
OrderByDescending IOrderedEnumerable<TElement> X
Range IEnumerable<T> X
Repeat IEnumerable<T> X
Reverse IEnumerable<T> X
Select IEnumerable<T> X
SelectMany IEnumerable<T> X
SequenceEqual Boolean X
Single TSource X
SingleOrDefault TSource? X
Skip IEnumerable<T> X
SkipWhile IEnumerable<T> X
Sum Egyetlen numerikus érték X
Take IEnumerable<T> X
TakeWhile IEnumerable<T> X
ThenBy IOrderedEnumerable<TElement> X
ThenByDescending IOrderedEnumerable<TElement> X
ToArray TSource[] Tömb X
ToDictionary Dictionary<TKey,TValue> X
ToList IList<T> X
ToLookup ILookup<TKey,TElement> X
Union IEnumerable<T> X
Where IEnumerable<T> X

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]);

Lásd még