This appendix provides syntax summaries for the most useful LINQ methods. For more detailed information, see Chapter 8, “LINQ.”
The following text shows the typical syntax for a LINQ query.
from ... where ... orderby ... select ...
The following sections describe these four standard clauses. The sections after those describe some of the other most useful LINQ clauses.
The from
clause tells where the data comes from and defines the name by which it is known within the LINQ query.
from queryVariable in dataSource
Examples:
var customerQuery =
from person in customers
select person;
var scoresQuery =
from student in students
from score in testScores
where student.StudentId == score.StudentId
select new {student, score};
Usually, if you select data from multiple sources, you will want to use a where
clause to join the results from the sources.
The where
clause applies filters to the records selected by the from
clause. The syntax follows.
where conditions
Use comparison operators (>
, <
, and ==
), logical operators (!
, |
, and &&
), object methods (ToString
and Length
), and functions to build complex conditions.
For example, the following query selects student and test score data, matching students to their test scores.
var scoresQuery =
from student in students
from score in testScores
where student.StudentId == score.StudentId
select new {student, score};
The following example selects students with last names starting with S.
var scoresQuery =
from student in students
from score in testScores
where student.StudentId == score.StudentId
&& student.LastName.StartsWith("S")
select new {student, score};
The orderby
clause makes a query sort the selected objects. For example, the following query selects students and their scores ordered by student last name followed by first name.
var scoresQuery =
from student in students
from score in testScores
where student.StudentId == score.StudentId
orderby student.LastName, student.FirstName
select new {student, score};
Add the descending
keyword to sort a field in descending order.
The select
clause lists the fields that the query should select into its result. Optionally, you can add an alias to the result.
The following query selects the customers’ FirstName
and LastName
values concatenated and gives the result the alias Name
.
var customerQuery = from person in customers
select Name = person.FirstName + " " + person.LastName;
You can pass values from the data sources into functions or constructors. For example, suppose the Person
class has a constructor that takes first and last names as parameters. Then the following query returns a group of Person
objects created from the selected customer data.
var customerQuery = from person in customers
select new Person(person.FirstName, person.LastName);
The join
keyword selects data from multiple data sources matching up corresponding fields. The following pseudo-code shows the join
command’s syntax.
from variable1 in dataSource1
join variable2 in dataSource2
on variable1.field1 equals variable2.field2
For example, the following query selects Customer
objects from the customers
array. For each Customer
object, it selects Order
objects from the orders
array where the two records have the same CustomerId
value.
var query =
from customer in customers
join order in orders
on customer.CustomerId equals order.CustomerId
select new { customer, order };
Note that you can get a similar result by using a where
clause. The following query selects a similar set of objects without using the join
keyword.
var query =
from customer in customers
from order in orders
where customer.CustomerId == order.CustomerId
select new { customer, order };
The group by
clause lets a program select data from a flat, relational style format and build a hierarchical arrangement of objects. The following code shows a simple example.
var query =
from order in orders
group order by order.CustomerId;
This query selects Order
objects from the orders
array and groups the selected objects by their CustomerId
values.
The result is a list of objects representing the groups. Each of those objects has a Key
property that gives the value that was used to build that group. In this example, the Key
is the value of the objects’ CustomerId
values.
Each of the group objects is also enumerable, so the program can loop through the objects that are in its group.
When you group data, you can use aggregate methods to select combined values from the groups.
The following query selects orders grouped by CustomerId
. The Sum
aggregate method highlighted in bold selects the sum of the prices of the orders in each group.
var query =
from order in orders
group order by order.CustomerId into Orders
select new
{
ID = Orders.Key,
Orders,
TotalPrice = Orders.Sum(order => order.Price)
};
The following list summarizes methods that LINQ provides for limiting the results returned by a query.
First
—Returns the first result and discards the rest. If the result includes no values, this throws an exception.FirstOrDefault
—Returns the first result and discards the rest. If the query contains no results, it returns a default value.Last
—Returns the last result and discards the rest. If the result includes no values, this throws an exception.LastOrDefault
—Returns the last result and discards the rest. If the query contains no results, it returns a default value.Single
—Returns the single item selected by the query. If the query does not contain exactly one result, this throws an exception.SingleOrDefault
—Returns the single item selected by the query. If the query contains no results, this returns a default value. If the query contains more than one item, this throws an exception.Skip
—Discards a specified number of results and keeps the rest.SkipWhile
—Discards results as long as some condition is true
and then keeps the rest. (The condition is given by a method, often a lambda expression.)Take
—Keeps a specified number of results and discards the rest.TakeWhile
—Keeps results as long as some condition is true
and then discards the rest.The following table summarizes LINQ useful extension methods that are not available from C# LINQ query syntax.
Function | Purpose |
Aggregate | Uses a function specified by the code to calculate a custom aggregate |
Concat | Concatenates two sequences into a new sequence |
Contains | Returns true if the result contains a specific value |
DefaultIfEmpty | Returns the query’s result or a default value if the query returns an empty result |
ElementAt | Returns an element at a specific position in the query’s result |
ElementAtOrDefault | Returns an element at a specific position in the query’s result or a default value if there is no such position |
Empty | Creates an empty IEnumerable |
Except | Returns the items in one IEnumerable that are not in a second IEnumerable |
Intersection | Returns the intersection of two IEnumerable objects |
Range | Creates an IEnumerable containing a range of integer values |
Repeat | Creates an IEnumerable containing a value repeated a specific number of times |
SequenceEqual | Returns true if two sequences are identical |
Union | Returns the union of two IEnumerable objects |
The following table summarizes LINQ data type conversion methods.
Function | Purpose |
AsEnumerable | Converts the result to IEnumerable<T> |
AsQueryable | Converts an IEnumerable to IQueryable |
OfType | Removes items that cannot be cast into a specific type |
ToArray | Places the results in an array |
ToDictionary | Places the results in a Dictionary |
ToList | Converts the result to List<T> |
ToLookup | Places the results in a Lookup (one-to-many dictionary) |
The following sections describe LINQ methods to move data in and out of XML.
C# does not support XML literals, but you can pass a string containing XML data into an XML object’s Parse
method, as shown in the following code.
XElement xelement = XElement.Parse(
@"<Employees>
<Employee FirstName=""Ann"" LastName=""Archer""/>
<Employee FirstName='Ben' LastName='Baker'/>
<Employee>
<FirstName>Cindy</FirstName>
<LastName>Cant</LastName>
</Employee>
</Employees>
");
LINQ’s XML classes provide constructors that enable you to build XML documents relatively easily. Each constructor’s parameter list ends with a parameter array, so you can pass any number of items into it. The following code uses functional construction to build an XML structure.
XElement employees = new XElement("Employees",
new XElement("Employee",
new XAttribute("FirstName", "Ann"),
new XAttribute("LastName", "Archer")
),
new XElement("Employee",
new XAttribute("FirstName", "Ben"),
new XAttribute("LastName", "Baker")
),
new XElement("Employee",
new XElement("FirstName", "Cindy"),
new XElement("LastName", "Cant")
)
);
XML classes such as XElement
provide LINQ functions that enable you to use LINQ queries on them just as you can select data from IEnumerable
objects.
The following code searches the XElement
named document
for descendants named "Employee"
and selects their FirstName
and LastName
attributes.
var selectEmployee =
from employee in document.Descendants("Employee")
select new
{
FirstName = employee.Attribute("FirstName").Value,
LastName = employee.Attribute("LastName").Value
};
The following table describes other methods supported by XElement
that a program can use to navigate through an XML hierarchy. Most of the functions return IEnumerable
objects that you can use in LINQ queries.
Function | Returns |
Ancestors | IEnumerable containing all ancestors of the element. |
AncestorsAndSelf | IEnumerable containing this element followed by all its ancestors. |
Attribute | The element’s attribute with a specific name. |
Attributes | IEnumerable containing the element’s attributes. |
Descendants | IEnumerable containing all descendants of the element. |
DescendantsAndSelf | IEnumerable containing this element followed by all its descendants. |
DescendantNodes | IEnumerable containing all descendant nodes of the element. These include all nodes such as XElement and XText . |
DescendantNodesAndSelf | IEnumerable containing this element followed by all its descendant nodes. |
Element | The first child element with a specific name. |
Elements | IEnumerable containing the immediate children of the element. |
ElementsAfterSelf | IEnumerable containing the siblings of the element that come after this element. |
ElementsBeforeSelf | IEnumerable containing the siblings of the element that come before this element. |
Nodes | IEnumerable containing the nodes that are immediate children of the element. These include all nodes such as XElement and XText . |
NodesAfterSelf | IEnumerable containing the sibling nodes of the element that come after this element. |
NodesBeforeSelf | IEnumerable containing the sibling nodes of the element that come before this element. |
LINQ to ADO.NET provides tools that enable you to apply LINQ-style queries to objects used by ADO.NET to store and interact with relational data. LINQ to ADO.NET includes three components: LINQ to SQL, LINQ to Entities, and LINQ to DataSet.
Building and managing SQL Server databases and the Entity Framework are topics too large to cover in this book, so LINQ to SQL and LINQ to Entities are not described in more detail here. For more information, consult the online help or Microsoft’s website.
LINQ to DataSet lets a program use LINQ-style queries to select data from DataSet
objects.
For example, suppose the testScoresDataSet
object contains tables named Students
and TestScores
. Then the following code gets references to the DataTable
objects that represent the tables.
DataTable studentsTable = testScoresDataSet.Tables["Students"];
DataTable scoresTable = testScoresDataSet.Tables["TestScores"];
You can then use LINQ to query the DataTable
table objects. For example, the following code selects the names of students with LastName
before "F"
alphabetically.
var namesBeforeFQuery =
from student in studentsTable.AsEnumerable()
where (student.Field<string>("LastName").CompareTo("F") < 0)
orderby student.Field<string>("LastName")
select new
{
FirstName = student.Field<string>("FirstName"),
LastName = student.Field<string>("LastName")
};
namesBeforeDDataGrid.DataSource = namesBeforeFQuery.ToList();
The following list summarizes the key differences between a LINQ to DataSet query and a normal LINQ to Objects query.
DataTable
object’s AsEnumerable
method to make the object queryable.DataRow
, as in student.Field<string>("LastName")
.DataGrid
or ListBox
, use the query’s ToList
method.Adding parallelism to LINQ is remarkably simple. First, add a reference to the System.Threading library to your program. Then add a call to AsParallel
to the enumerable object that you’re searching. For example, the following code uses AsParallel
to select the even numbers from the array numbers
.
var evens =
from int number in numbers.AsParallel()
where number % 2 == 0
select number;
join order in orders on customer.CustomerId equals order.CustomerId