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


A lekérdezési kifejezés alapjai

Ez a cikk a C#-ban található lekérdezési kifejezésekkel kapcsolatos alapfogalmakat ismerteti.

Mi az a lekérdezés, és mit tesz?

A lekérdezések utasítások készlete, amelyek ismertetik, hogy egy adott adatforrásból (vagy forrásokból) milyen adatokat kell lekérni, és milyen alakzattal és szervezettel kell rendelkezniük a visszaadott adatoknak. A lekérdezések eltérnek az általa előállított eredményektől.

A forrásadatok általában logikusan, azonos típusú elemek sorozataként lesznek rendszerezve. Egy SQL-adatbázistábla például sorsorokat tartalmaz. Egy XML-fájlban az XML-elemek "sorozata" található (bár az XML-elemek hierarchikusan vannak rendszerezve egy faszerkezetben). A memóriában lévő gyűjtemény objektumok sorozatát tartalmazza.

Az alkalmazás szempontjából az eredeti forrásadatok konkrét típusa és szerkezete nem fontos. Az alkalmazás mindig IEnumerable<T> vagy IQueryable<T> gyűjteményként látja a forrásadatokat. A LINQ to XML esetében például a forrásadatok láthatók IEnumerable<XElement>.

A forrásütemezés miatt a lekérdezések a következő három dolog egyikét hajthatják végre:

  • Az elemek egy részhalmazának lekérése új sorozat létrehozásához az egyes elemek módosítása nélkül. A lekérdezés ezután különböző módokon rendezheti vagy csoportosíthatja a visszaadott sorozatot az alábbi példában látható módon (tegyük fel, hogy scores egy int[]):

    IEnumerable<int> highScoresQuery =
        from score in scores
        where score > 80
        orderby score descending
        select score;
    
  • Az előző példához hasonlóan lekérheti az elemek sorozatát, de átalakíthatja őket egy új típusú objektummá. Előfordulhat például, hogy egy lekérdezés csak az adatforrás bizonyos ügyfélrekordjaiból kéri le a családneveket. Vagy lekérheti a teljes rekordot, majd felhasználhatja egy másik memóriabeli objektumtípus vagy akár XML-adatok létrehozására a végső eredményütemezés létrehozása előtt. Az alábbi példa egy int és egy stringközötti előrejelzést mutatja be. Figyelje meg a highScoresQueryúj típusát.

    IEnumerable<string> highScoresQuery2 =
        from score in scores
        where score > 80
        orderby score descending
        select $"The score is {score}";
    
  • A forrásadatok egy-egy adott értékének lekérése, például:

    • Egy adott feltételnek megfelelő elemek száma.

    • Az az elem, amely a legnagyobb vagy a legkisebb értékkel rendelkezik.

    • Az első elem, amely megfelel egy feltételnek, vagy egy adott elemkészlet adott értékeinek összege. Az alábbi lekérdezés például a scores egész számtömb 80-nál nagyobb pontszámait adja vissza:

      var highScoreCount = (
          from score in scores
          where score > 80
          select score
      ).Count();
      

      Az előző példában jegyezze fel a zárójelek használatát a lekérdezési kifejezés körül a Enumerable.Count metódus hívása előtt. A konkrét eredmény tárolásához egy új változót is használhat.

      IEnumerable<int> highScoresQuery3 =
          from score in scores
          where score > 80
          select score;
      
      var scoreCount = highScoresQuery3.Count();
      

Az előző példában a lekérdezést a Counthívása hajtja végre, mert Count az eredményeket iterálnia kell a highScoresQueryáltal visszaadott elemek számának meghatározásához.

Mi az a lekérdezési kifejezés?

A lekérdezési kifejezés olyan lekérdezés, amely a lekérdezésszintaxist használja. A lekérdezési kifejezés egy első osztályú nyelvi szerkezet. Ez ugyanúgy működik, mint bármely más kifejezés, és bármely olyan környezetben használható, amelyben a C# kifejezés érvényes. A lekérdezési kifejezések az SQL-hez vagy az XQueryhez hasonló deklaratív szintaxisban írt záradékokból állnak. Minden záradék egy vagy több C#-kifejezést tartalmaz, és ezek a kifejezések maguk is lehetnek lekérdezési kifejezések vagy tartalmazhatnak lekérdezési kifejezéseket.

A lekérdezési kifejezésnek záradékkal kell kezdődnie, és vagy csoport záradékkal kell végződnie. Az első from záradék és az utolsó select vagy group záradék között tartalmazhat egy vagy több választható záradékot: ahol, rendelés szerint, összekapcsolás, legyen, sőt egy másik záradékot is. A into kulcsszóval is engedélyezheti egy join vagy group záradék eredményét, hogy forrásként szolgáljon több lekérdezési záradékhoz ugyanabban a lekérdezési kifejezésben.

Lekérdezési változó

A LINQ-ban a lekérdezési változók olyan változók, amelyek lekérdezési tárolnak a lekérdezés eredményei helyett. Pontosabban a lekérdezésváltozók mindig számbavehető típusok, amelyek egy foreach utasítás vagy a IEnumerator.MoveNext() metódus közvetlen hívása során több elemsort állítanak elő.

Jegyzet

A cikkben szereplő példák az alábbi adatforrásokat és mintaadatokat használják.

record City(string Name, long Population);
record Country(string Name, double Area, long Population, List<City> Cities);
record Product(string Name, string Category);
static readonly City[] cities = [
    new City("Tokyo", 37_833_000),
    new City("Delhi", 30_290_000),
    new City("Shanghai", 27_110_000),
    new City("São Paulo", 22_043_000),
    new City("Mumbai", 20_412_000),
    new City("Beijing", 20_384_000),
    new City("Cairo", 18_772_000),
    new City("Dhaka", 17_598_000),
    new City("Osaka", 19_281_000),
    new City("New York-Newark", 18_604_000),
    new City("Karachi", 16_094_000),
    new City("Chongqing", 15_872_000),
    new City("Istanbul", 15_029_000),
    new City("Buenos Aires", 15_024_000),
    new City("Kolkata", 14_850_000),
    new City("Lagos", 14_368_000),
    new City("Kinshasa", 14_342_000),
    new City("Manila", 13_923_000),
    new City("Rio de Janeiro", 13_374_000),
    new City("Tianjin", 13_215_000)
];

static readonly Country[] countries = [
    new Country ("Vatican City", 0.44, 526, [new City("Vatican City", 826)]),
    new Country ("Monaco", 2.02, 38_000, [new City("Monte Carlo", 38_000)]),
    new Country ("Nauru", 21, 10_900, [new City("Yaren", 1_100)]),
    new Country ("Tuvalu", 26, 11_600, [new City("Funafuti", 6_200)]),
    new Country ("San Marino", 61, 33_900, [new City("San Marino", 4_500)]),
    new Country ("Liechtenstein", 160, 38_000, [new City("Vaduz", 5_200)]),
    new Country ("Marshall Islands", 181, 58_000, [new City("Majuro", 28_000)]),
    new Country ("Saint Kitts & Nevis", 261, 53_000, [new City("Basseterre", 13_000)])
];

Az alábbi példakód egy egyszerű lekérdezési kifejezést mutat be egy adatforrással, egy szűrési záradékkal, egy rendezési záradékkal és a forráselemek átalakítása nélkül. A select záradék befejezi a lekérdezést.

// Data source.
int[] scores = [90, 71, 82, 93, 75, 82];

// Query Expression.
IEnumerable<int> scoreQuery = //query variable
    from score in scores //required
    where score > 80 // optional
    orderby score descending // optional
    select score; //must end with select or group

// Execute the query to produce the results
foreach (var testScore in scoreQuery)
{
    Console.WriteLine(testScore);
}

// Output: 93 90 82 82

Az előző példában a scoreQuery egy lekérdezési változó, amelyet néha csak lekérdezésinéven is neveznek. A lekérdezési változó nem tárol tényleges eredményadatokat, amelyek a foreach ciklusban lesznek létrehozva. A foreach utasítás végrehajtásakor a lekérdezési eredmények nem lesznek visszaadva a scoreQuerylekérdezési változón keresztül. Ehelyett a testScoreiterációs változón keresztül adják vissza őket. A scoreQuery változó egy második foreach ciklusban is iterálható. Ugyanazokat az eredményeket hozza létre, amíg sem azt, sem az adatforrást nem módosították.

A lekérdezési változók olyan lekérdezést tárolhatnak, amely lekérdezési szintaxisban vagy metódusszintaxisban vagy a kettő kombinációjában van kifejezve. Az alábbi példákban queryMajorCities és queryMajorCities2 is lekérdezési változók:

City[] cities = [
    new City("Tokyo", 37_833_000),
    new City("Delhi", 30_290_000),
    new City("Shanghai", 27_110_000),
    new City("São Paulo", 22_043_000)
];

//Query syntax
IEnumerable<City> queryMajorCities =
    from city in cities
    where city.Population > 30_000_000
    select city;

// Execute the query to produce the results
foreach (City city in queryMajorCities)
{
    Console.WriteLine(city);
}

// Output:
// City { Name = Tokyo, Population = 37833000 }
// City { Name = Delhi, Population = 30290000 }

// Method-based syntax
IEnumerable<City> queryMajorCities2 = cities.Where(c => c.Population > 30_000_000);
// Execute the query to produce the results
foreach (City city in queryMajorCities2)
{
    Console.WriteLine(city);
}
// Output:
// City { Name = Tokyo, Population = 37833000 }
// City { Name = Delhi, Population = 30290000 }

Másrészt az alábbi két példa olyan változókat mutat be, amelyek nem lekérdezési változók, annak ellenére, hogy mindegyik inicializálva van egy lekérdezéssel. Nem lekérdezési változók, mert az eredményeket tárolják:

var highestScore = (
    from score in scores
    select score
).Max();

// or split the expression
IEnumerable<int> scoreQuery =
    from score in scores
    select score;

var highScore = scoreQuery.Max();
// the following returns the same result
highScore = scores.Max();
var largeCitiesList = (
    from country in countries
    from city in country.Cities
    where city.Population > 10000
    select city
).ToList();

// or split the expression
IEnumerable<City> largeCitiesQuery =
    from country in countries
    from city in country.Cities
    where city.Population > 10000
    select city;
var largeCitiesList2 = largeCitiesQuery.ToList();

Lekérdezési változók explicit és implicit beírása

Ez a dokumentáció általában a lekérdezési változó explicit típusát adja meg a lekérdezési változó és a választó záradékközötti típuskapcsolat megjelenítéséhez. A var kulcsszóval azonban arra is utasíthatja a fordítót, hogy a fordításkor a lekérdezési változó (vagy bármely más helyi változó) típusára következtetjen. A cikkben korábban bemutatott lekérdezési példa például implicit beírással is kifejezhető:

var queryCities =
    from city in cities
    where city.Population > 100000
    select city;

Az előző példában a var használata nem kötelező. queryCities egy IEnumerable<City>, akár implicit módon, akár explicit módon van begépelve.

Lekérdezési kifejezés indítása

A lekérdezési kifejezésnek egy from záradékkal kell kezdődnie. Egy adatforrást és egy tartományváltozót határoz meg. A tartományváltozó a forrásütemezés minden egymást követő elemét jelöli a forrásütemezés bejárása közben. A tartományváltozó erősen be van állítva az adatforrás elemeinek típusa alapján. Az alábbi példában, mivel a countries a Country objektumok tömbje, a tartományváltozó típusa is Country. Mivel a tartományváltozó erősen be van állítva, a pont operátorral elérheti a típus bármely elérhető tagját.

IEnumerable<Country> countryAreaQuery =
    from country in countries
    where country.Area > 20 //sq km
    select country;

A tartományváltozó hatókörben marad, amíg a lekérdezést vagy pontosvesszővel, vagy folytatási záradékkal le nem zárják.

Egy lekérdezési kifejezés több from záradékot is tartalmazhat. Használjon több from záradékot, ha a forrásütemezés minden eleme maga gyűjtemény vagy gyűjteményt tartalmaz. Tegyük fel például, hogy Country objektumgyűjteménye van, amelyek mindegyike CityCitiesnevű objektumgyűjteményt tartalmaz. Az egyes CityCountry objektumainak lekérdezéséhez használjon két from záradékot az itt látható módon:

IEnumerable<City> cityQuery =
    from country in countries
    from city in country.Cities
    where city.Population > 10000
    select city;

További információért lásd a részt azáradékból.

Lekérdezési kifejezés befejezése

A lekérdezési kifejezésnek group záradékkal vagy select záradékkal kell végződnie.

A csoport záradéka

A group záradék használatával egy megadott kulccsal rendszerezett csoportok sorozatát hozhatja létre. A kulcs bármilyen adattípus lehet. A következő lekérdezés például egy vagy több Country objektumot tartalmazó csoportok sorozatát hozza létre, amelyek kulcsa egy char típus, amelynek értéke az országok nevének első betűje.

var queryCountryGroups =
    from country in countries
    group country by country.Name[0];

További információért a csoportosításról tekintse meg a csoportos záradékot.

select záradék

Használja a select záradékot az összes többi sorozattípus előállításához. Egy egyszerű select záradék egyszerűen ugyanolyan típusú objektumsorozatot hoz létre, mint az adatforrásban található objektumok. Ebben a példában az adatforrás Country objektumokat tartalmaz. A orderby záradék csak egy új sorrendbe rendezi az elemeket, és a select záradék létrehozza az átrendezett Country objektumok sorozatát.

IEnumerable<Country> sortedQuery =
    from country in countries
    orderby country.Area
    select country;

A select záradék segítségével a forrásadatok új típusok sorozatává alakíthatók. Ezt az átalakítást vetítésnekis nevezik. A következő példában a select záradék projektek névtelen típusok sorozatát, amelyek csak az eredeti elem mezőinek egy részét tartalmazzák. Az új objektumok inicializálása objektum-inicializálóval történik.

var queryNameAndPop =
    from country in countries
    select new
    {
        Name = country.Name,
        Pop = country.Population
    };

Ebben a példában a var azért van szükség, mert a lekérdezés névtelen típust hoz létre.

További információt az select záradék forrásadatok átalakításának minden módjáról a válogató záradékválasztásával kapcsolatban találhat.

Folytatások -ból-be

A into kulcsszót egy select vagy group záradékban használhatja egy lekérdezést tároló ideiglenes azonosító létrehozásához. A into záradékot akkor használja, ha további lekérdezési műveleteket kell végrehajtania egy lekérdezésen a csoportosítási vagy kiválasztási művelet után. Az alábbi példában a countries 10 milliós tartományban lévő populáció szerint vannak csoportosítva. A csoportok létrehozása után további záradékok szűrnek ki néhány csoportot, majd növekvő sorrendbe rendezik a csoportokat. A további műveletek végrehajtásához a countryGroup által képviselt folytatásra van szükség.

// percentileQuery is an IEnumerable<IGrouping<int, Country>>
var percentileQuery =
    from country in countries
    let percentile = (int)country.Population / 1_000
    group country by percentile into countryGroup
    where countryGroup.Key >= 20
    orderby countryGroup.Key
    select countryGroup;

// grouping is an IGrouping<int, Country>
foreach (var grouping in percentileQuery)
{
    Console.WriteLine(grouping.Key);
    foreach (var country in grouping)
    {
        Console.WriteLine(country.Name + ":" + country.Population);
    }
}

További információ: into.

Szűrés, rendelés és csatlakozás

A kezdő from záradék és a záró select vagy group záradék között az összes többi záradék (where, join, orderby, from, let) nem kötelező. A választható záradékok bármelyike nulla vagy többször is használható egy lekérdezés törzsében.

A WHERE záradék

A where záradék használatával kiszűrheti a forrásadatok elemeit egy vagy több predikátumkifejezés alapján. Az alábbi példában szereplő where záradék egy predikátumot tartalmaz két feltétellel.

IEnumerable<City> queryCityPop =
    from city in cities
    where city.Population is < 15_000_000 and > 10_000_000
    select city;

További információért lásd a(z) , ahol megtalálható a(z) záradék.

Az orderby záradék

A orderby záradék használatával növekvő vagy csökkenő sorrendbe rendezheti az eredményeket. Másodlagos rendezési rendeléseket is megadhat. Az alábbi példa egy elsődleges rendezést hajt végre a country objektumokon a Area tulajdonság használatával. Ezután másodlagos rendezést hajt végre a Population tulajdonság használatával.

IEnumerable<Country> querySortedCountries =
    from country in countries
    orderby country.Area, country.Population descending
    select country;

A ascending kulcsszó nem kötelező; ez az alapértelmezett rendezési sorrend, ha nincs megadva sorrend. További információért lásd: rendezési záradék.

Az illesztési záradék

A join záradék használatával társíthatja és/vagy kombinálhatja az egyik adatforrás elemeit egy másik adatforrás elemeivel az egyes elemek megadott kulcsainak egyenlőségi összehasonlítása alapján. A LINQ-ban az illesztési műveletek különböző típusú objektumok sorozatán hajthatók végre. Két sorozat összekapcsolása után egy select vagy group utasítással kell megadnia, hogy melyik elemet tárolja a kimeneti sorozatban. Névtelen típussal is kombinálhatja az egyes társított elemek tulajdonságait egy új típusba a kimeneti sorozathoz. Az alábbi példa prod objektumokat társít, amelyek Category tulajdonsága megegyezik a categories sztringtömb egyik kategóriájával. Azokat a termékeket, amelyek Category nem felelnek meg a categories bármely sztringjének, a rendszer kiszűri. A select utasítás egy új típust hoz létre, amelynek tulajdonságai mind a cat-ből, mind a prod-ből származnak.

var categoryQuery =
    from cat in categories
    join prod in products on cat equals prod.Category
    select new
    {
        Category = cat,
        Name = prod.Name
    };

Csoportillesztést úgy is végrehajthat, hogy a join művelet eredményeit egy ideiglenes változóba tárolja a into kulcsszó használatával. További információért lásd a(z) illesztési záradékot.

A let záradék

A let záradék használatával egy kifejezés eredményét( például metódushívást) egy új tartományváltozóban tárolhatja. Az alábbi példában a firstName tartományváltozó a Splitáltal visszaadott sztringek tömbjének első elemét tárolja.

string[] names = ["Svetlana Omelchenko", "Claire O'Donnell", "Sven Mortensen", "Cesar Garcia"];
IEnumerable<string> queryFirstNames =
    from name in names
    let firstName = name.Split(' ')[0]
    select firstName;

foreach (var s in queryFirstNames)
{
    Console.Write(s + " ");
}

//Output: Svetlana Claire Sven Cesar

További információért lásd a let kifejezést.

Lekérdezési kifejezésben szereplő albekérdezések

A lekérdezési záradékok maguk is tartalmazhatnak lekérdezési kifejezést, amelyet néha al-lekérdezésnéven is hívnak. Minden al lekérdezés saját from záradékkal kezdődik, amely nem feltétlenül ugyanarra az adatforrásra mutat az első from záradékban. Az alábbi lekérdezés például egy lekérdezési kifejezést jelenít meg, amelyet a választó utasítás használ a csoportosítási művelet eredményeinek lekéréséhez.

var queryGroupMax =
    from student in students
    group student by student.Year into studentGroup
    select new
    {
        Level = studentGroup.Key,
        HighestScore = (
            from student2 in studentGroup
            select student2.ExamScores.Average()
        ).Max()
    };

További információért lásd: Al-lekérdezés végrehajtása egy csoportosítási műveleten.

Lásd még: