416
LESSON 37 LINQ to objects
ORDER BY CLAUSES
Often the result of a query is easier to read if you sort the selected values. You can do this by inserting
an order by clause between the where clause and the select clause.
The order by clause begins with the keyword
orderby followed by one or more values separated by
commas that determine how the results are ordered.
Optionally you can follow a value by the keyword
ascending (the default) or descending to determine
whether the results are ordered in ascending (1-2-3 or A-B-C) or descending (3-2-1 or C-B-A) order.
For example, the following query selects
Customers with negative balances and orders them so
those with the smallest (most negative) values come first:
var negativeQuery =
from Customer cust in customers
where cust.Balance < 0
orderby cust.Balance ascending
select cust;
The following version orders the results first by balance and then, if two customers have the same
balance, by last name:
var negativeQuery =
from Customer cust in customers
where cust.Balance < 0
orderby cust.Balance, cust.LastName
select cust;
SELECT CLAUSES
The select clause determines what data is pulled from the data source and stored in the result. All of
the previous examples select the data over which they are ranging. For example, the FindCustomers
example program ranges over an array of
Customer objects and selects certain Customer objects.
Instead of selecting the objects in the query’s range, a program can select only some properties of
those objects, a result calculated from those properties, or even completely new objects. Selecting a
new kind of data from the existing data is called projecting or transforming the data.
The FindStudents example program shown in Figure 37-2 (and available in this chapter’s code
download on the web site) uses the following simple
Student class:
class Student
{
public string FirstName { get; set; }
public string LastName { get; set; }
public List<int> TestScores { get; set; }
}
The program uses the following query to select all of the students’ names and test averages ordered
by name:
// Select all students and their test averages ordered by name.
var allStudents =
596906c37.indd 416 4/7/10 12:35:00 PM
Select Clauses
417
from Student student in students
orderby student.LastName, student.FirstName
select String.Format(“{0} {1} {2:0.00}“,
student.FirstName, student.LastName,
student.TestScores.Average());
allListBox.DataSource = allStudents.ToArray();
FIGURE 372
This query’s select statement does not select the range variable student. Instead it selects a string
that holds the students first and last names and the student’s test score average. (Notice how the
code calls the
TestScore list’s Average method to get the average of the test scores.) The result of
the query is a
List<string> instead of a List<Student>.
The program next uses the following code to list the students who have averages of at least 60, giving
them passing grades:
// Select passing students ordered by name.
var passingStudents =
from Student student in students
orderby student.LastName, student.FirstName
where student.TestScores.Average() >= 60
select student.FirstName + “ “ + student.LastName;
passingListBox.DataSource = passingStudents.ToArray();
This code again selects a string instead of a Customer object. The code that selects failing students
is similar, so it isn’t shown here.
The program uses the following code to select students with averages below the class average:
// Select all scores and compute a class average.
var allAverages =
from Student student in students
orderby student.LastName, student.FirstName
select student.TestScores.Average();
double classAverage = allAverages.Average();
// Display the average.
this.Text = “FindStudents: Class Average = “ + classAverage.ToString(“0.00”);
596906c37.indd 417 4/7/10 12:35:00 PM
418
LESSON 37 LINQ to objects
// Select students with average below the class average ordered by average.
var belowAverageStudents =
from Student student in students
orderby student.TestScores.Average()
where student.TestScores.Average() < classAverage
select new {Name = student.FirstName + “ “ + student.LastName,
Average = student.TestScores.Average()};
foreach (var info in belowAverageStudents)
{
belowAverageListBox.Items.Add(info.Name + “ ” + info.Average);
}
This snippet starts by selecting all of the students’ test score averages. This returns a List<double>.
The program calls that lists
Average function to get the class average.
Next the code queries the student data again, this time selecting students with averages below the
class average.
This query demonstrates a new kind of select clause that creates a list of objects. The new objects
have two properties,
Name and Average, that are given values by the select clause.
The data type of these new objects is created automatically and isn’t given an explicit name so this is
known as an anonymous type.
After creating the query, the code loops through its results, using each object’s
Name and Average
property to display the below average students in a
ListBox. Notice that the code gives the looping
variable
info the implicit data type var so it doesn’t need to figure out what data type it really has.
Objects with anonymous data types actually have a true data type, just not
one that you want to have to figure out. For example, you can add the follow-
ing statement inside the previous code’s
foreach loop to see what data type the
objects actually have:
Console.WriteLine(info.GetType().ToString());
If you look in the Output window, you’ll see that these objects have the ungainly
data type:
<>f__AnonymousType0`2[System.String,System.Double]
Though you can sort of see whats going on here (note that the object contains
a string and a double), you probably wouldn’t want to type this mess into your
code even if you could. In this case, the
var type is a lot easier to read.
LINQ provides plenty of other features that wont fit in this lesson. It lets you:
Group results to produce output lists that contain other lists
Take only a certain number of results or take results while a certain condition is true
596906c37.indd 418 4/7/10 12:35:01 PM
Try It
419
Skip a certain number of results or skip results while a certain condition is true
Join objects selected from multiple data sources
Use aggregate functions such as
Average (which you’ve already seen), Count, Min, Max, and Sum
Microsoft’s “Language-Integrated Query (LINQ)” page at msdn.microsoft.com/library/
bb397926.aspx
provides a good starting point for learning more about LINQ.
TRY IT
In Lesson 30s Try It, you built a program that used the DirectoryInfo class’s GetFiles
method to search for files matching a pattern and containing a target string. For example, the
program could search the directory hierarchy starting at C:C#Projects to find files with the .cs
extension and containing the string “DirectoryInfo.
In this Try It, you modify that program to perform the same search with LINQ. Instead of writing
code to loop through the files returned by
GetFiles and examining each, you make LINQ examine
the files for you.
You can download the code and resources for this Try It from the book’s web
page at
www.wrox.com or www.CSharpHelper.com/24hour.html. You can find
them in the Lesson37 folder of the download.
Lesson Requirements
Copy the program you built for Lesson 30’s Try It (or download Lesson 30’s version from
the book’s web site) and modify the code to use LINQ to search for files.
Hints
Use the
DirectoryInfo object’s GetFiles method in the query’s from clause.
In the query’s where clause, use the
File class’s ReadAllText method to get the file’s contents.
Convert it to lowercase and use
Contains to see if the file holds the target string.
Step-by-Step
Copy the program you built for Lesson 30’s Try It (or download Lesson 30’s version from
the book’s web site) and modify the code to use LINQ to search for files.
1. Copying the program is reasonably straightforward. To use LINQ to search for files,
modify the Search button’s
Click event handler so it looks like the following. The lines
in bold show the modified code.
596906c37.indd 419 4/7/10 12:35:01 PM
420
LESSON 37 LINQ to objects
// Search for les matching the pattern
// and containing the target string.
private void searchButton_Click(object sender, EventArgs e)
{
// Get the le pattern and target string.
string pattern = patternComboBox.Text;
string target = targetTextBox.Text.ToLower();
// Search for the les.
DirectoryInfo dirinfo =
new DirectoryInfo(directoryTextBox.Text);
var leQuery =
from FileInfo leinfo
in dirinfo.GetFiles(pattern,
SearchOption.AllDirectories)
where
File.ReadAllText(leinfo.FullName).ToLower().Contains(target)
select leinfo.FullName;
// Display the result.
leListBox.DataSource = leQuery.ToArray();
}
Please select Lesson 37 on the DVD to view the video that accompanies this lesson.
EXERCISES
1. Build a program that lists the names of the files in a directory together with their sizes,
ordered with the biggest files first.
2. Copy the program you built for Exercise 1 and modify it so it searches for files in the direc-
tory hierarchy starting at the specified directory.
3. Make a program that lists the perfect squares between 0 and 999.
For Exercises 4 through 8 download the CustomerOrders program. This program defines the
following classes:
class Person
{
public string Name { get; set; }
}
class OrderItem
{
public string Description { get; set; }
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
596906c37.indd 420 4/7/10 12:35:02 PM
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.28.93