Skip to content

Freshness pass on LINQ content #44410

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions docs/csharp/linq/get-started/query-expression-basics.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Query expression basics (LINQ)
description: Introduces concepts related to query expressions
ms.date: 03/06/2024
ms.date: 01/16/2025
---
# Query expression basics

Expand Down Expand Up @@ -52,7 +52,7 @@ A query expression must begin with a [from](../../language-reference/keywords/fr
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.

> [!NOTE]
> Examples in this article uses the following data source and sample data.
> Examples in this article use the following data source and sample data.

:::code language="csharp" source="./snippets/SnippetApp/DataSources.cs" id="basics_datasource":::

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

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

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

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:

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

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

#### group clause
#### The group clause

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.

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

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.

#### where clause
#### The where clause

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.

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

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

#### orderby clause
#### The orderby clause

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.

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

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).

#### join clause
#### The join clause

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`.

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

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).

#### let clause
#### The let clause

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`.

Expand Down
110 changes: 98 additions & 12 deletions docs/csharp/linq/get-started/snippets/SnippetApp/Basics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@
public static class Basics
{

static readonly int[] scores = [0]; // Max is called on this, so one value is needed
static readonly int[] scores =
[
0, 30, 50, 70, 80,
85, 94, 87, 96, 88,
59, 90, 91, 85, 60,
49, 100
];

// <SourceData>
static readonly City[] cities = [
Expand Down Expand Up @@ -50,6 +56,10 @@ where score > 80
orderby score descending
select score;
// </basics1>
foreach(var score in highScoresQuery)
{
Console.WriteLine(score);
}
}

public static void Basics2()
Expand All @@ -61,6 +71,10 @@ where score > 80
orderby score descending
select $"The score is {score}";
// </basics2>
foreach (var score in highScoresQuery2)
{
Console.WriteLine(score);
}
}

public static void Basics3()
Expand All @@ -72,6 +86,7 @@ where score > 80
select score
).Count();
// </basics3>
Console.WriteLine($"highest score: {highScoreCount}");
}

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

var scoreCount = highScoresQuery3.Count();
// </basics4>
Console.WriteLine($"highest score: {scoreCount}");
}

public static void Basics5()
Expand Down Expand Up @@ -122,7 +138,7 @@ public static void Basics6()
//Query syntax
IEnumerable<City> queryMajorCities =
from city in cities
where city.Population > 100000
where city.Population > 30_000_000
select city;

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

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

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

// Method-based syntax
IEnumerable<City> queryMajorCities2 = cities.Where(c => c.Population > 100000);
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 }
// </basics6>
}

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

public static void Basics7a()
Expand All @@ -178,6 +203,14 @@ where city.Population > 10000
select city;
var largeCitiesList2 = largeCitiesQuery.ToList();
// </basics7a>
foreach(var item in largeCitiesList)
{
Console.WriteLine(item.Name + ":" + item.Population);
}
foreach (var item in largeCitiesList2)
{
Console.WriteLine(item.Name + ":" + item.Population);
}
}

public static void Basics8()
Expand All @@ -188,16 +221,24 @@ from city in cities
where city.Population > 100000
select city;
// </basics8>
foreach (var city in queryCities)
{
Console.WriteLine(city.Name + ":" + city.Population);
}
}

public static void Basics9()
{
// <basics9>
IEnumerable<Country> countryAreaQuery =
from country in countries
where country.Area > 500000 //sq km
where country.Area > 20 //sq km
select country;
// </basics9>
foreach (var country in countryAreaQuery)
{
Console.WriteLine(country.Name + ":" + country.Area);
}
}

public static void Basics10()
Expand All @@ -209,6 +250,10 @@ from city in country.Cities
where city.Population > 10000
select city;
// </basics10>
foreach (var city in cityQuery)
{
Console.WriteLine(city.Name + ":" + city.Population);
}
}

public static void Basics11()
Expand All @@ -218,6 +263,14 @@ public static void Basics11()
from country in countries
group country by country.Name[0];
// </basics11>
foreach (var group in queryCountryGroups)
{
Console.WriteLine(group.Key);
foreach (var country in group)
{
Console.WriteLine(country.Name);
}
}
}

public static void Basics12()
Expand All @@ -228,6 +281,10 @@ from country in countries
orderby country.Area
select country;
// </basics12>
foreach (var country in sortedQuery)
{
Console.WriteLine(country.Name + ":" + country.Area);
}
}

public static void Basics13()
Expand All @@ -241,6 +298,10 @@ from country in countries
Pop = country.Population
};
// </basics13>
foreach (var item in queryNameAndPop)
{
Console.WriteLine(item.Name + ":" + item.Pop);
}
}

public static void Basics14()
Expand All @@ -249,7 +310,7 @@ public static void Basics14()
// percentileQuery is an IEnumerable<IGrouping<int, Country>>
var percentileQuery =
from country in countries
let percentile = (int)country.Population / 10_000_000
let percentile = (int)country.Population / 1_000
group country by percentile into countryGroup
where countryGroup.Key >= 20
orderby countryGroup.Key
Expand All @@ -272,9 +333,13 @@ public static void Basics15()
// <basics15>
IEnumerable<City> queryCityPop =
from city in cities
where city.Population is < 200000 and > 100000
where city.Population is < 15_000_000 and > 10_000_000
select city;
// </basics15>
foreach (var city in queryCityPop)
{
Console.WriteLine(city.Name + ":" + city.Population);
}
}

public static void Basics16()
Expand All @@ -285,12 +350,25 @@ from country in countries
orderby country.Area, country.Population descending
select country;
// </basics16>
foreach (var country in querySortedCountries)
{
Console.WriteLine(country.Name + ":" + country.Area + ":" + country.Population);
}
}

public static void Basics17()
{
string[] categories = [];
Product[] products = [];
string[] categories = ["brass", "winds", "percussion"];
Product[] products =
[
new Product("Trumpet", "brass"),
new Product("Trombone", "brass"),
new Product("French Horn", "brass"),
new Product("Clarinet", "winds"),
new Product("Flute", "winds"),
new Product("Cymbal", "percussion"),
new Product("Drum", "percussion")
];

// <basics17>
var categoryQuery =
Expand All @@ -302,6 +380,10 @@ join prod in products on cat equals prod.Category
Name = prod.Name
};
// </basics17>
foreach (var item in categoryQuery)
{
Console.WriteLine(item.Category + ":" + item.Name);
}
}

public static void Basics18()
Expand Down Expand Up @@ -339,5 +421,9 @@ select student2.ExamScores.Average()
).Max()
};
// </basics19>
foreach (var item in queryGroupMax)
{
Console.WriteLine(item.Level + ":" + item.HighestScore);
}
}
}
14 changes: 6 additions & 8 deletions docs/csharp/linq/get-started/snippets/SnippetApp/Exceptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@ public static void Exceptions1()
if (dataSource is not null)
{
// If we get here, it is safe to proceed.
var query =
from i in dataSource
select i * i;
var query = from i in dataSource
select i * i;

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

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

// Demonstration query that throws.
var exceptionDemoQuery =
from file in files
let n = SomeMethodThatMightThrow(file)
select n;
var exceptionDemoQuery = from file in files
let n = SomeMethodThatMightThrow(file)
select n;

try
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<RootNamespace>Linq.GetStarted</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
Expand Down
Loading
Loading