Skip to content

Commit c887106

Browse files
BillWagnergewarren
andauthored
Freshness pass on LINQ content (#44410)
* Fix open issues - Fixes #43760: Update where clause to filter out some values. Update output comment. - Fixes #43765: Update `let` statement so the query result isn't empty. - Fixes #43766: Update query parameters so the result set isn't empty. - Fixes #43768: Add sample data - Fixes #43790: Audit all samples in this article and ensure all have output. - Fixes #43809: Update samples to include the data sources. While doing this, update the project to .NET 9 * Review and update code. Hide whitespace for this commit. It's mostly formatting changes. * Edit pass. Grammar and edit pass. * Apply suggestions from code review Co-authored-by: Genevieve Warren <[email protected]> --------- Co-authored-by: Genevieve Warren <[email protected]>
1 parent 1c007f0 commit c887106

13 files changed

+223
-112
lines changed

docs/csharp/linq/get-started/query-expression-basics.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Query expression basics (LINQ)
33
description: Introduces concepts related to query expressions
4-
ms.date: 03/06/2024
4+
ms.date: 01/16/2025
55
---
66
# Query expression basics
77

@@ -52,7 +52,7 @@ A query expression must begin with a [from](../../language-reference/keywords/fr
5252
In LINQ, a query variable is any variable that stores a *query* instead of the *results* of a query. More specifically, a query variable is always an enumerable type that produces a sequence of elements when iterated over in a `foreach` statement or a direct call to its <xref:System.Collections.IEnumerator.MoveNext?displayProperty=nameWithType> method.
5353

5454
> [!NOTE]
55-
> Examples in this article uses the following data source and sample data.
55+
> Examples in this article use the following data source and sample data.
5656
5757
:::code language="csharp" source="./snippets/SnippetApp/DataSources.cs" id="basics_datasource":::
5858

@@ -62,7 +62,7 @@ The following code example shows a simple query expression with one data source,
6262

6363
:::code language="csharp" source="./snippets/SnippetApp/Basics.cs" id="basics5":::
6464

65-
In the previous example, `scoreQuery` is a *query variable,* which is sometimes referred to as just a *query*. The query variable stores no actual result data, which is produced in the `foreach` loop. And when the `foreach` statement executes, the query results aren't returned through the query variable `scoreQuery`. Rather, they're returned through the iteration variable `testScore`. The `scoreQuery` variable can be iterated in a second `foreach` loop. It produces the same results as long as neither it nor the data source has been modified.
65+
In the previous example, `scoreQuery` is a *query variable,* which is sometimes referred to as just a *query*. The query variable stores no actual result data, which is produced in the `foreach` loop. And when the `foreach` statement executes, the query results aren't returned through the query variable `scoreQuery`. Rather, they're returned through the iteration variable `testScore`. The `scoreQuery` variable can be iterated in a second `foreach` loop. It produces the same results as long as neither it nor the data source was modified.
6666

6767
A query variable might store a query that is expressed in query syntax or method syntax, or a combination of the two. In the following examples, both `queryMajorCities` and `queryMajorCities2` are query variables:
6868

@@ -100,7 +100,7 @@ For more information, see [from clause](../../language-reference/keywords/from-c
100100

101101
A query expression must end with either a `group` clause or a `select` clause.
102102

103-
#### group clause
103+
#### The group clause
104104

105105
Use the `group` clause to produce a sequence of groups organized by a key that you specify. The key can be any data type. For example, the following query creates a sequence of groups that contains one or more `Country` objects and whose key is a `char` type with value being the first letter of countries' names.
106106

@@ -134,31 +134,31 @@ For more information, see [into](../../language-reference/keywords/into.md).
134134

135135
Between the starting `from` clause, and the ending `select` or `group` clause, all other clauses (`where`, `join`, `orderby`, `from`, `let`) are optional. Any of the optional clauses might be used zero times or multiple times in a query body.
136136

137-
#### where clause
137+
#### The where clause
138138

139139
Use the `where` clause to filter out elements from the source data based on one or more predicate expressions. The `where` clause in the following example has one predicate with two conditions.
140140

141141
:::code language="csharp" source="./snippets/SnippetApp/Basics.cs" id="basics15":::
142142

143143
For more information, see [where clause](../../language-reference/keywords/where-clause.md).
144144

145-
#### orderby clause
145+
#### The orderby clause
146146

147147
Use the `orderby` clause to sort the results in either ascending or descending order. You can also specify secondary sort orders. The following example performs a primary sort on the `country` objects by using the `Area` property. It then performs a secondary sort by using the `Population` property.
148148

149149
:::code language="csharp" source="./snippets/SnippetApp/Basics.cs" id="basics16":::
150150

151151
The `ascending` keyword is optional; it's the default sort order if no order is specified. For more information, see [orderby clause](../../language-reference/keywords/orderby-clause.md).
152152

153-
#### join clause
153+
#### The join clause
154154

155155
Use the `join` clause to associate and/or combine elements from one data source with elements from another data source based on an equality comparison between specified keys in each element. In LINQ, join operations are performed on sequences of objects whose elements are different types. After you join two sequences, you must use a `select` or `group` statement to specify which element to store in the output sequence. You can also use an anonymous type to combine properties from each set of associated elements into a new type for the output sequence. The following example associates `prod` objects whose `Category` property matches one of the categories in the `categories` string array. Products whose `Category` doesn't match any string in `categories` are filtered out. The `select` statement projects a new type whose properties are taken from both `cat` and `prod`.
156156

157157
:::code language="csharp" source="./snippets/SnippetApp/Basics.cs" id="basics17":::
158158

159159
You can also perform a group join by storing the results of the `join` operation into a temporary variable by using the [into](../../language-reference/keywords/into.md) keyword. For more information, see [join clause](../../language-reference/keywords/join-clause.md).
160160

161-
#### let clause
161+
#### The let clause
162162

163163
Use the `let` clause to store the result of an expression, such as a method call, in a new range variable. In the following example, the range variable `firstName` stores the first element of the array of strings returned by `Split`.
164164

docs/csharp/linq/get-started/snippets/SnippetApp/Basics.cs

Lines changed: 98 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@
33
public static class Basics
44
{
55

6-
static readonly int[] scores = [0]; // Max is called on this, so one value is needed
6+
static readonly int[] scores =
7+
[
8+
0, 30, 50, 70, 80,
9+
85, 94, 87, 96, 88,
10+
59, 90, 91, 85, 60,
11+
49, 100
12+
];
713

814
// <SourceData>
915
static readonly City[] cities = [
@@ -50,6 +56,10 @@ where score > 80
5056
orderby score descending
5157
select score;
5258
// </basics1>
59+
foreach(var score in highScoresQuery)
60+
{
61+
Console.WriteLine(score);
62+
}
5363
}
5464

5565
public static void Basics2()
@@ -61,6 +71,10 @@ where score > 80
6171
orderby score descending
6272
select $"The score is {score}";
6373
// </basics2>
74+
foreach (var score in highScoresQuery2)
75+
{
76+
Console.WriteLine(score);
77+
}
6478
}
6579

6680
public static void Basics3()
@@ -72,6 +86,7 @@ where score > 80
7286
select score
7387
).Count();
7488
// </basics3>
89+
Console.WriteLine($"highest score: {highScoreCount}");
7590
}
7691

7792
public static void Basics4()
@@ -84,6 +99,7 @@ where score > 80
8499

85100
var scoreCount = highScoresQuery3.Count();
86101
// </basics4>
102+
Console.WriteLine($"highest score: {scoreCount}");
87103
}
88104

89105
public static void Basics5()
@@ -122,7 +138,7 @@ public static void Basics6()
122138
//Query syntax
123139
IEnumerable<City> queryMajorCities =
124140
from city in cities
125-
where city.Population > 100000
141+
where city.Population > 30_000_000
126142
select city;
127143

128144
// Execute the query to produce the results
@@ -132,12 +148,19 @@ where city.Population > 100000
132148
}
133149

134150
// Output:
135-
// City { Population = 120000 }
136-
// City { Population = 112000 }
137-
// City { Population = 150340 }
138-
151+
// City { Name = Tokyo, Population = 37833000 }
152+
// City { Name = Delhi, Population = 30290000 }
153+
139154
// Method-based syntax
140-
IEnumerable<City> queryMajorCities2 = cities.Where(c => c.Population > 100000);
155+
IEnumerable<City> queryMajorCities2 = cities.Where(c => c.Population > 30_000_000);
156+
// Execute the query to produce the results
157+
foreach (City city in queryMajorCities2)
158+
{
159+
Console.WriteLine(city);
160+
}
161+
// Output:
162+
// City { Name = Tokyo, Population = 37833000 }
163+
// City { Name = Delhi, Population = 30290000 }
141164
// </basics6>
142165
}
143166

@@ -158,6 +181,8 @@ from score in scores
158181
// the following returns the same result
159182
highScore = scores.Max();
160183
// </basics7>
184+
Console.WriteLine($"highest score: {highestScore}");
185+
Console.WriteLine($"high Score: {highScore}");
161186
}
162187

163188
public static void Basics7a()
@@ -178,6 +203,14 @@ where city.Population > 10000
178203
select city;
179204
var largeCitiesList2 = largeCitiesQuery.ToList();
180205
// </basics7a>
206+
foreach(var item in largeCitiesList)
207+
{
208+
Console.WriteLine(item.Name + ":" + item.Population);
209+
}
210+
foreach (var item in largeCitiesList2)
211+
{
212+
Console.WriteLine(item.Name + ":" + item.Population);
213+
}
181214
}
182215

183216
public static void Basics8()
@@ -188,16 +221,24 @@ from city in cities
188221
where city.Population > 100000
189222
select city;
190223
// </basics8>
224+
foreach (var city in queryCities)
225+
{
226+
Console.WriteLine(city.Name + ":" + city.Population);
227+
}
191228
}
192229

193230
public static void Basics9()
194231
{
195232
// <basics9>
196233
IEnumerable<Country> countryAreaQuery =
197234
from country in countries
198-
where country.Area > 500000 //sq km
235+
where country.Area > 20 //sq km
199236
select country;
200237
// </basics9>
238+
foreach (var country in countryAreaQuery)
239+
{
240+
Console.WriteLine(country.Name + ":" + country.Area);
241+
}
201242
}
202243

203244
public static void Basics10()
@@ -209,6 +250,10 @@ from city in country.Cities
209250
where city.Population > 10000
210251
select city;
211252
// </basics10>
253+
foreach (var city in cityQuery)
254+
{
255+
Console.WriteLine(city.Name + ":" + city.Population);
256+
}
212257
}
213258

214259
public static void Basics11()
@@ -218,6 +263,14 @@ public static void Basics11()
218263
from country in countries
219264
group country by country.Name[0];
220265
// </basics11>
266+
foreach (var group in queryCountryGroups)
267+
{
268+
Console.WriteLine(group.Key);
269+
foreach (var country in group)
270+
{
271+
Console.WriteLine(country.Name);
272+
}
273+
}
221274
}
222275

223276
public static void Basics12()
@@ -228,6 +281,10 @@ from country in countries
228281
orderby country.Area
229282
select country;
230283
// </basics12>
284+
foreach (var country in sortedQuery)
285+
{
286+
Console.WriteLine(country.Name + ":" + country.Area);
287+
}
231288
}
232289

233290
public static void Basics13()
@@ -241,6 +298,10 @@ from country in countries
241298
Pop = country.Population
242299
};
243300
// </basics13>
301+
foreach (var item in queryNameAndPop)
302+
{
303+
Console.WriteLine(item.Name + ":" + item.Pop);
304+
}
244305
}
245306

246307
public static void Basics14()
@@ -249,7 +310,7 @@ public static void Basics14()
249310
// percentileQuery is an IEnumerable<IGrouping<int, Country>>
250311
var percentileQuery =
251312
from country in countries
252-
let percentile = (int)country.Population / 10_000_000
313+
let percentile = (int)country.Population / 1_000
253314
group country by percentile into countryGroup
254315
where countryGroup.Key >= 20
255316
orderby countryGroup.Key
@@ -272,9 +333,13 @@ public static void Basics15()
272333
// <basics15>
273334
IEnumerable<City> queryCityPop =
274335
from city in cities
275-
where city.Population is < 200000 and > 100000
336+
where city.Population is < 15_000_000 and > 10_000_000
276337
select city;
277338
// </basics15>
339+
foreach (var city in queryCityPop)
340+
{
341+
Console.WriteLine(city.Name + ":" + city.Population);
342+
}
278343
}
279344

280345
public static void Basics16()
@@ -285,12 +350,25 @@ from country in countries
285350
orderby country.Area, country.Population descending
286351
select country;
287352
// </basics16>
353+
foreach (var country in querySortedCountries)
354+
{
355+
Console.WriteLine(country.Name + ":" + country.Area + ":" + country.Population);
356+
}
288357
}
289358

290359
public static void Basics17()
291360
{
292-
string[] categories = [];
293-
Product[] products = [];
361+
string[] categories = ["brass", "winds", "percussion"];
362+
Product[] products =
363+
[
364+
new Product("Trumpet", "brass"),
365+
new Product("Trombone", "brass"),
366+
new Product("French Horn", "brass"),
367+
new Product("Clarinet", "winds"),
368+
new Product("Flute", "winds"),
369+
new Product("Cymbal", "percussion"),
370+
new Product("Drum", "percussion")
371+
];
294372

295373
// <basics17>
296374
var categoryQuery =
@@ -302,6 +380,10 @@ join prod in products on cat equals prod.Category
302380
Name = prod.Name
303381
};
304382
// </basics17>
383+
foreach (var item in categoryQuery)
384+
{
385+
Console.WriteLine(item.Category + ":" + item.Name);
386+
}
305387
}
306388

307389
public static void Basics18()
@@ -339,5 +421,9 @@ select student2.ExamScores.Average()
339421
).Max()
340422
};
341423
// </basics19>
424+
foreach (var item in queryGroupMax)
425+
{
426+
Console.WriteLine(item.Level + ":" + item.HighestScore);
427+
}
342428
}
343429
}

docs/csharp/linq/get-started/snippets/SnippetApp/Exceptions.cs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,8 @@ public static void Exceptions1()
2323
if (dataSource is not null)
2424
{
2525
// If we get here, it is safe to proceed.
26-
var query =
27-
from i in dataSource
28-
select i * i;
26+
var query = from i in dataSource
27+
select i * i;
2928

3029
foreach (var i in query)
3130
{
@@ -42,16 +41,15 @@ public static void Exceptions2()
4241
string SomeMethodThatMightThrow(string s) =>
4342
s[4] == 'C' ?
4443
throw new InvalidOperationException() :
45-
@"C:\newFolder\" + s;
44+
$"""C:\newFolder\{s}""";
4645

4746
// Data source.
4847
string[] files = ["fileA.txt", "fileB.txt", "fileC.txt"];
4948

5049
// Demonstration query that throws.
51-
var exceptionDemoQuery =
52-
from file in files
53-
let n = SomeMethodThatMightThrow(file)
54-
select n;
50+
var exceptionDemoQuery = from file in files
51+
let n = SomeMethodThatMightThrow(file)
52+
select n;
5553

5654
try
5755
{

docs/csharp/linq/get-started/snippets/SnippetApp/LinqGetStarted.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net8.0</TargetFramework>
5+
<TargetFramework>net9.0</TargetFramework>
66
<RootNamespace>Linq.GetStarted</RootNamespace>
77
<ImplicitUsings>enable</ImplicitUsings>
88
<Nullable>enable</Nullable>

0 commit comments

Comments
 (0)