Introduce DataBase,Asp.net,JavaScript,Xml,Html,Css,Sql,Php,ASP.NET Controls,AJAX,Tools,HTML,CSS,JavaScript,Open Source Project,WPF,.Net Framework,Linq
Top Recommended Hosting

Linq Query Operators(1)

by the3factory 3/13/2008 7:20:00 AM

The Where Operator

Imagine that you need to list the names and cities of customers from Italy. To filter a set of items, you can use the Where operator, which is also called a restriction operator because it restricts a set of items. Listing 4-3 shows a simple example.

Listing 4-3: A query with a restriction
Image from book

var expr =
from   c in customers
where  c.Country == Countries.Italy
select new { c.Name, c.City };
Image from book

Here are the signatures of the Where operator:

public static IEnumerable<T> Where<T>(
this IEnumerable<T> source,
Func<T, bool> predicate);
public static IEnumerable<T> Where<T>(
this IEnumerable<T> source,
Func<T, int, bool> predicate);

As you can see, two signatures are available. In Listing 4-3, we used the first signature, which enumerates items of the source sequence and yields those that verify the predicate (c.Country == Countries.Italy). The second signature accepts an additional parameter of type Integer for the predicate. This argument is used as a zero-based index of the elements within the source sequence. Keep in mind that if you pass null arguments to the predicates, an ArgumentNullException error will be thrown. You can use the index parameter to start filtering by a particular index, as shown in Listing 4-4.

Listing 4-4: A query with a restriction and an index-based filter
Image from book

var expr =
customers
.Where((c, index) => (c.Country == Countries.Italy && index >= 1))
.Select(c => c.Name);
Image from book
Important 

In Listing 4-4, we cannot use the LINQ query syntax because the Where version that we want to call is not supported by an equivalent LINQ query clause. We will use both syntaxes from here onward.

The result of Listing 4-4 will be the list of Italian customers, skipping the first one. The capability to filter items of the source sequence by using their positional index is useful when you want to extract a specific page of data from a large sequence of items. Listing 4-5 shows an example.

Listing 4-5: A query with a paging restriction
Image from book

int start = 5;
int end = 10;
var expr =
customers
.Where((c, index) => ((index >= start) && (index < end)))
.Select(c => c.Name);
Image from book

Keep in mind that it is generally not a good practice to store large sequences of data loaded from a database persistence layer in memory; usually, it is better to page data at the persistence layer level. Therefore, use this paging technique only if you have already loaded data into memory. Reloading the current page from a persistence layer is less efficient than directly accessing the sequence already loaded “in memory.”

Projection Operators

The following sections describe how to use projection operators. These operators are used to select (or “project”) contents from the source enumeration into the result.

Select

In Listing 4-3, you saw an example of defining the result of the query by using the Select operator. The signatures for the Select operator are shown here:

public static IEnumerable<S> Select<T, S>(
this IEnumerable<T> source,
Func<T, S> selector);
public static IEnumerable<S> Select<T, S>(
this IEnumerable<T> source,
Func<T, int, S> selector);

The Select operator is one of the projection operators because it projects the query results, making them available through an object that implements IEnumerable<T>. This object will enumerate items identified by the selector predicate. Like the Where operator, Select enumerates the source sequence and yields the result of the selector predicate. Consider the following predicate:

var expr = customers.Select(c => c.Name);

This predicate’s result will be a sequence of customer names (IEnumerable<string>). Now consider this example:

var expr = customers.Select(c => new { c.Name, c.City });

This predicate projects a sequence of an anonymous type, defined as a tuple of Name and City, for each customer object. With the second overload of Select, we can also provide an argument of type Integer for the predicate. This zero-based index is used to define the positional index of each item inserted in the resulting sequence.

SelectMany

Imagine that you want to select all the orders of customers from Italy. You could write the query shown in Listing 4-6 using the verbose method.

Listing 4-6: The list of orders made by Italian customers
Image from book

var orders =
customers
.Where(c => c.Country == Countries.Italy)
.Select(c => c.Orders);
foreach(var item in orders) { Console.WriteLine(item); }
Image from book

Because of the behavior of the Select operator, the resulting type of this query will be IEnumerable<Order[]>, where each item in the resulting sequence represents the array of orders of a single customer. In fact, the Orders property of a Customer instance is of type Order[]. The output of the code in Listing 4-6 would be the following:

DevLeap.Linq.Ch4.Operators.Order[]
DevLeap.Linq.Ch4.Operators.Order[]

To have a “flat” IEnumerable<Order> result type, we need to use the SelectMany operator:

public static IEnumerable<S> SelectMany<T, S>(
this IEnumerable<T> source,
Func<T, IEnumerable<S>> selector);
public static IEnumerable<S> SelectMany<T, S>(
this IEnumerable<T> source,
Func<T, int, IEnumerable<S>> selector);
public static IEnumerable<S> SelectMany<T, C, S>(
this IEnumerable<T> source,
Func<T, IEnumerable<C>> collectionSelector,
Func<T, C, S> resultSelector);

This operator enumerates the source sequence and merges the resulting items, providing them as a single enumerable sequence. The second overload available is analogous to the equivalent overload for Select, which allows a zero-based integer index for indexing purposes. Listing 4-7 shows an example.

Listing 4-7: The flattened list of orders made by Italian customers
Image from book

IEnumerable<Order> orders =
customers
.Where(c => c.Country == Countries.Italy)
.SelectMany(c => c.Orders);
Image from book

Using the query expression syntax, the query in Listing 4-7 can be written with the code shown in Listing 4-8.

Listing 4-8: The flattened list of orders made by Italian customers, written with a query expression
Image from book

IEnumerable<Order> orders =
from   c in customers
where  c.Country == Countries.Italy
from   o in c.Orders
select o;
Image from book

The select keyword in query expressions, for all but the initial from clause, is translated to invocations of SelectMany. In other words, every time you see a query expression with more than one from clause, you can apply this rule: the select over the first from clause is converted to an invocation of Select, and the other select commands are translated into a SelectMany call.

The third overload of SelectMany is useful whenever you need to select a custom result from the source set of sequences instead of simply merging their items, as with the two previous overloads. This overload invokes the collectionSelector predicate over the source sequence and returns the result of the resultSelector predicate, applied to each item in the collections selected by collectionSelector. In Listing 4-9, you can see an example of this method, used to extract a new anonymous type made from the Quantity and IdProduct of each order of Italian customers.

Listing 4-9: The list of Quantity and IdProduct of orders made by Italian customers
Image from book

var items = customers
.Where(c => c.Country == Countries.Italy)
.SelectMany(c => c.Orders,
(c, o) => new {o.Quantity, o.IdProduct});
Image from book

The query in Listing 4-9 can be written with the query expression shown in Listing 4-10.

Listing 4-10: The list of Quantity and IdProduct of orders made by Italian customers, written with a query expression
Image from book

IEnumerable<Order> orders =
from   c in customers
where  c.Country == Countries.Italy
from   o in c.Orders
select new {o.Quantity, o.IdProduct};
Image from book

Ordering Operators

Another useful set of operators is the ordering operators group. Ordering operators are used to determine the ordering and direction of elements in output sequences.

OrderBy and OrderByDescending

Sometimes it is helpful to apply an order to the results of a database query. LINQ can order the results of queries, in ascending or descending order, by using ordering operators, just as we do in SQL syntax. For instance, if you need to select the Name and City of all Italian customers in descending order by Name, you can write the corresponding query expression shown in Listing 4-11.

Listing 4-11: A query expression with a descending orderby clause
Image from book

var expr =
from    c in customers
where   c.Country == Countries.Italy
orderby c.Name descending
select  new { c.Name, c.City };
Image from book

The query expression syntax will translate the orderby keyword into one of the following ordering extension methods:

public static IOrderedSequence<T> OrderBy<T, K>(
this IEnumerable<T> source,
Func<T, K> keySelector);
public static IOrderedSequence<T> OrderBy<T, K>(
this IEnumerable<T> source,
Func<T, K> keySelector,
IComparer<K> comparer);
public static IOrderedSequence<T> OrderByDescending<T, K>(
this IEnumerable<T> source,
Func<T, K> keySelector);
public static IOrderedSequence<T> OrderByDescending<T, K>(
this IEnumerable<T> source,
Func<T, K> keySelector,
IComparer<K> comparer);

As you can see, the two main extension methods, OrderBy and OrderByDescending, both have two overloads. The methods’ names suggest their objective: OrderBy is for ascending order, and OrderByDescending is for descending order. The keySelector argument represents a function that extracts a key, of type K, from each item of type T, taken from the source sequence. The extracted key represents the typed content to be compared by the comparer while ordering, and the T type describes the type of each item of the source sequence. Both methods have an overload that allows you to provide a custom comparer. If no comparer is provided or the comparer argument is null, a default comparer is used (Comparer<K>.Default). It is important to emphasize that these ordering methods return not just IEnumerable<T> but IOrderedSequence<T>, which is an interface that implements IEnumerable<T> internally.

The code sample in Listing 4-11 will be translated to the following:

var expr =
customers
.Where(c => c.Country == Countries.Italy)
.OrderByDescending(c => c.Name)
.Select(c => new { c.Name, c.City } );

ThenBy and ThenByDescending

When you need to order data by many different keys, you can take advantage of the ThenBy and ThenByDescending operators. Here are their signatures:

public static IOrderedSequence<T> ThenBy<T, K>(
this IOrderedSequence<T> source,
Func<T, K> keySelector);
public static IOrderedSequence<T> ThenBy<T, K>(
this IOrderedSequence<T> source,
Func<T, K> keySelector,
IComparer<K> comparer);
public static IOrderedSequence<T> ThenByDescending<T, K>(
this IOrderedSequence<T> source,
Func<T, K> keySelector);
public static IOrderedSequence<T> ThenByDescending<T, K>(
this IOrderedSequence<T> source,
Func<T, K> keySelector,
IComparer<K> comparer);

These operators have signatures similar to OrderBy and OrderByDescending. The difference is that ThenBy and ThenByDescending can be applied only to IOrderedSequence<T> and not to any IEnumerable<T>. Therefore, you can use the ThenBy or ThenByDescending operator just after the first use of OrderBy or OrderByDescending. Here is an example:

var expr = customers
.Where(c => c.Country == Countries.Italy)
.OrderByDescending(c => c.Name)
.ThenBy(c => c.City)
.Select(c => new { c.Name, c.City } );

In Listing 4-12, you can see the corresponding query expression.

Listing 4-12: A query expression with orderby and thenby
Image from book

var expr =
from    c in customers
where   c.Country == Countries.Italy
orderby c.Name descending, c.City
select  new { c.Name, c.City };
Image from book
Important 

In the case of multiple occurrences of the same key within a sequence to be ordered, the result is not guaranteed to be “stable.” In such conditions, the original ordering cannot be preserved by the comparer.

A custom comparer might be useful when the items in your source sequence need to be ordered using custom logic. For instance, imagine that you want to select all the orders of your customers ordered by month:

var expr =
from c in customers
from    o in c.Orders
orderby o.Month
select  o;

If you apply the default comparer to the Month property of the orders, you will get a result alphabetically ordered. The result is wrong because the Month property is just a string and not a number or a date:

20 - True - December – 3
20 - True - December – 3
3 - False - January – 1
20 - False - July – 5
10 - False - July – 1
5 - True - May - 2

You should use a custom MonthComparer that correctly compares months:

using System.Globalization;
private class MonthComparer: IComparer<string> {
public int Compare(string x, string y) {
DateTime xDate = DateTime.ParseExact(x, "MMMM", new CultureInfo("en-US"));
DateTime yDate = DateTime.ParseExact(y, "MMMM", new CultureInfo("en-US"));
return(Comparer<DateTime>.Default.Compare(xDate, yDate));
} }

The newly defined custom MonthComparer could be passed as a parameter while invoking the OrderBy extension method, as in Listing 4-13.

Listing 4-13: A custom comparer used with an OrderBy operator
Image from book

IEnumerable<Order> orders =
customers
.SelectMany(c => c.Orders)
.OrderBy(o => o.Month, new MonthComparer());
Image from book

Reverse Operator

Sometimes you need to reverse the result of a query, listing the last item in the result first. LINQ provides a last-ordering operator, called Reverse, that allows you to perform this operation:

public static IEnumerable<T> Reverse<T>(
this IEnumerable<T> source);

The implementation of Reverse is quite simple. It just yields each item in the source sequence in reverse order. Listing 4-14 shows an example of its use.

Listing 4-14: The Reverse operator applied
Image from book

var expr =
customers
.Where(c => c.Country == Countries.Italy)
.OrderByDescending(c => c.Name)
.ThenBy(c => c.City)
.Select(c => new { c.Name, c.City } )
.Reverse();
Image from book

The Reverse operator, like many other operators, does not have a short “alias” in LINQ query expressions. However, we can merge query expression syntax with operators, as shown in Listing 4-15.

Listing 4-15: The Reverse operator applied to a query expression with orderby and thenby
Image from book

var expr =
(from    c in customers
where   c.Country == Countries.Italy
orderby c.Name descending, c.City
select  new { c.Name, c.City }
).Reverse();
Image from book

As you can see, we apply the Reverse operator to the expression resulting from Listing 4-11. Under the covers, the inner query expression is first translated to the resulting list of extension methods, and then the Reverse method is applied. It is just like Listing 4-14, but easier to write

Tags:

Linq

Related posts

Sign up for PayPal and start accepting credit card payments instantly.


Powered by BlogEngine.NET 1.2.0.0