diff --git a/docs/csharp/linq/get-started/query-expression-basics.md b/docs/csharp/linq/get-started/query-expression-basics.md index bc32690523a9a..e0cc368e9ae8d 100644 --- a/docs/csharp/linq/get-started/query-expression-basics.md +++ b/docs/csharp/linq/get-started/query-expression-basics.md @@ -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 @@ -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 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"::: @@ -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: @@ -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. @@ -134,7 +134,7 @@ 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. @@ -142,7 +142,7 @@ Use the `where` clause to filter out elements from the source data based on one 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. @@ -150,7 +150,7 @@ Use the `orderby` clause to sort the results in either ascending or descending o 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`. @@ -158,7 +158,7 @@ Use the `join` clause to associate and/or combine elements from one data source 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`. diff --git a/docs/csharp/linq/get-started/snippets/SnippetApp/Basics.cs b/docs/csharp/linq/get-started/snippets/SnippetApp/Basics.cs index 9ce7f6c4ef4c3..624b74483c9b0 100644 --- a/docs/csharp/linq/get-started/snippets/SnippetApp/Basics.cs +++ b/docs/csharp/linq/get-started/snippets/SnippetApp/Basics.cs @@ -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 + ]; // static readonly City[] cities = [ @@ -50,6 +56,10 @@ where score > 80 orderby score descending select score; // + foreach(var score in highScoresQuery) + { + Console.WriteLine(score); + } } public static void Basics2() @@ -61,6 +71,10 @@ where score > 80 orderby score descending select $"The score is {score}"; // + foreach (var score in highScoresQuery2) + { + Console.WriteLine(score); + } } public static void Basics3() @@ -72,6 +86,7 @@ where score > 80 select score ).Count(); // + Console.WriteLine($"highest score: {highScoreCount}"); } public static void Basics4() @@ -84,6 +99,7 @@ where score > 80 var scoreCount = highScoresQuery3.Count(); // + Console.WriteLine($"highest score: {scoreCount}"); } public static void Basics5() @@ -122,7 +138,7 @@ public static void Basics6() //Query syntax IEnumerable queryMajorCities = from city in cities - where city.Population > 100000 + where city.Population > 30_000_000 select city; // Execute the query to produce the results @@ -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 queryMajorCities2 = cities.Where(c => c.Population > 100000); + IEnumerable 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 } // } @@ -158,6 +181,8 @@ from score in scores // the following returns the same result highScore = scores.Max(); // + Console.WriteLine($"highest score: {highestScore}"); + Console.WriteLine($"high Score: {highScore}"); } public static void Basics7a() @@ -178,6 +203,14 @@ where city.Population > 10000 select city; var largeCitiesList2 = largeCitiesQuery.ToList(); // + 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() @@ -188,6 +221,10 @@ from city in cities where city.Population > 100000 select city; // + foreach (var city in queryCities) + { + Console.WriteLine(city.Name + ":" + city.Population); + } } public static void Basics9() @@ -195,9 +232,13 @@ public static void Basics9() // IEnumerable countryAreaQuery = from country in countries - where country.Area > 500000 //sq km + where country.Area > 20 //sq km select country; // + foreach (var country in countryAreaQuery) + { + Console.WriteLine(country.Name + ":" + country.Area); + } } public static void Basics10() @@ -209,6 +250,10 @@ from city in country.Cities where city.Population > 10000 select city; // + foreach (var city in cityQuery) + { + Console.WriteLine(city.Name + ":" + city.Population); + } } public static void Basics11() @@ -218,6 +263,14 @@ public static void Basics11() from country in countries group country by country.Name[0]; // + foreach (var group in queryCountryGroups) + { + Console.WriteLine(group.Key); + foreach (var country in group) + { + Console.WriteLine(country.Name); + } + } } public static void Basics12() @@ -228,6 +281,10 @@ from country in countries orderby country.Area select country; // + foreach (var country in sortedQuery) + { + Console.WriteLine(country.Name + ":" + country.Area); + } } public static void Basics13() @@ -241,6 +298,10 @@ from country in countries Pop = country.Population }; // + foreach (var item in queryNameAndPop) + { + Console.WriteLine(item.Name + ":" + item.Pop); + } } public static void Basics14() @@ -249,7 +310,7 @@ public static void Basics14() // percentileQuery is an IEnumerable> 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 @@ -272,9 +333,13 @@ public static void Basics15() // IEnumerable 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; // + foreach (var city in queryCityPop) + { + Console.WriteLine(city.Name + ":" + city.Population); + } } public static void Basics16() @@ -285,12 +350,25 @@ from country in countries orderby country.Area, country.Population descending select country; // + 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") + ]; // var categoryQuery = @@ -302,6 +380,10 @@ join prod in products on cat equals prod.Category Name = prod.Name }; // + foreach (var item in categoryQuery) + { + Console.WriteLine(item.Category + ":" + item.Name); + } } public static void Basics18() @@ -339,5 +421,9 @@ select student2.ExamScores.Average() ).Max() }; // + foreach (var item in queryGroupMax) + { + Console.WriteLine(item.Level + ":" + item.HighestScore); + } } } diff --git a/docs/csharp/linq/get-started/snippets/SnippetApp/Exceptions.cs b/docs/csharp/linq/get-started/snippets/SnippetApp/Exceptions.cs index b1f842bf60fe2..c4190e027dbda 100644 --- a/docs/csharp/linq/get-started/snippets/SnippetApp/Exceptions.cs +++ b/docs/csharp/linq/get-started/snippets/SnippetApp/Exceptions.cs @@ -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) { @@ -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 { diff --git a/docs/csharp/linq/get-started/snippets/SnippetApp/LinqGetStarted.csproj b/docs/csharp/linq/get-started/snippets/SnippetApp/LinqGetStarted.csproj index 13b6b87ef23cd..8522f376c0a40 100644 --- a/docs/csharp/linq/get-started/snippets/SnippetApp/LinqGetStarted.csproj +++ b/docs/csharp/linq/get-started/snippets/SnippetApp/LinqGetStarted.csproj @@ -2,7 +2,7 @@ Exe - net8.0 + net9.0 Linq.GetStarted enable enable diff --git a/docs/csharp/linq/get-started/snippets/SnippetApp/NullValues.cs b/docs/csharp/linq/get-started/snippets/SnippetApp/NullValues.cs index aaf4fe4733bb6..9bd2cb5c33f29 100644 --- a/docs/csharp/linq/get-started/snippets/SnippetApp/NullValues.cs +++ b/docs/csharp/linq/get-started/snippets/SnippetApp/NullValues.cs @@ -1,25 +1,49 @@ namespace LinqSamples; +// record Product(string Name, int CategoryID); record Category(string Name, int ID); +// + public static class NullValues { - static readonly Category[] categories = []; - static readonly Product[] products = []; + // + static Category?[] categories = + [ + new ("brass", 1), + null, + new ("winds", 2), + default, + new ("percussion", 3) + ]; + + static Product?[] products = + [ + new Product("Trumpet", 1), + new Product("Trombone", 1), + new Product("French Horn", 1), + null, + new Product("Clarinet", 2), + new Product("Flute", 2), + null, + new Product("Cymbal", 3), + new Product("Drum", 3) + ]; + // + public static void NullValues1() { // - var query1 = - from c in categories - where c != null - join p in products on c.ID equals p?.CategoryID - select new - { - Category = c.Name, - Name = p.Name - }; + var query1 = from c in categories + where c != null + join p in products on c.ID equals p?.CategoryID + select new + { + Category = c.Name, + Name = p.Name + }; // } } diff --git a/docs/csharp/linq/get-started/snippets/SnippetApp/Program.cs b/docs/csharp/linq/get-started/snippets/SnippetApp/Program.cs index b38cb84a5047e..37eb3d63eab87 100644 --- a/docs/csharp/linq/get-started/snippets/SnippetApp/Program.cs +++ b/docs/csharp/linq/get-started/snippets/SnippetApp/Program.cs @@ -2,6 +2,7 @@ WriteSeparator(nameof(PartsOfAQuery)); PartsOfAQuery(); +Console.WriteLine(); WriteSeparator(nameof(Linq.GetStarted.Basics.Basics1)); Linq.GetStarted.Basics.Basics1(); @@ -41,6 +42,7 @@ Linq.GetStarted.Basics.Basics17(); WriteSeparator(nameof(Linq.GetStarted.Basics.Basics18)); Linq.GetStarted.Basics.Basics18(); +Console.WriteLine(); WriteSeparator(nameof(Linq.GetStarted.Basics.Basics19)); Linq.GetStarted.Basics.Basics19(); @@ -103,10 +105,9 @@ static void PartsOfAQuery() // 2. Query creation. // numQuery is an IEnumerable - var numQuery = - from num in numbers - where (num % 2) == 0 - select num; + var numQuery = from num in numbers + where (num % 2) == 0 + select num; // 3. Query execution. foreach (int num in numQuery) @@ -123,26 +124,23 @@ from num in numbers // // - var evenNumQuery = - from num in numbers - where (num % 2) == 0 - select num; + var evenNumQuery = from num in numbers + where (num % 2) == 0 + select num; int evenNumCount = evenNumQuery.Count(); // // - List numQuery2 = - (from num in numbers - where (num % 2) == 0 - select num).ToList(); + List numQuery2 = (from num in numbers + where (num % 2) == 0 + select num).ToList(); // or like this: // numQuery3 is still an int[] - var numQuery3 = - (from num in numbers - where (num % 2) == 0 - select num).ToArray(); + var numQuery3 = (from num in numbers + where (num % 2) == 0 + select num).ToArray(); // } diff --git a/docs/csharp/linq/get-started/snippets/SnippetApp/ReturnQueryFromMethod.cs b/docs/csharp/linq/get-started/snippets/SnippetApp/ReturnQueryFromMethod.cs index bf6f06adc56ac..8476640e347f1 100644 --- a/docs/csharp/linq/get-started/snippets/SnippetApp/ReturnQueryFromMethod.cs +++ b/docs/csharp/linq/get-started/snippets/SnippetApp/ReturnQueryFromMethod.cs @@ -11,12 +11,11 @@ where i > 4 select i.ToString(); void QueryMethod2(int[] ints, out IEnumerable returnQ) => - returnQ = - from i in ints - where i < 4 - select i.ToString(); + returnQ = from i in ints + where i < 4 + select i.ToString(); - int[] nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + int[] nums = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]; var myQuery1 = QueryMethod1(nums); // @@ -49,10 +48,9 @@ where i < 4 // // - myQuery1 = - from item in myQuery1 - orderby item descending - select item; + myQuery1 = from item in myQuery1 + orderby item descending + select item; // Execute the modified query. Console.WriteLine("\nResults of executing modified myQuery1:"); diff --git a/docs/csharp/linq/get-started/snippets/SnippetApp/RuntimeFiltering.cs b/docs/csharp/linq/get-started/snippets/SnippetApp/RuntimeFiltering.cs index 87f5549f57338..6b0691023f239 100644 --- a/docs/csharp/linq/get-started/snippets/SnippetApp/RuntimeFiltering.cs +++ b/docs/csharp/linq/get-started/snippets/SnippetApp/RuntimeFiltering.cs @@ -7,16 +7,15 @@ public static class RuntimeFiltering public static void RuntimeFiltering1() { // - int[] ids = [111, 114, 112]; + int[] ids = [ 111, 114, 112 ]; - var queryNames = - from student in students - where ids.Contains(student.ID) - select new - { - student.LastName, - student.ID - }; + var queryNames = from student in students + where ids.Contains(student.ID) + select new + { + student.LastName, + student.ID + }; foreach (var name in queryNames) { @@ -30,7 +29,7 @@ where ids.Contains(student.ID) */ // Change the ids. - ids = [122, 117, 120, 115]; + ids = [ 122, 117, 120, 115 ]; // The query will now return different results foreach (var name in queryNames) diff --git a/docs/csharp/linq/get-started/snippets/SnippetApp/StoreResultsOfQueryInMemory.cs b/docs/csharp/linq/get-started/snippets/SnippetApp/StoreResultsOfQueryInMemory.cs index 42c1f6c78b5bd..cb79aa568a0ed 100644 --- a/docs/csharp/linq/get-started/snippets/SnippetApp/StoreResultsOfQueryInMemory.cs +++ b/docs/csharp/linq/get-started/snippets/SnippetApp/StoreResultsOfQueryInMemory.cs @@ -5,12 +5,11 @@ public static class StoreResultsOfQueryInMemory public static void StoreResultsOfQueryInMemory1() { // - List numbers = [1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]; + List numbers = [ 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 ]; - IEnumerable queryFactorsOfFour = - from num in numbers - where num % 4 == 0 - select num; + IEnumerable queryFactorsOfFour = from num in numbers + where num % 4 == 0 + select num; // Store the results in a new variable // without executing a foreach loop. diff --git a/docs/csharp/linq/get-started/snippets/SnippetApp/TypeRelationships.cs b/docs/csharp/linq/get-started/snippets/SnippetApp/TypeRelationships.cs index d3421ee7b5390..cca43c6e0c51d 100644 --- a/docs/csharp/linq/get-started/snippets/SnippetApp/TypeRelationships.cs +++ b/docs/csharp/linq/get-started/snippets/SnippetApp/TypeRelationships.cs @@ -9,15 +9,14 @@ class Customer public string FirstName { get; set; } public string Phone {get; set;} } - static List customers = new List(); + static List customers = new (); public static void ExplicitType() { // - IEnumerable customerQuery = - from cust in customers - where cust.City == "London" - select cust; + IEnumerable customerQuery = from cust in customers + where cust.City == "London" + select cust; foreach (Customer customer in customerQuery) { @@ -29,10 +28,9 @@ from cust in customers public static void ImplicitType() { // - var customerQuery2 = - from cust in customers - where cust.City == "London" - select cust; + var customerQuery2 = from cust in customers + where cust.City == "London" + select cust; foreach(var customer in customerQuery2) { diff --git a/docs/csharp/linq/get-started/snippets/SnippetApp/WriteLinqQueries.cs b/docs/csharp/linq/get-started/snippets/SnippetApp/WriteLinqQueries.cs index f17b7b02ab110..b21e2aa8d378c 100644 --- a/docs/csharp/linq/get-started/snippets/SnippetApp/WriteLinqQueries.cs +++ b/docs/csharp/linq/get-started/snippets/SnippetApp/WriteLinqQueries.cs @@ -2,9 +2,9 @@ public class WriteLinqQueries { - static readonly List numbers = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0]; - static readonly List numbers1 = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0]; - static readonly List numbers2 = [15, 14, 11, 13, 19, 18, 16, 17, 12, 10]; + static readonly List numbers = [ 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 ]; + static readonly List numbers1 = [ 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 ]; + static readonly List numbers2 = [ 15, 14, 11, 13, 19, 18, 16, 17, 12, 10 ]; public static void MethodSyntax() { @@ -19,7 +19,9 @@ orderby num select num; //Method syntax: - IEnumerable numQuery2 = numbers.Where(num => num % 2 == 0).OrderBy(n => n); + IEnumerable numQuery2 = numbers + .Where(num => num % 2 == 0) + .OrderBy(n => n); foreach (int i in numQuery1) { @@ -36,7 +38,7 @@ orderby num public static void WriteLinqQueries1() { // - List numbers = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0]; + List numbers = [ 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 ]; // The query variables can also be implicitly typed by using var @@ -64,8 +66,8 @@ from item in groupingQuery public static void WriteLinqQueries2() { // - List numbers1 = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0]; - List numbers2 = [15, 14, 11, 13, 19, 18, 16, 17, 12, 10]; + List numbers1 = [ 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 ]; + List numbers2 = [ 15, 14, 11, 13, 19, 18, 16, 17, 12, 10 ]; // Query #4. double average = numbers1.Average(); diff --git a/docs/csharp/linq/get-started/snippets/SnippetApp/how-to-handle-null-values-in-query-expressions_2.cs b/docs/csharp/linq/get-started/snippets/SnippetApp/how-to-handle-null-values-in-query-expressions_2.cs deleted file mode 100644 index 5f282702bb03e..0000000000000 --- a/docs/csharp/linq/get-started/snippets/SnippetApp/how-to-handle-null-values-in-query-expressions_2.cs +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/docs/csharp/linq/get-started/write-linq-queries.md b/docs/csharp/linq/get-started/write-linq-queries.md index 2ad2a5eac7e16..6e64fab2fd58d 100644 --- a/docs/csharp/linq/get-started/write-linq-queries.md +++ b/docs/csharp/linq/get-started/write-linq-queries.md @@ -1,11 +1,11 @@ --- title: Write LINQ queries description: Learn how to write LINQ queries in C#. -ms.date: 04/19/2024 +ms.date: 01/16/2025 --- # Write C# LINQ queries to query data -Most queries in the introductory Language Integrated Query (LINQ) documentation are written by using the LINQ declarative query syntax. However, the query syntax must be translated into method calls for the .NET common language runtime (CLR) when the code is compiled. These method calls invoke the standard query operators, which have names such as `Where`, `Select`, `GroupBy`, `Join`, `Max`, and `Average`. You can call them directly by using method syntax instead of query syntax. +Most queries in the introductory Language Integrated Query (LINQ) documentation are written by using the LINQ declarative query syntax. The C# compiler translates query syntax into method calls. These method calls implement the standard query operators, and have names such as `Where`, `Select`, `GroupBy`, `Join`, `Max`, and `Average`. You can call them directly by using method syntax instead of query syntax. Query syntax and method syntax are semantically identical, but query syntax is often simpler and easier to read. Some queries must be expressed as method calls. For example, you must use a method call to express a query that retrieves the number of elements that match a specified condition. You also must use a method call for a query that retrieves the element that has the maximum value in a source sequence. The reference documentation for the standard query operators in the namespace generally uses method syntax. You should become familiar with how to use method syntax in queries and in query expressions themselves. @@ -15,30 +15,28 @@ The following example shows a simple *query expression* and the semantically equ :::code language="csharp" source="./snippets/SnippetApp/WriteLinqQueries.cs" id="MethodSyntax"::: -The output from the two examples is identical. You can see that the type of the query variable is the same in both forms: . +The output from the two examples is identical. The type of the query variable is the same in both forms: . -To understand the method-based query, let's examine it more closely. On the right side of the expression, notice that the `where` clause is now expressed as an instance method on the `numbers` object, which has a type of `IEnumerable`. If you're familiar with the generic interface, you know that it doesn't have a `Where` method. However, if you invoke the IntelliSense completion list in the Visual Studio IDE, you see not only a `Where` method, but many other methods such as `Select`, `SelectMany`, `Join`, and `Orderby`. These methods implement the standard query operators. +On the right side of the expression, notice that the `where` clause is now expressed as an instance method on the `numbers` object, which has a type of `IEnumerable`. If you're familiar with the generic interface, you know that it doesn't have a `Where` method. However, if you invoke the IntelliSense completion list in the Visual Studio IDE, you see not only a `Where` method, but many other methods such as `Select`, `SelectMany`, `Join`, and `Orderby`. These methods implement the standard query operators. ![Screenshot showing all the standard query operators in Intellisense.](./media/write-linq-queries/standard-query-operators.png) -Although it looks as if includes more methods, it doesn't. The standard query operators are implemented as *extension methods*. Extensions methods "extend" an existing type; they can be called as if they were instance methods on the type. The standard query operators extend and that is why you can write `numbers.Where(...)`. - -To use extension methods, you bring them into scope with `using` directives. From your application's point of view, an extension method and a regular instance method are the same. +Although it looks as if includes more methods, it doesn't. The standard query operators are implemented as *extension methods*. Extensions methods "extend" an existing type; they can be called as if they were instance methods on the type. The standard query operators extend and that is why you can write `numbers.Where(...)`. You bring extensions into scope with `using` directives before calling them. For more information about extension methods, see [Extension Methods](../../programming-guide/classes-and-structs/extension-methods.md). For more information about standard query operators, see [Standard Query Operators Overview (C#)](../standard-query-operators/index.md). Some LINQ providers, such as [Entity Framework](/ef/core/) and LINQ to XML, implement their own standard query operators and extension methods for other types besides . ## Lambda expressions -In the previous example, notice that the conditional expression (`num % 2 == 0`) is passed as an in-line argument to the method: `Where(num => num % 2 == 0).` This inline expression is a [lambda expression](../../language-reference/operators/lambda-expressions.md). It's a convenient way to write code that would otherwise have to be written in more cumbersome form. The `num` on the left of the operator is the input variable, which corresponds to `num` in the query expression. The compiler can infer the type of `num` because it knows that `numbers` is a generic type. The body of the lambda is just the same as the expression in query syntax or in any other C# expression or statement. It can include method calls and other complex logic. The return value is just the expression result. Certain queries can only be expressed in method syntax and some of those require lambda expressions. Lambda expressions are a powerful and flexible tool in your LINQ toolbox. +In the preceding example, the conditional expression (`num % 2 == 0`) is passed as an in-line argument to the method: `Where(num => num % 2 == 0).` This inline expression is a [lambda expression](../../language-reference/operators/lambda-expressions.md). It's a convenient way to write code that would otherwise have to be written in more cumbersome form. The `num` on the left of the operator is the input variable, which corresponds to `num` in the query expression. The compiler can infer the type of `num` because it knows that `numbers` is a generic type. The body of the lambda is the same as the expression in query syntax or in any other C# expression or statement. It can include method calls and other complex logic. The return value is the expression result. Certain queries can only be expressed in method syntax and some of those queries require lambda expressions. Lambda expressions are a powerful and flexible tool in your LINQ toolbox. ## Composability of queries -In the previous code example, the method is invoked by using the dot operator on the call to `Where`. `Where` produces a filtered sequence, and then `Orderby` sorts the sequence produced by `Where`. Because queries return an `IEnumerable`, you compose them in method syntax by chaining the method calls together. The compiler does this composition when you write queries using query syntax. Because a query variable doesn't store the results of the query, you can modify it or use it as the basis for a new query at any time, even after you execute it. +In the preceding code example, the method is invoked by using the dot operator on the call to `Where`. `Where` produces a filtered sequence, and then `Orderby` sorts the sequence produced by `Where`. Because queries return an `IEnumerable`, you compose them in method syntax by chaining the method calls together. The compiler does this composition when you write queries using query syntax. Because a query variable doesn't store the results of the query, you can modify it or use it as the basis for a new query at any time, even after you execute it. -The following examples demonstrate some simple LINQ queries by using each approach listed previously. +The following examples demonstrate some basic LINQ queries by using each approach listed previously. > [!NOTE] -> These queries operate on simple in-memory collections; however, the basic syntax is identical to that used in LINQ to Entities and LINQ to XML. +> These queries operate on in-memory collections; however, the syntax is identical to that used in LINQ to Entities and LINQ to XML. ## Example - Query syntax @@ -54,7 +52,7 @@ In each previous example, the queries don't actually execute until you iterate o ## Example - Method syntax -Some query operations must be expressed as a method call. The most common such methods are those methods that return singleton numeric values, such as , , , , and so on. These methods must always be called last in any query because they return a single value and can't serve as the source for an extra query operation. The following example shows a method call in a query expression: +Some query operations must be expressed as a method call. The most common such methods are those methods that return singleton numeric values, such as , , , , and so on. These methods must always be called last in any query because they return a single value and can't serve as the source for an additional query operation. The following example shows a method call in a query expression: :::code language="csharp" source="./snippets/SnippetApp/WriteLinqQueries.cs" id="write_linq_queries_2"::: @@ -70,7 +68,7 @@ Each of the previous queries can be written by using implicit typing with [`var` ## Example - Mixed query and method syntax -This example shows how to use method syntax on the results of a query clause. Just enclose the query expression in parentheses, and then apply the dot operator and call the method. In the following example, query #7 returns a count of the numbers whose value is between 3 and 7. In general, however, it's better to use a second variable to store the result of the method call. In this manner, the query is less likely to be confused with the results of the query. +This example shows how to use method syntax on the results of a query clause. Just enclose the query expression in parentheses, and then apply the dot operator and call the method. In the following example, query #7 returns a count of the numbers whose value is between 3 and 7. :::code language="csharp" source="./snippets/SnippetApp/WriteLinqQueries.cs" id="write_linq_queries_5"::: @@ -96,6 +94,13 @@ In some cases, you don't know until run time how many predicates you have to app :::code language="csharp" source="./snippets/SnippetApp/RuntimeFiltering.cs" id="runtime_filtering_1"::: +> [!NOTE] +> This example uses the following data source and data: + +:::code language="csharp" source="./snippets/SnippetApp/DataSources.cs" id="basics_datasource"::: + +:::code language="csharp" source="./snippets/SnippetApp/Basics.cs" id="SourceData"::: + You can use control flow statements, such as `if... else` or `switch`, to select among predetermined alternative queries. In the following example, `studentQuery` uses a different `where` clause if the run-time value of `oddYear` is `true` or `false`. :::code language="csharp" source="./snippets/SnippetApp/RuntimeFiltering.cs" id="runtime_filtering_2"::: @@ -104,6 +109,12 @@ You can use control flow statements, such as `if... else` or `switch`, to select This example shows how to handle possible null values in source collections. An object collection such as an can contain elements whose value is [null](../../language-reference/keywords/null.md). If a source collection is `null` or contains an element whose value is `null`, and your query doesn't handle `null` values, a is thrown when you execute the query. +The following example uses these types and static data arrays: + +:::code language="csharp" source="./snippets/SnippetApp/NullValues.cs" id="dataSourceTypes"::: + +:::code language="csharp" source="./snippets/SnippetApp/NullValues.cs" id="source"::: + You can code defensively to avoid a null reference exception as shown in the following example: :::code language="csharp" source="./snippets/SnippetApp/NullValues.cs" id="null_values_1"::: @@ -124,7 +135,7 @@ In each of the examples, the `equals` query keyword is used. You can also use [p ## Handle exceptions in query expressions -It's possible to call any method in the context of a query expression. Don't call any method in a query expression that can create a side effect such as modifying the contents of the data source or throwing an exception. This example shows how to avoid raising exceptions when you call methods in a query expression without violating the general .NET guidelines on exception handling. Those guidelines state that it's acceptable to catch a specific exception when you understand why it's thrown in a given context. For more information, see [Best Practices for Exceptions](../../../standard/exceptions/best-practices-for-exceptions.md). +It's possible to call any method in the context of a query expression. Don't call any method in a query expression that can create a side effect such as modifying the contents of the data source or throwing an exception. This example shows how to avoid raising exceptions when you call methods in a query expression without violating the general .NET guidelines on exception handling. Those guidelines state that it's acceptable to catch a specific exception when you understand why it was thrown in a given context. For more information, see [Best Practices for Exceptions](../../../standard/exceptions/best-practices-for-exceptions.md). The final example shows how to handle those cases when you must throw an exception during execution of a query. @@ -144,7 +155,6 @@ Remember to catch whatever exception you expect to raise and/or do any necessary ## See also -- [Walkthrough: Writing Queries in C#](walkthrough-writing-queries-linq.md) - [where clause](../../language-reference/keywords/where-clause.md) - [Querying based on runtime state](../../advanced-topics/expression-trees/debugview-syntax.md) -