The three stages of a query operation
Figure 11-3 presents the three stages of a query operation and illustrates these stages using a generic list. The first stage is to get the data source. How you do that depends on the type of data source you're working with. For the generic list shown here, getting the data source means declaring a variable to hold the list and then calling a method that returns a List<Invoice> object.
The second stage is to define the query expression. This expression identifies the data source and the data to be retrieved from that data source. The query expression in this figure, for example, retrieves all the invoices with an invoice total greater than 20,000. It also sorts those invoices by invoice total in descending sequence. (Don't worry if you don't understand the syntax of this query expression. You'll learn how to code query expressions in the topics that follow.)
Notice here that the query expression is stored in a query variable. That's necessary because this query isn't executed when it's defined. Also notice that the query variable is declared with the var keyword. This keyword indicates that the variable will be given a type implicitly based on the type of elements returned by the query. As you learned in the previous figure, this is one of the new features of C#. In this case, because the query returns Invoice objects, the query variable is given the type IEnumerable<Invoice>.
For this to work, the data source must be an enumerable type, which means that it implements the IEnumerable<T> interface. The data source can also implement the IQueryable<T> interface since this interface implements IEnumerable<T>. In case you're not familiar with interfaces, they consist of a set of declarations for one or more properties, methods, and events, but they don't provide implementation for those properties, methods, and events. For the example in this figure, however, all you need to know is that the List<> class implements the IEnumerable<T> interface.
The third stage of a query operation is to execute the query. To do that, you typically use a foreach statement like the one shown in this figure. Here, each element that's returned by the query expression is added to a string variable. Then, after all the elements have been processed, the string is displayed in a message box. As you can see, this message box lists the invoice numbers and invoice totals for all invoices with totals greater than 20,000.
When a query is defined and executed separately as shown here, the process is referred to as deferred execution. In contrast, queries that are executed when they're defined use immediate execution. Immediate execution typically occurs when a method that requires access to the individual elements returned by the query is executed on the query expression. For example, to get a count of the number of elements returned by a query, you can execute the Count method on the query expression. Then, the query will be executed immediately so the count can be calculated. You'll learn about some of the methods for returning these types of values later in this chapter.
A LINQ query that retrieves data from a generic list of invoices
A statement that declares and populates the list
List<Invoice> invoiceList = InvoiceDB.GetInvoices();
A statement that defines the query expression
var invoices = from invoice in invoiceList
where invoice.InvoiceTotal > 20000
orderby invoice.InvoiceTotal descending
select invoice;
Code that executes the querystring invoiceDisplay = "Invoice No.\tInvoice Total\n";foreach (var invoice in invoices){ invoiceDisplay += invoice.InvoiceNumber + "\t\t" + invoice.InvoiceTotal.ToString("c") + "\n";}MessageBox.Show(invoiceDisplay, "Invoices Over $20,000");
The resulting dialog box
Description
Figure 11-3 The three stages of a query operation