Now that you have learnt some basic LINQ querying including how to select, filter, and order results of a query, let’s take a look at more examples combining the concepts of the past lessons and introducing some new LINQ features as well. The examples will allow you to more familiar to more techniques of using LINQ. I will show the LINQ query and its corresponding method calls to LINQ methods.

var query1 = from p in people
             orderby p.LastName
             where p.Age >= 18
             select new { p.FirstName, LN = p.LastName };
var query1 = people.OrderBy(p => p.LastName).Where(p => p.Age >= 18)
                   .Select(p => new { p.FirstName, LN = p.LastName });

The queries above are equivalent and they both represent some combinations of selecting, filtering and ordering results. We must take note of the second version where we used the actual LINQ methods. The dot operator was used immediately after the end of the previous method. The methods or members you can call after the dot operator depends on the value returned by the previous method. Since LINQ methods return a result implementing IEnumerable<T>, you can nest or cascade method calls one after the other. The final result to be stored in the result variable depends on the final results of the last LINQ method call, in the case above, the Select method. The second version presented can also be called the dot notation style.

var query2 = from p in people
             orderby p.LastName, p.FirstName
             where p.Age >= 18 && p.LastName.StartsWith("A")
             select new { FullName = p.FirstName + " " + p.LastName };
var query2 = people.OrderBy(p=>p.LastName).ThenBy(p=>p.FirstName)
                   .Where(p=>p.Age >= 18 && p.LastName.StartsWith("A"))
                   .Select(p=> new {FullName = p.FirstName + " " + p.LastName });

The query selects persons from the people collection ordering the results by LastName and then by FirstName, and whose Age is greater than or equal 18 and has a LastName which starts with A. The query then selects an anonymous type containing the FullName of the person that met the condition in the where clause.

We can use the let clause to define another range variable inside a query and assign it with an expression or property of the original range variable. The following shows you an example of using the let clause.

var query2 = from p in people
             let lastName = p.LastName
             where lastName.StartsWith("A")
             select lastName;
var query2 = people.Select(p=> new { p, lastName = p.LastName })
                   .Where(pln => pln.lastName.StartsWith("A"))
                   .Select(pln => pln.lastName)

We defined a new range variable and assigned the LastName property of the original range variable. We can now use the new range variable in the following clauses. The second version that uses dot notation style shows how we can do the same functionality as the first version. The let clause in a LINQ query is just similar to a Select method. We called the Select method at the very beginning so the following methods will know the modifications made.

We can use multiple data sources and compare each of their values against each other. The following uses two from clauses that retrieve values from two integer arrays.

var query3 = from x in n1
             from y in n2
             where x == y
             select x;
var query3 = n1.Intersect(n2);

The query first retrieves a value from n1. The retrieved value will then be compared to each value in n2 thanks to the second from clause and the where clause. The where clause states that only retrieve the value of x that has an equal value in any of the elements of y. This is also called intersections where only values that both exists in two sets will be returned in the result set. We can simply use the Intersect method to do the same thing.

Another example is an object that contains a property containing a collection of more objects. Imagine our Person class having a property named Siblings which is a list of siblings (List<Person>) of a person. We can select every sibling of a person and even specify a condition for the selection.

var query4 = from p in people
             from s in p.Siblings
             where s.Age < p.Age
             select new { FullName = p.FirstName + " " + p.LastName,
                          SiblingName = s.FirstName + " " + s.LastName };
var query4 = people.SelectMany(p => p.Siblings.Select(s => s).Where(s => s.Age < p.Age));

The first from clause retrieves a Person from the list of Person objects. The second from clause retrieves every Person object in the current Person‘s Sibling property. The first from clause will only continue to retrieve the next Person object after the second from clause is finish retrieving all of the items from the Siblings property. The where and select clauses in the query also execute for every item retrieve by the latter from clause from its corresponding data source. The second version of the query uses the SelectMany method. You can see that inside it is a lambda expression selecting every Person from people and we accessed each person’s Sibling property and used another Select method followed by a Where method inside the SelectMany method.