Skip to content

Commit

Permalink
Freshness pass on LINQ content (#44410)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
BillWagner and gewarren authored Jan 17, 2025
1 parent 1c007f0 commit c887106
Show file tree
Hide file tree
Showing 13 changed files with 223 additions and 112 deletions.
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

0 comments on commit c887106

Please sign in to comment.