FREE BOOK

Chapter 11: An introduction to LINQ

Posted by Murach Free Book | LINQ August 13, 2009
In this chapter, you’ll learn the basic concepts and skills for using a new feature of C# 2008 called LINQ. To illustrate these concepts and skills, I’ll use an implementation of LINQ called LINQ to Objects. You use LINQ to Objects to work with in-memory data structures such as generic lists and arrays.

How to use lambda expressions with extension methods

Several of the extension methods that are used to implement LINQ define one or more parameters that accept lambda expressions. Like the invoiceOver20000 variable you saw in the last example, these parameters represent delegates that specify the signature of a method. For example, the second parameter of the Where method is defined as a delegate that accepts a function with two parameters. The first parameter is the source element, and the second parameter is a Boolean expression.

The second example in this figure should help you understand how this works. Here, the query uses extension methods and lambda expressions instead of C# clauses. As you review this query, remember that when you use an extension method, you execute it on an instance of the data type it extends. In this case, the extension methods are executed on the invoiceList object, which is an Enumerable type.

The query shown here uses three extension methods, and each method accepts a lambda expression that identifies two parameters. In each case, the first parameter is the source element, which is an invoice in this example. Then, the second parameter of the lambda expression for the Where method is a Boolean expression, the second parameter for the OrderBy method is an expression that specifies the key that's used for sorting, and the second parameter for the Select method is an object that identifies the values to be returned by the query.

The basic syntax of a lambda expression
[(]parameterList[)] => expression

A lambda expression that tests a condition

A statement that declares the delegate type

delegate bool compareDel(decimal total);

A statement that defines the lambda expression and assigns it to a variable created from the delegate type
compareDel invoiceOver20000 = total => total > 20000;

Code that executes the lambda expression
decimal invoiceTotal = 22438.19M;
string invoiceMessage = "";
invoiceMessage += "Invoice Total: " + invoiceTotal.ToString("c") +
"\n" + "Invoice over $20,000: " +
invoiceOver20000(invoiceTotal);
MessageBox.Show(invoiceMessage, "Invoice Test");

The resulting dialog box

A query that uses extension methods and lambda expressions

var invoices = invoiceList

.Where(i => i.InvoiceTotal > 20000)

.OrderBy(i => i.VendorID)

.Select(i => new { i.VendorID, i.InvoiceTotal });

The same query using C# clauses

var invoices = from invoice in invoiceList

              where invoice.InvoiceTotal > 20000

              orderby invoice.VendorID

              select new { invoice.VendorID, invoice.InvoiceTotal };

Description

  • When a LINQ query is compiled, it's translated into a method-based query. You can also code method-based queries explicitly.
  • To code a method-based query, you use lambda expressions. A lambda expression consists of an unnamed function that evaluates a single expression and returns its value.
  • Lambda expressions are typically passed as arguments to methods that accept a delegate, which specifies the signature of a method. Many of the LINQ methods, including the Where, OrderBy, and Select methods shown above, accept delegates as parameters.
  • Because method-based queries and lambda expressions are more difficult to work with than queries that use C# clauses, we recommend you use clauses whenever possible.

Figure 11-12 How to use lambda expressions

The last example is a query that performs the same function as the previous query but uses C# clauses instead of extension methods. If you compare these two queries, I think you'll agree that the one that uses C# clauses is easier to understand. Because of that, we recommend you use this technique whenever possible.

How to use extension methods that implement aggregate functions

In addition to the extension methods you've seen in the last two topics, LINQ provides methods that aren't associated with C# clauses. In this topic, you'll learn about the extension methods that implement aggregate functions. You'll learn about some additional extension methods later in this book.

The table at the top of figure 11-13 lists the extension methods LINQ provides for performing aggregate functions. These methods perform an operation on a set of elements. If you review the descriptions of these methods, you shouldn't have any trouble understanding how they work.

The first example in this figure shows how you can use an aggregate method to summarize the results of a query. Here, the Average method is called on a query that returns the invoice totals for a list of invoices. Notice that when you call a method like this on query results, the query expression must be enclosed in parentheses. Also notice that because the query must be executed before the average can be calculated, the query is executed immediately. Then, the result returned by the Average method is assigned to a decimal variable.

You can also use the aggregate methods to summarize the groups defined by a query. This is illustrated in the second example in this figure. Here, the invoices in a list of Invoice objects are grouped by vendor ID. Then, the where clause uses the Sum method to calculate an invoice total for each vendor so that only those vendors with invoice totals over $10,000 are returned by the query. Notice that a lambda expression is used within the Sum method to indicate which field is to be totaled. In contrast, it wasn't necessary to use a lambda expression in the first example because the query returns a single field.

The Sum method is also used in the orderby clause to sort the grouped invoice totals in descending sequence, and it's used in the select clause to include the invoice total for each vendor in the query results along with the vendor ID. Then, the code that executes the query uses a foreach statement to loop through the results and display the vendor ID and invoice total for each vendor.

Extension methods that implement aggregate functions

Method Description
All Returns a Boolean value that specifies if all the elements of a collection satisfy a condition.
Any Returns a Boolean value that specifies if any of the elements of a collection satisfy a condition.
Average Calculates the average of a given field or expression.
Count Counts the number of elements in a collection or the number of elements that satisfy a condition.
LongCount Same as Count, but returns the count as a long type.
Max Calculates the maximum value of a given field or expression.
Min Calculates the minimum value of a given field or expression.
Sum Calculates the sum of a given field or expression.

A query expression that gets the average of invoice totals
decimal invoiceAvg = (from invoice in invoiceList
select invoice.InvoiceTotal).Average();

A LINQ query that uses an aggregate with groups

A query expression that gets invoice totals by vendor

var largeVendors =

from invoice in invoiceList

group invoice by invoice.VendorID

    into invoiceGroup

    where invoiceGroup.Sum(i => i.InvoiceTotal) > 10000

    orderby invoiceGroup.Sum(i => i.InvoiceTotal) descending

    select new

    {

        ID = invoiceGroup.Key,

        Total = invoiceGroup.Sum(i => i.InvoiceTotal)

    };

Code that displays the query results
string totalDisplay = "Vendor ID\tInvoice Total\n";
foreach (var vendor in largeVendors)
{
    totalDisplay += vendor.ID + "\t\t" +
    vendor.Total.ToString("c") + "\n";
}
MessageBox.Show(totalDisplay, "Invoice Totals by Vendor");

Description

  • The aggregate methods can be called on the result of a query to return a single value or on a group within a query to return a value for each group. Lambda expressions can be used with aggregate methods to identify the operation that's performed.
  • Because C# doesn't provide keywords for the methods shown above, you can only use them by executing the methods directly.

Figure 11-13 How to use extension methods that implement aggregate functions

Total Pages : 10 678910

comments