EditExtension Method Query Syntax
The extension method format is simply where you cascade multiple extension methods together, each returning an IEnumerable
result to allow the next extension method to flow on. A new language feature called Lambda Expressions allows us to pass delegates for evaluation and for projection (shaping the return result collection) depending on what that operator needs.
Microsoft have defined a whole set of extension methods (called the Standard Query Operators), and these extensions introduce methods to any type that implements IEnumerable to allow us to write query operations. Every collection type built into the .NET framework implements IEnumerable, so we can access these extension methods when querying any built-in collection type. There is nothing specifically startling about these extension methods, in fact you might be shocked at their individual simplicity, but when combined and cascaded together the full power of LINQ to Objects becomes most apparent.
The basic extension method syntax takes the following general form -
[source collection].[extension method].[extension method]…;
For example -
[source].Where([Lambda Expression]).Select([Lambda Expression]);
The following standard query operators return an IEnumerable interface and can be cascaded into another Standard Query Operator extension method -
Cast, Concat, DefaultIfEmpty, Distinct, Empty, Except, GroupBy, GroupJoin, Intersect, Join, OfType, OrderBy, OrderByDescending, Range, Repeat, Reverse, Select, SelectMany, Skip, SkipWhile, Take, TakeWhile, ThenBy, ThenByDescending, ToArray, ToDictionary, ToList, ToLookup, ToSequence, Union, Where
Let us now look at some examples to see how we can cascade extension method operators to form queries. After seeing this syntax, especially the Join syntax, you will appreciate the Query Expression syntax even more.
List<Contacts> contacts = Contacts.SampleData();
var q = contacts.Where(c => c.State == "WA")
.OrderBy(c => c.LastName)
.ThenBy(c => c.FirstName);
foreach(Contacts c in q)
Console.WriteLine("{0} {1}", c.FirstName, c.LastName);
Stewart Kagel
Chance Lard
Armando Valdes
Figure: Query gets all contacts in the state of "WA" ordered by last name, then first name
List<Contacts> contacts = Contacts.SampleData();
List<CallLog> callLog = CallLog.SampleData();
var q = callLog.Join( contacts,
call => call.Number,
contact => contact.Phone,
(call, contact) => new { contact.FirstName,
contact.LastName,
call.When,
call.Duration })
.Take(5)
.OrderByDescending(call => call.When);
foreach(var call in q)
Console.WriteLine("{0} - {1} {2} ({3}min)",
call.When.ToString("ddMMM HH:m"),
call.FirstName, call.LastName, call.Duration);
07Aug 11:15 - Stewart Kagel (4min)
07Aug 10:35 - Collin Zeeman (2min)
07Aug 10:5 - Mack Kamph (1min)
07Aug 09:23 - Ariel Hazelgrove (15min)
07Aug 08:12 - Barney Gottshall (2min)
Figure - Joins are particularly nasty in Extension Method syntax. Query returns the first 5 call details order by most-recent.
There are many other standard query operators that return a fixed result type, and cannot be cascaded together. These operators can be used in Query Expressions just like any of the others, it is just they end the IEnumerable chain and return a fixed result type.
Aggregate, All, Any, Average, Contains, Count, ElementAt, ElementAtOrDefault, EqualAll, First, FirstOrDefault, Fold, Last, LastOrDefault, LongCount, Max, Min, Single, SingleOrDefault, Sum
These operators can also be used in the select expression too; this mainly makes use of the aggregation operators like Count, Average, Min or Max.
Developer Tips –
• Express the most limiting query method first; This reduces the workload of the successive operators;
• Split each operator onto a different line (including the period joiner). This allows you to comment out individual operators when debugging;
• Be consistent, within an application use the same style throughout;
• To make it easier to read queries, don’t be afraid to split up the query into multiple parts and indent to show hierarchy.