Language Integrated Query (LINQ)
Language Integrated Query (LINQ)
(LINQ)
First Program
• The LINQ query statement in this program uses the LINQ declarative
query syntax:
var queryResults =
from n in names
where n.StartsWith("S")
select n;
• The statement has four parts: the result variable declaration
beginning with var, which is assigned using a query expression
consisting of the from clause; the where clause; and the select clause
Declaring a Variable for Results Using the var
Keyword
• The LINQ query starts by declaring a variable to hold the results of the
query, which is usually done by declaring a variable with the var keyword:
• var queryResult =
• Var is a keyword in C# created to declare a general variable type that is
ideal for holding the results of LINQ queries.
• The var keyword tells the C# compiler to infer the type of the result based
on the query.
• If the query can return multiple items, then it acts like a collection of the
objects in the query data source (technically, it is not a collection; it just
looks that way).
Specifying the Data Source: from Clause
• The next part of the LINQ query is the from clause, which specifies
the data you are querying:
from n in names
• Your data source in this case is names, the array of strings declared
earlier. The variable n is just a stand-in for an individual element in
the data source, similar to the variable name following a foreach
statement.
• By specifying from, you are indicating that you are going to query a
subset of the collection, rather than iterate through all the elements.
Specify Condition: where Clause
• In the next part of the LINQ query, you specify the condition for your
query using the where clause, which looks like this:
where n.StartsWith("S")
• Any Boolean (true or false) expression that can be applied to the
items in the data source can be specified in the where clause.
• Actually, the where clause is optional and can even be omitted.
• The where clause is called a restriction operator in LINQ
• for example, a length greater than 10 (where n.Length > 10) or
containing a Q (where n.Contains("Q"))
Selecting Items: select Clause
• Finally, the select clause specifies which items appear in the result
set. The select clause looks like this:
select n
• The select clause is required because you must specify which items
from your query appear in the result set.
ORDERING QUERY RESULTS
var queryResults =
from n in names
where n.StartsWith("S")
orderby n
select n;
• By default, orderby orders in ascending order (A to Z), but you can
specify descending order (from Z to A) simply by adding the
descending keyword:
orderby n descending
ORDERING QUERY RESULTS
• to order by the last letter in the name instead of normal alphabetical
order, you just change the orderby clause to the following:
orderby n.Substring(n.Length - 1)
QUERYING A LARGE DATA SET
QUERYING A LARGE DATA SET
static void Main(string[] args) private static int[] GenerateLotsOfNumbers(int count)
{ {
int[] numbers = GenerateLotsOfNumbers(12345678); Random generator = new Random(0);
var queryResults = int[] result = new int[count];
from n in numbers for (int i = 0; i < count; i++)
where n < 1000 {
select n; result[i] = generator.Next();
Console.WriteLine("Numbers less than 1000:"); }
foreach (var item in queryResults) return result;
{Console.WriteLine(item);} }
Console.Write("Program finished, press Enter/Return
to continue:");
Console.ReadLine();
}
USING AGGREGATE OPERATORS
• if you were to change the condition to list the numbers greater than
1,000, rather than the numbers less than 1,000, there would be so
many query results that the numbers would not stop printing!
• LINQ provides a set of aggregate operators that enable you to analyze
the results of a query without having to loop through them all.
TABLE 23-1: Aggregate Operators for Numeric Results
OPERATOR DESCRIPTION
Count() Count of results
Min() Minimum value in results
Max() Maximum value in results
Average() Average value of numeric results
Sum() Total of all of numeric results
var queryResults =
from n in numbers
where n > 1000
select n
;
Console.WriteLine("Count of Numbers > 1000");
Console.WriteLine(queryResults.Count());
There are so many large numbers in the data set that the sum of all of them
would be too large to fit into a standard 32-bit int, which is what the no-
parameter version of Sum() returns. The lambda expression enables you to
convert the result of Sum() to a long 64-bit integer, which is what you need to
hold the total of over 13 quadrillion without overflow —13,254,853,218,619,179
lambda expressions enable you to perform this kind of fix-up easily.
QUERYING COMPLEX OBJECTS
QUERYING COMPLEX OBJECTS
var queryResults =
from c in customers
where c.Region == "Asia"
select c
;
Console.WriteLine("Customers in Asia:");
foreach (Customer c in queryResults)
{
Console.WriteLine(c);
}
PROJECTION: CREATING NEW OBJECTS IN
QUERIES
• Projection is the technical term for creating a new data type from
other data types in a LINQ query.
• The select keyword is the projection operator.
• Selecting a specific field from a data object, as opposed to selecting
the entire object itself.
PROJECTION: CREATING NEW OBJECTS IN
QUERIES
• For example, to select only the City field from the Customer list in the
previous example, simply change the select clause in the query
statement to reference only the City property:
var queryResults =
from c in customers
where c.Region == "Asia"
select c.City
;
PROJECTION: CREATING NEW OBJECTS IN
QUERIES
• LINQ does not allow multiple fields in a select clause. That means the
line
select c.City, c.Country, c.Sales
• produces a compile error because the select clause takes only one
item in its parameter list.
• What you do in LINQ instead is to create a new object on-the-fly in
the select clause to hold the results you want for your query
C# anonymous-type creation
• C# anonymous-type creation syntax is used in the select clause to
create a new unnamed object type having the City, Country, and Sales
properties.
• The select clause creates the new object.
• This way, only these three properties are duplicated and carried
through the different stages of processing the query.
C# anonymous-type creation
var queryResults =
from c in customers
where c.Region == "North America"
select new { c.City, c.Country, c.Sales }
;
foreach (var item in queryResults)
{
Console.WriteLine(item);
}
USING THE SELECT DISTINCT QUERY
• Another type of query is the SELECT DISTINCT query, in which you
search for the unique values in your data — that is, values that are
not repeated. This is a fairly common need when working with
queries.
var queryResults =
customers.Select(c => c.Region).Distinct();
USING THE SELECT DISTINCT QUERY
• Because Distinct()is available only in method syntax, you make the call
to Select()using method syntax.
• However, you can call Distinct()to modify a query made in the query
syntax as well:
var queryResults =
(from c in customers select c.Region). Distinct();
USING THE ANY AND ALL METHODS
• Another type of query that you often need is for determining whether
any of your data satisfies a certain condition, or ensuring that all data
satisfies a condition.
• For example, you may need to know whether a product is out of stock
(quantity is zero), or whether a transaction has occurred.
• LINQ provides two Boolean methods — Any()and All()— that can
quickly tell you whether a condition is true or false for your data.
bool anyUSA = customers.Any(c => c.Country == "USA");
if (anyUSA)
{ Console.WriteLine("Some customers are in the USA"); }
else
{ Console.WriteLine("No customers are in the USA"); }
bool allAsia = customers.All(c => c.Region == "Asia");
if (allAsia)
{ Console.WriteLine("All customers are in Asia"); }
else
{ Console.WriteLine("Not all customers are in Asia"); }
ORDERING BY MULTIPLE LEVELS
• Now that you are dealing with objects with multiple properties, you
might be able to envision a situation where ordering the query results
by a single field is not enough.
• What if you wanted to query your customers and order the results
alphabetically by region, but then order alphabetically by country or
city name within a region.
ORDERING BY MULTIPLE LEVELS
var queryResults =
from c in customers
orderby c.Region, c.Country, c.City
select new { c.ID, c.Region, c.Country, c.City }
;
class Order
{
public string ID { get; set; }
public decimal Amount { get; set; }
}
USING THE LINQ SET OPERATORS
List<Order> orders = new List<Order> { var customerIDs =
new Order { ID="P", Amount=100 }, from c in customers
new Order { ID="Q", Amount=200 }, select c.ID
new Order { ID="R", Amount=300 }, ;
new Order { ID="S", Amount=400 },
new Order { ID="T", Amount=500 }, var orderIDs =
new Order { ID="U", Amount=600 }, from o in orders
new Order { ID="V", Amount=700 }, select o.ID
new Order { ID="W", Amount=800 }, ;
new Order { ID="X", Amount=900 },
new Order { ID="Y", Amount=1000 },
new Order { ID="Z", Amount=1100 }
};
Intersect
var customersWithOrders =
customerIDs.Intersect(orderIDs);
Console.WriteLine("Customer IDs with Orders:");
foreach (var item in customersWithOrders)
{
Console.Write("{0} ", item);
} The Intersect() set operator finds only the
Console.WriteLine(); customer IDs that also have orders in the
orderIDsresult.
Only the IDs that appear in both result sets are
included in the intersect set.
Except
Console.WriteLine("Order IDs with no
customers:");
var ordersNoCustomers =
orderIDs.Except(customerIDs);
foreach (var item in ordersNoCustomers)
{
Console.Write("{0} ", item);
}
Console.WriteLine();
The Except() operator finds the order IDs that
have no matching customer.
Union
Console.WriteLine("All Customer and Order
IDs:");
var allCustomerOrderIDs =
orderIDs.Union(customerIDs);
foreach (var item in allCustomerOrderIDs)
{
Console.Write("{0} ", item);
}
Console.WriteLine();
The Union() operator finds the union of all the
customer ID and order ID fields.
USING JOINS
• A data set such as the customers and orders list just created, with a
shared key field (ID), enables a join query, whereby you can query
related data in both lists with a single query, joining the results
together with the key field.
USING JOINS
foreach (var item in
var queryResults = queryResults)
from c in customers {
join o in orders on c.ID equals o.ID Console.WriteLine(item);
select new }
{
c.ID,
c.City,
SalesBefore = c.Sales,
NewOrder = o.Amount,
SalesAfter = c.Sales + o.Amount
};
Reference Reading
• Chapter 23, Introduction to LINQ,
Beginning Visual C# 2012 Programming by Karli Watson, Jacob Vibe
Hammer, Jon Reid and Morgan Skinner, Wrox, 2012.