Basisbeginselen van query-expressies
In dit artikel worden de basisconcepten geïntroduceerd die betrekking hebben op query-expressies in C#.
Wat is een query en wat doet deze?
Een query is een reeks instructies die beschrijven welke gegevens moeten worden opgehaald uit een bepaalde gegevensbron (of bronnen) en welke vorm en organisatie de geretourneerde gegevens moeten hebben. Een query verschilt van de resultaten die worden geproduceerd.
Over het algemeen worden de brongegevens logisch ingedeeld als een reeks elementen van hetzelfde type. Een SQL-databasetabel bevat bijvoorbeeld een reeks rijen. In een XML-bestand is er een 'reeks' XML-elementen (hoewel XML-elementen hiërarchisch zijn ingedeeld in een structuurstructuur). Een in-memory verzameling bevat een reeks objecten.
Vanuit het oogpunt van een toepassing is het specifieke type en de structuur van de oorspronkelijke brongegevens niet belangrijk. De toepassing ziet altijd de brongegevens als een IEnumerable<T> of IQueryable<T> verzameling. In LINQ naar XML worden de brongegevens bijvoorbeeld zichtbaar gemaakt als een IEnumerable
XElement<>.
Op basis van deze bronvolgorde kan een query een van de volgende drie dingen doen:
Haal een subset van de elementen op om een nieuwe reeks te produceren zonder de afzonderlijke elementen te wijzigen. De query kan vervolgens de geretourneerde volgorde op verschillende manieren sorteren of groeperen, zoals wordt weergegeven in het volgende voorbeeld (stel dat
scores
dit eenint[]
is):IEnumerable<int> highScoresQuery = from score in scores where score > 80 orderby score descending select score;
Haal een reeks elementen op zoals in het vorige voorbeeld, maar transformeer ze naar een nieuw type object. Een query kan bijvoorbeeld alleen de familienamen ophalen uit bepaalde klantrecords in een gegevensbron. Of het kan de volledige record ophalen en vervolgens gebruiken om een ander objecttype in het geheugen of zelfs XML-gegevens te maken voordat de uiteindelijke resultatenreeks wordt gegenereerd. In het volgende voorbeeld ziet u een projectie van een
int
naar eenstring
. Let op het nieuwe typehighScoresQuery
.IEnumerable<string> highScoresQuery2 = from score in scores where score > 80 orderby score descending select $"The score is {score}";
Haal een singleton-waarde op over de brongegevens, zoals:
Het aantal elementen dat overeenkomt met een bepaalde voorwaarde.
Het element met de grootste of laagste waarde.
Het eerste element dat overeenkomt met een voorwaarde of de som van bepaalde waarden in een opgegeven set elementen. Met de volgende query wordt bijvoorbeeld het aantal scores geretourneerd dat groter is dan 80 uit de
scores
matrix met gehele getallen:var highScoreCount = ( from score in scores where score > 80 select score ).Count();
Let in het vorige voorbeeld op het gebruik van haakjes rond de query-expressie voordat de aanroep naar de Enumerable.Count methode wordt uitgevoerd. U kunt ook een nieuwe variabele gebruiken om het concrete resultaat op te slaan.
IEnumerable<int> highScoresQuery3 = from score in scores where score > 80 select score; var scoreCount = highScoresQuery3.Count();
In het vorige voorbeeld wordt de query uitgevoerd in de aanroep naar Count
, omdat Count
de resultaten moeten worden herhaald om het aantal elementen te bepalen dat wordt geretourneerd door highScoresQuery
.
Wat is een query-expressie?
Een query-expressie is een query die wordt uitgedrukt in de querysyntaxis. Een query-expressie is een eersteklas taalconstructie. Het is net als elke andere expressie en kan worden gebruikt in elke context waarin een C#-expressie geldig is. Een query-expressie bestaat uit een set componenten die zijn geschreven in een declaratieve syntaxis die vergelijkbaar is met SQL of XQuery. Elke component bevat een of meer C#-expressies en deze expressies kunnen zelf een query-expressie zijn of een query-expressie bevatten.
Een query-expressie moet beginnen met een from-component en moet eindigen met een select - of groepscomponent . Tussen de eerste component en de laatste from
select
of group
component kan deze een of meer van deze optionele componenten bevatten: waarbij, orderby, join, let en zelfs een andere component van componenten. U kunt het in trefwoord ook gebruiken om het resultaat van een join
of group
component in te schakelen als de bron voor meer queryclausules in dezelfde query-expressie.
Queryvariabele
In LINQ is een queryvariabele een variabele waarmee een query wordt opgeslagen in plaats van de resultaten van een query. In het bijzonder is een queryvariabele altijd een opsommingstype dat een reeks elementen produceert wanneer deze in een foreach
instructie wordt overschreven of een directe aanroep van IEnumerator.MoveNext() de methode.
Notitie
In voorbeelden in dit artikel worden de volgende gegevensbron en voorbeeldgegevens gebruikt.
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)])
];
In het volgende codevoorbeeld ziet u een eenvoudige query-expressie met één gegevensbron, één filtercomponent, één bestelcomponent en geen transformatie van de bronelementen. De select
component beëindigt de query.
// 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
In het vorige voorbeeld scoreQuery
is een queryvariabele, die soms alleen een query wordt genoemd. De queryvariabele slaat geen werkelijke resultaatgegevens op, die in de foreach
lus worden geproduceerd. En wanneer de foreach
instructie wordt uitgevoerd, worden de queryresultaten niet geretourneerd via de queryvariabele scoreQuery
. In plaats daarvan worden ze geretourneerd via de iteratievariabele testScore
. De scoreQuery
variabele kan in een tweede foreach
lus worden herhaald. Het produceert dezelfde resultaten zolang deze noch de gegevensbron is gewijzigd.
Een queryvariabele kan een query opslaan die wordt uitgedrukt in querysyntaxis of methodesyntaxis, of een combinatie van de twee. In de volgende voorbeelden zijn beide queryMajorCities
queryvariabelen queryMajorCities2
:
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);
Aan de andere kant tonen de volgende twee voorbeelden variabelen die geen queryvariabelen zijn, ook al worden ze geïnitialiseerd met een query. Ze zijn geen queryvariabelen omdat ze resultaten opslaan:
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();
Expliciet en impliciet typen van queryvariabelen
Deze documentatie bevat meestal het expliciete type van de queryvariabele om de typerelatie tussen de queryvariabele en de select-component weer te geven. U kunt echter ook het trefwoord var gebruiken om de compiler te instrueren het type queryvariabele (of een andere lokale variabele) tijdens het compileren af te stellen. Het queryvoorbeeld dat eerder in dit artikel is weergegeven, kan bijvoorbeeld ook worden uitgedrukt met impliciet typen:
var queryCities =
from city in cities
where city.Population > 100000
select city;
In het voorgaande voorbeeld is het gebruik van var optioneel. queryCities
is een IEnumerable<City>
impliciete of expliciet getypte.
Een query-expressie starten
Een query-expressie moet beginnen met een from
component. Hiermee geeft u een gegevensbron samen met een bereikvariabele op. De bereikvariabele vertegenwoordigt elk opeenvolgend element in de bronreeks wanneer de bronreeks wordt doorkruist. De bereikvariabele wordt sterk getypt op basis van het type elementen in de gegevensbron. In het volgende voorbeeld, omdat countries
dit een matrix van Country
objecten is, wordt de bereikvariabele ook getypt als Country
. Omdat de bereikvariabele sterk is getypt, kunt u de puntoperator gebruiken om toegang te krijgen tot alle beschikbare leden van het type.
IEnumerable<Country> countryAreaQuery =
from country in countries
where country.Area > 500000 //sq km
select country;
De bereikvariabele valt binnen het bereik totdat de query wordt afgesloten met een puntkomma of met een vervolgcomponent .
Een query-expressie kan meerdere from
componenten bevatten. Gebruik meer from
componenten wanneer elk element in de bronreeks zelf een verzameling is of een verzameling bevat. Stel dat u een verzameling Country
objecten hebt die elk een verzameling City
objecten bevat met de naam Cities
. Als u een query wilt uitvoeren op de City
objecten in elk Country
object, gebruikt u twee from
componenten, zoals hier wordt weergegeven:
IEnumerable<City> cityQuery =
from country in countries
from city in country.Cities
where city.Population > 10000
select city;
Zie de component from voor meer informatie.
Een query-expressie beëindigen
Een query-expressie moet eindigen met een group
component of een select
component.
group component
Gebruik de group
component om een reeks groepen te produceren die zijn ingedeeld op basis van een sleutel die u opgeeft. De sleutel kan elk gegevenstype zijn. Met de volgende query wordt bijvoorbeeld een reeks groepen gemaakt die een of meer Country
objecten bevat en waarvan de sleutel een char
type is met de waarde die de eerste letter van de namen van landen is.
var queryCountryGroups =
from country in countries
group country by country.Name[0];
Zie de groepscomponent voor meer informatie over groeperen.
component selecteren
Gebruik de select
component om alle andere typen reeksen te produceren. Een eenvoudige select
component produceert alleen een reeks van hetzelfde type objecten als de objecten die zich in de gegevensbron bevinden. In dit voorbeeld bevat Country
de gegevensbron objecten. De orderby
component sorteert de elementen in een nieuwe volgorde en de select
component produceert een reeks van de opnieuw gerangschikte Country
objecten.
IEnumerable<Country> sortedQuery =
from country in countries
orderby country.Area
select country;
De select
component kan worden gebruikt om brongegevens te transformeren in reeksen van nieuwe typen. Deze transformatie heet ook een projectie. In het volgende voorbeeld projecteert de select
component een reeks anonieme typen die slechts een subset van de velden in het oorspronkelijke element bevatten. De nieuwe objecten worden geïnitialiseerd met behulp van een object-initialisatiefunctie.
var queryNameAndPop =
from country in countries
select new
{
Name = country.Name,
Pop = country.Population
};
In dit voorbeeld is de var
vereiste omdat de query een anoniem type produceert.
Zie de select-component voor meer informatie over alle manieren waarop een select
component kan worden gebruikt om brongegevens te transformeren.
Vervolgen met in
U kunt het into
trefwoord in een select
of group
component gebruiken om een tijdelijke id te maken waarin een query wordt opgeslagen. Gebruik de into
component wanneer u extra querybewerkingen op een query moet uitvoeren na een groeperings- of selectiebewerking. In het volgende voorbeeld countries
worden ze gegroepeerd op basis van de populatie in bereiken van 10 miljoen. Nadat deze groepen zijn gemaakt, filteren meer componenten sommige groepen en sorteert u de groepen in oplopende volgorde. Als u deze extra bewerkingen wilt uitvoeren, is de voortzetting countryGroup
vereist.
// 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);
}
}
Zie voor meer informatie.
Filteren, ordenen en samenvoegen
Tussen de beginclausule from
en het einde select
of group
de component zijn alle andere componenten (where
, join
, orderby
, from
) let
optioneel. Een van de optionele componenten kan nul of meerdere keren worden gebruikt in een querytekst.
where-component
Gebruik de where
component om elementen uit de brongegevens te filteren op basis van een of meer predicaatexpressies. De where
component in het volgende voorbeeld heeft één predicaat met twee voorwaarden.
IEnumerable<City> queryCityPop =
from city in cities
where city.Population is < 200000 and > 100000
select city;
Zie de where-component voor meer informatie.
orderby-component
Gebruik de orderby
component om de resultaten in oplopende of aflopende volgorde te sorteren. U kunt ook secundaire sorteervolgordes opgeven. In het volgende voorbeeld wordt een primaire sortering uitgevoerd op de country
objecten met behulp van de Area
eigenschap. Vervolgens wordt een secundaire sortering uitgevoerd met behulp van de Population
eigenschap.
IEnumerable<Country> querySortedCountries =
from country in countries
orderby country.Area, country.Population descending
select country;
Het trefwoord is optioneel. Dit ascending
is de standaardsortering als er geen volgorde is opgegeven. Zie de orderby-component voor meer informatie.
join-component
Gebruik de join
component om elementen van de ene gegevensbron te koppelen en/of te combineren met elementen uit een andere gegevensbron op basis van een gelijkheidsvergelijking tussen opgegeven sleutels in elk element. In LINQ worden joinbewerkingen uitgevoerd op reeksen objecten waarvan de elementen verschillende typen zijn. Nadat u twee reeksen hebt samengevoegd, moet u een select
of group
instructie gebruiken om op te geven welk element moet worden opgeslagen in de uitvoerreeks. U kunt ook een anoniem type gebruiken om eigenschappen van elke set gekoppelde elementen te combineren in een nieuw type voor de uitvoervolgorde. In het volgende voorbeeld worden objecten gekoppeld prod
waarvan Category
de eigenschap overeenkomt met een van de categorieën in de categories
tekenreeksmatrix. Producten waarvan Category
de tekenreeks categories
niet overeenkomt, worden uitgefilterd. De select
instructie projecteert een nieuw type waarvan de eigenschappen afkomstig zijn van zowel cat
als prod
.
var categoryQuery =
from cat in categories
join prod in products on cat equals prod.Category
select new
{
Category = cat,
Name = prod.Name
};
U kunt ook een groepsdeelname uitvoeren door de resultaten van de join
bewerking op te slaan in een tijdelijke variabele met behulp van het trefwoord in . Zie join-component voor meer informatie.
let-component
Gebruik de let
component om het resultaat van een expressie, zoals een methodeaanroep, op te slaan in een nieuwe bereikvariabele. In het volgende voorbeeld slaat de bereikvariabele firstName
het eerste element op van de matrix met tekenreeksen die worden geretourneerd door 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
Zie let-component voor meer informatie.
Subquery's in een query-expressie
Een querycomponent kan zelf een query-expressie bevatten, die soms een subquery wordt genoemd. Elke subquery begint met een eigen from
component die niet noodzakelijkerwijs verwijst naar dezelfde gegevensbron in de eerste from
component. De volgende query toont bijvoorbeeld een query-expressie die wordt gebruikt in de select-instructie om de resultaten van een groeperingsbewerking op te halen.
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()
};
Zie Een subquery uitvoeren voor een groeperingsbewerking voor meer informatie.
Zie ook
Feedback
https://aka.ms/ContentUserFeedback.
Binnenkort beschikbaar: In de loop van 2024 zullen we GitHub-problemen geleidelijk uitfaseren als het feedbackmechanisme voor inhoud en deze vervangen door een nieuw feedbacksysteem. Zie voor meer informatie:Feedback verzenden en weergeven voor