A query expression (Language-Integrated Query, version 9) asks questions of a sequence in a form that reads like SQL: it starts with From or Aggregate, goes through any number of clauses and, unless it is an aggregate, ends with Select or Group By (a query may also end after other clauses, in which case it selects the range variables). The keywords are contextual: they are keywords only inside a query, so Take, Skip, Where and Group are ordinary names elsewhere.
The clauses
Dim q1 = From c In customers
Where c.City = "London"
Order By c.Name Descending, c.Id
Select c.Name, Upper = c.Name.ToUpper()
Dim q2 = From c In customers
Join o In orders On c.Id Equals o.CustomerId
Select c.Name, o.Total
Dim q3 = From c In customers
Group Join o In orders On c.Id Equals o.CustomerId Into Items = Group, Sum(o.Total), Count()
Select c.Name, Items, Sum, Count
Dim q4 = From o In orders
Group By o.CustomerId Into Total = Sum(o.Total), Count()
Dim q5 = From o In orders
Let doubled = o.Total * 2
Where doubled > 10
Select doubled
Distinct
Dim q7 = From c In customers Skip 1 Take 1 Select c
Dim q8 = From o In orders Take While o.Total > 5 Select o.Total
Dim all = Aggregate o In orders Into Sum(o.Total), Average(o.Total), Max(o.Total), Any(o.Total > 10)
Dim n = Aggregate c In customers Where c.City = "Paris" Into Count()
| Clause | Meaning |
|---|---|
From x [As T] In source | introduces a range variable that ranges over the elements of a sequence; more From clauses nest (a cross join). Without As, the element type is inferred |
Where condition | keeps the elements for which the condition is true |
Select list | projects each element: a single expression, or a list that makes an anonymous type, optionally with names (Upper = …); the names of members are taken from the expressions otherwise |
Order By e [Ascending|Descending], … | sorts by one or more keys (ascending is the default) |
Group [items] By key Into … | groups elements by a key; Into names the aggregates (or Group, the elements of each group). The range variables are replaced by the key and the Into names |
Join y In other On a Equals b | an inner join on equal keys (several conditions with And); Group Join … Into gives each outer element its matching group |
Let name = expression | introduces another range variable computed from the existing ones |
Distinct | removes duplicates from the result |
Skip n, Take n, Skip While c, Take While c | partitions: leave out or keep the first n elements, or the elements while the condition holds |
Aggregate x In source … Into f(…), … | reduces the sequence to one value (or a set of named values) with Sum, Count, LongCount, Average, Min, Max, Any, All, Group or any extension method |
A line may be broken before or after a query keyword with no underscore: the break is an implicit continuation (see lexical structure). A query is deferred: it builds a sequence that is evaluated when it is enumerated (For Each, ToList(), Count()). A range variable may not have the same name as another variable in scope (names are case-insensitive: a range variable Orders clashes with a local orders).
Under the query
The compiler turns each clause into a call of a method of the same name on the source (Where, Select, OrderBy, Join, GroupBy, Skip, Take, Distinct…), with lambdas as arguments. Any type with the methods works; the library provides them for IEnumerable(Of T) (System.Linq.Enumerable, LINQ to Objects) and IQueryable(Of T) (expression trees for database providers). The same query can be written with the methods:
Dim m = customers.Where(Function(c) c.Id > 1).Select(Function(c) c.Name).ToList()
Not described
Known, not yet covered
- Reference: the translation rules of every clause into method calls, the exact shape of the range variables after
Group ByandGroup Join, the queryable-type pattern (which methods a type must have), default query indexers and the rules for the aggregate functions are in the query chapter of the specification and are only summarised. - Viewer: the clause keywords are coloured as keywords from the text near them:
FromandAggregateonly when a range variable andInorAsfollow on the same line, and the other words only until the end of the statement (a following line that starts with a clause keyword continues it).
See also
- Generics, lambdas and delegates – the lambdas a query is made of
- XML literals – queries over XML