Megosztás a következőn keresztül:


Bevezetés a LINQ-lekérdezések használatába c nyelven#

A lekérdezési 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 van végrehajtva, és a foreach-hez IEnumerable vagy IEnumerable<T>szükséges. Azokat a típusokat, amelyek támogatják a IEnumerable<T>-t vagy egy származtatott felületet, mint például a generikus IQueryable<T>-et, lekérdezhető típusoknaknevezzü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. Például a LINQ to XML betölt egy XML-dokumentumot egy lekérdezhető XElement típusba.

// Create a data source from an XML document.
// using System.Xml.Linq;
XElement contacts = XElement.Load(@"c:\myContactList.xml");

Az EntityFrameworksegítségével 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 az adatbázis egy adott tábláját jelöli, a lekérdezés eredményének típusa pedig IQueryable<T>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 olyan objektumok, amelyek támogatják az általános IEnumerable<T> felületet, vagy egy olyan felület, amely örökli azt, általában IQueryable<T>.

Megjegyzés

Az olyan típust, mint a ArrayList, amely támogatja a nem általános IEnumerable felületet, szintén lehet használni LINQ-adatforrásként. További információ: Hogyan lehet lekérdezni egy ArrayList-et LINQ (C#) segítségével.

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 kell használnia.

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.

Megjegyzés

A lekérdezések metódusszintaxissal is kifejezhetők. További információ: lekérdezési szintaxis és metódusszintaxis a LINQ.

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ódon hajthatók végre: azonnali vagy halasztott. A késleltetett végrehajtást használó lekérdezési operátorok két további kategóriába oszthatók: folyamatos és nem folyamatos.

Azonnali

Az azonnali végrehajtás azt jelenti, hogy az adatforrást beolvassák, és a műveletet egyszer hajtják 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 Count, Max, Averageés First. Ezek a metódusok explicit foreach utasítás nélkül futnak, mert magának a lekérdezésnek foreach kell használnia az eredmény visszaadásához. Ezek a lekérdezések egyetlen értéket adnak vissza, nem egy IEnumerable gyűjteményt. Bármely lekérdezést kényszeríthet, hogy azonnal végrehajtsa a Enumerable.ToList vagy Enumerable.ToArray metódusok 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 a ToList vagy ToArray metódusokat.

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 foreach ciklust közvetlenül a lekérdezési kifejezés után helyezi el. A ToList vagy ToArray meghívásával azonban az összes adatot egyetlen gyűjteményobjektumban is gyorsítótárazza.

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 minden olyan szabványos lekérdezési operátor, amelynek visszatérési típusa IEnumerable<T> vagy IOrderedEnumerable<TElement> halasztott módon hajtható végre. 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);
}

A foreach utasítás a lekérdezési eredmények lekérésének helye is. Az előző lekérdezésben például az num iterációs változó 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. A(z) alkalmazásban létrehozhat egy lekérdezést, amely lekéri a legújabb adatokat, majd ezt időközönként végrehajthatja, hogy frissített eredményeket kapjon.

A késleltetett végrehajtást használó lekérdezési operátorokat tovább lehet sorolni streamelésnek vagy nem streamelésnek.

Online közvetíté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 streaming alapú

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.

Megjegyzé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
All Boolean
Any Boolean
AsEnumerable IEnumerable<T>
Average Egyetlen numerikus érték
Cast IEnumerable<T>
Concat IEnumerable<T>
Contains Boolean
Count Int32
DefaultIfEmpty IEnumerable<T>
Distinct IEnumerable<T>
ElementAt TSource
ElementAtOrDefault TSource?
Empty IEnumerable<T>
Except IEnumerable<T>
First TSource
FirstOrDefault TSource?
GroupBy IEnumerable<T>
GroupJoin IEnumerable<T>
Intersect IEnumerable<T>
Join IEnumerable<T>
Last TSource
LastOrDefault TSource?
LongCount Int64
Max Egy numerikus érték, TSourcevagy TResult?
Min Egy numerikus érték, TSourcevagy TResult?
OfType IEnumerable<T>
OrderBy IOrderedEnumerable<TElement>
OrderByDescending IOrderedEnumerable<TElement>
Range IEnumerable<T>
Repeat IEnumerable<T>
Reverse IEnumerable<T>
Select IEnumerable<T>
SelectMany IEnumerable<T>
SequenceEqual Boolean
Single TSource
SingleOrDefault TSource?
Skip IEnumerable<T>
SkipWhile IEnumerable<T>
Sum Egyetlen numerikus érték
Take IEnumerable<T>
TakeWhile IEnumerable<T>
ThenBy IOrderedEnumerable<TElement>
ThenByDescending IOrderedEnumerable<TElement>
ToArray TSource[] tömb
ToDictionary Dictionary<TKey,TValue>
ToList IList<T>
ToLookup ILookup<TKey,TElement>
Union IEnumerable<T>
Where IEnumerable<T>

LINQ az objektumokhoz

A "LINQ to Objects" olyan LINQ-lekérdezések használatát jelenti, amelyek közvetlenül IEnumerable vagy IEnumerable<T> gyűjteményt használnak. 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 ciklusokkal szemben:

  • Tömörebbek és olvashatóbbak, különösen amikor több feltételt szűrünk.
  • 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. Ha foreach használatával iterálja az eredményeket, a rendszer az elemeket hozzáférésként adja vissza. A lekérdezés kiértékeléséhez és az eredmények foreach ciklus végrehajtása nélkül történő tárolásához 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