Dela via


Grunderna i frågeuttryck

Den här artikeln beskriver grundläggande begrepp som rör frågeuttryck i C#.

Vad är en fråga och vad gör den?

En fråga är en uppsättning instruktioner som beskriver vilka data som ska hämtas från en viss datakälla (eller källor) och vilken form och organisation de returnerade data ska ha. En fråga skiljer sig från de resultat som den producerar.

Vanligtvis ordnas källdata logiskt som en sekvens med element av samma slag. En SQL-databastabell innehåller till exempel en sekvens med rader. I en XML-fil finns det en "sekvens" av XML-element (även om XML-element ordnas hierarkiskt i en trädstruktur). En minnesintern samling innehåller en sekvens med objekt.

Från ett programs synvinkel är den specifika typen och strukturen för de ursprungliga källdata inte viktig. Programmet ser alltid källdata som en IEnumerable<T> eller IQueryable<T> samling. I LINQ till XML visas till exempel källdata som en IEnumerableXElement<>.

Med tanke på den här källsekvensen kan en fråga göra något av tre saker:

  • Hämta en delmängd av elementen för att skapa en ny sekvens utan att ändra de enskilda elementen. Frågan kan sedan sortera eller gruppera den returnerade sekvensen på olika sätt, som du ser i följande exempel (anta scores är en int[]):

    IEnumerable<int> highScoresQuery =
        from score in scores
        where score > 80
        orderby score descending
        select score;
    
  • Hämta en sekvens med element som i föregående exempel men transformera dem till en ny typ av objekt. En fråga kan till exempel bara hämta familjenamnen från vissa kundposter i en datakälla. Eller så kan den hämta den fullständiga posten och sedan använda den för att konstruera en annan minnesintern objekttyp eller till och med XML-data innan den slutliga resultatsekvensen genereras. I följande exempel visas en projektion från en int till en string. Observera den nya typen av highScoresQuery.

    IEnumerable<string> highScoresQuery2 =
        from score in scores
        where score > 80
        orderby score descending
        select $"The score is {score}";
    
  • Hämta ett singleton-värde om källdata, till exempel:

    • Antalet element som matchar ett visst villkor.

    • Det element som har det största eller minsta värdet.

    • Det första elementet som matchar ett villkor eller summan av vissa värden i en angiven uppsättning element. Följande fråga returnerar till exempel antalet poäng större än 80 från heltalsmatrisen scores :

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

      I föregående exempel bör du notera användningen av parenteser runt frågeuttrycket före anropet Enumerable.Count till metoden. Du kan också använda en ny variabel för att lagra det konkreta resultatet.

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

I föregående exempel körs frågan i anropet till Count, eftersom Count måste iterera över resultaten för att fastställa antalet element som returneras av highScoresQuery.

Vad är ett frågeuttryck?

Ett frågeuttryck är en fråga som uttrycks i frågesyntaxen. Ett frågeuttryck är en förstklassig språkkonstruktion. Det är precis som alla andra uttryck och kan användas i alla sammanhang där ett C#-uttryck är giltigt. Ett frågeuttryck består av en uppsättning satser skrivna i en deklarativ syntax som liknar SQL eller XQuery. Varje sats innehåller i sin tur ett eller flera C#-uttryck, och dessa uttryck kan i sig vara antingen ett frågeuttryck eller innehålla ett frågeuttryck.

Ett frågeuttryck måste börja med en from-sats och måste avslutas med en select - eller grupp-sats . Mellan den första from satsen och den sista select eller group -satsen kan den innehålla en eller flera av dessa valfria satser: där, orderby, join, let och even another from clauses. Du kan också använda in i nyckelordet för att aktivera resultatet av en join eller group -sats för att fungera som källa för fler frågesatser i samma frågeuttryck.

Frågevariabel

I LINQ är en frågevariabel en variabel som lagrar en fråga i stället för resultatet av en fråga. Mer specifikt är en frågevariabel alltid en uppräkningsbar typ som skapar en sekvens med element när den itereras över i en foreach -instruktion eller ett direktanrop till dess IEnumerator.MoveNext() -metod.

Kommentar

Exempel i den här artikeln använder följande datakälla och exempeldata.

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

I följande kodexempel visas ett enkelt frågeuttryck med en datakälla, en filtreringssats, en beställningssats och ingen transformering av källelementen. - select satsen avslutar frågan.

// 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

I föregående exempel scoreQuery är en frågevariabel, som ibland bara kallas för en fråga. Frågevariabeln lagrar inga faktiska resultatdata, som skapas i loopen foreach . Och när -instruktionen foreach körs returneras inte frågeresultatet via frågevariabeln scoreQuery. I stället returneras de via iterationsvariabeln testScore. Variabeln scoreQuery kan itereras i en andra foreach loop. Den ger samma resultat så länge varken den eller datakällan har ändrats.

En frågevariabel kan lagra en fråga som uttrycks i frågesyntax eller metodsyntax, eller en kombination av de två. I följande exempel är både queryMajorCities och queryMajorCities2 frågevariabler:

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 > 100000
    select city;

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

// Output:
// City { Population = 120000 }
// City { Population = 112000 }
// City { Population = 150340 }

// Method-based syntax
IEnumerable<City> queryMajorCities2 = cities.Where(c => c.Population > 100000);

Å andra sidan visar följande två exempel variabler som inte är frågevariabler trots att var och en initieras med en fråga. De är inte frågevariabler eftersom de lagrar resultat:

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

Explicit och implicit typning av frågevariabler

Den här dokumentationen innehåller vanligtvis den explicita typen av frågevariabel för att visa typrelationen mellan frågevariabeln och select-satsen. Du kan dock också använda nyckelordet var för att instruera kompilatorn att härleda typen av en frågevariabel (eller någon annan lokal variabel) vid kompileringstiden. Frågeexemplet som visades tidigare i den här artikeln kan till exempel också uttryckas med implicit inmatning:

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

I föregående exempel är användningen av var valfri. queryCities är en IEnumerable<City> implicit eller explicit typ.

Starta ett frågeuttryck

Ett frågeuttryck måste börja med en from sats. Den anger en datakälla tillsammans med en intervallvariabel. Intervallvariabeln representerar varje efterföljande element i källsekvensen när källsekvensen bläddras igenom. Intervallvariabeln skrivs starkt baserat på typen av element i datakällan. I följande exempel, eftersom countries är en matris med Country objekt, skrivs även intervallvariabeln som Country. Eftersom intervallvariabeln är starkt skriven kan du använda punktoperatorn för att komma åt alla tillgängliga medlemmar av typen.

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

Intervallvariabeln finns i omfånget tills frågan avslutas antingen med ett semikolon eller med en fortsättningssats .

Ett frågeuttryck kan innehålla flera from satser. Använd fler from satser när varje element i källsekvensen i sig är en samling eller innehåller en samling. Anta till exempel att du har en samling Country objekt som var och en innehåller en samling City objekt med namnet Cities. Om du vill köra frågor mot objekten City i varje Countryanvänder du två from satser som visas här:

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

Mer information finns i från -satsen.

Avsluta ett frågeuttryck

Ett frågeuttryck måste sluta med antingen en group sats eller en select -sats.

gruppsats

group Använd -satsen för att skapa en sekvens med grupper ordnade efter en nyckel som du anger. Nyckeln kan vara vilken datatyp som helst. Följande fråga skapar till exempel en sekvens med grupper som innehåller ett eller flera Country objekt och vars nyckel är en char typ där värdet är den första bokstaven i ländernas namn.

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

Mer information om gruppering finns i gruppsatsen.

select-sats

select Använd -satsen för att skapa alla andra typer av sekvenser. En enkel select sats genererar bara en sekvens av samma typ av objekt som objekten som finns i datakällan. I det här exemplet innehåller Country datakällan objekt. - orderby satsen sorterar bara elementen i en ny ordning och select satsen genererar en sekvens av de omsorterade Country objekten.

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

Satsen select kan användas för att omvandla källdata till sekvenser av nya typer. Den här omvandlingen heter också en projektion. I följande exempel select projicerar satsen en sekvens av anonyma typer som endast innehåller en delmängd av fälten i det ursprungliga elementet. De nya objekten initieras med hjälp av en objektinitierare.

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

Så i det här exemplet var krävs eftersom frågan genererar en anonym typ.

Mer information om alla sätt som en select sats kan användas för att transformera källdata finns i select-satsen.

Fortsättningar med in i

Du kan använda nyckelordet into i en select eller group -sats för att skapa en tillfällig identifierare som lagrar en fråga. Använd - into satsen när du måste utföra extra frågeåtgärder på en fråga efter en grupperings- eller urvalsåtgärd. I följande exempel countries grupperas enligt populationen i intervall på 10 miljoner. När dessa grupper har skapats filtrerar fler satser bort vissa grupper och sorterar sedan grupperna i stigande ordning. För att utföra dessa extra åtgärder krävs fortsättningen som representeras av countryGroup .

// percentileQuery is an IEnumerable<IGrouping<int, Country>>
var percentileQuery =
    from country in countries
    let percentile = (int)country.Population / 10_000_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);
    }
}

Mer information finns i.

Filtrering, ordning och anslutning

Mellan startsatsen from och slutet select eller group -satsen är alla andra satser (where, , joinorderby, from, let) valfria. Valfria satser kan användas noll gånger eller flera gånger i en frågetext.

where-sats

where Använd -satsen för att filtrera bort element från källdata baserat på ett eller flera predikatuttryck. where Satsen i följande exempel har ett predikat med två villkor.

IEnumerable<City> queryCityPop =
    from city in cities
    where city.Population is < 200000 and > 100000
    select city;

Mer information finns i where-sats.

orderby-sats

orderby Använd -satsen för att sortera resultatet i antingen stigande eller fallande ordning. Du kan också ange sekundära sorteringsordningar. I följande exempel utförs en primär sortering av objekten country med hjälp Area av egenskapen . Den utför sedan en sekundär sortering med hjälp Population av egenskapen .

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

Nyckelordet ascending är valfritt. Det är standardsorteringsordningen om ingen ordning har angetts. Mer information finns i orderby-satsen.

kopplingssats

join Använd -satsen för att associera och/eller kombinera element från en datakälla med element från en annan datakälla baserat på en likhetsjämförelse mellan angivna nycklar i varje element. I LINQ utförs kopplingsåtgärder på sekvenser av objekt vars element är olika typer. När du har sammanfogat två sekvenser måste du använda en select -instruktion för group att ange vilket element som ska lagras i utdatasekvensen. Du kan också använda en anonym typ för att kombinera egenskaper från varje uppsättning associerade element till en ny typ för utdatasekvensen. I följande exempel associeras prod objekt vars Category egenskap matchar en av kategorierna i strängmatrisen categories . Produkter vars Category sträng inte matchar i categories filtreras bort. Instruktionen select projicerar en ny typ vars egenskaper hämtas från både cat och prod.

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

Du kan också utföra en gruppkoppling genom att lagra resultatet av join åtgärden i en tillfällig variabel med hjälp av nyckelordet i . Mer information finns i kopplingssatsen.

let-sats

let Använd -satsen för att lagra resultatet av ett uttryck, till exempel ett metodanrop, i en ny intervallvariabel. I följande exempel lagrar intervallvariabeln firstName det första elementet i matrisen med strängar som returneras av Split.

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

Mer information finns i let-satsen.

Underfrågor i ett frågeuttryck

En frågesats kan i sig innehålla ett frågeuttryck, som ibland kallas för en underfråga. Varje underfråga börjar med en egen from sats som inte nödvändigtvis pekar på samma datakälla i den första from satsen. Följande fråga visar till exempel ett frågeuttryck som används i select-instruktionen för att hämta resultatet av en grupperingsåtgärd.

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()
    };

Mer information finns i Utföra en underfråga för en grupperingsåtgärd.

Se även