LINQ to Objects - Selects & Joins

1. Create an object class with basic constructor:

public class Employee
{
    public string Name { get; set; }
    public int Score { get; set; }
    public Employee(string name, int score)
    {
        Name = name;
        Score = score;
    }
}
2. In your program, create a generic list of the objects (to simulate a collection of them gleaned from a DB or elsewhere):

        List<Employee> theData = new List<Employee>();
        theData.Add(new Employee("fred", 10));
        theData.Add(new Employee("bob", 8));
        theData.Add(new Employee("sue", 6));
        theData.Add(new Employee("amanda", 4));
        theData.Add(new Employee("graham", 2));
        theData.Add(new Employee("mary", 0));
3. Examples of simple queries on this collection might be as follows:

To select all records from the collection:

      var query = from n in theData select n;

To select all records from the collection where Score > 5:
      var query = from n in theData where n.Score>5 select n;

To select all records from the collection where the name is "sue":
      var query = from n in theData where n.Name.Equals("sue") select n;

3. The query is dynamically typed as var, because we may not know until runtime what objects are in the returned query, only that it will implement IEnumerable. In this case, it comes back as IEnumerable<Employee> because that is the type of the collection. This can be iterated around to do something with the query results:
        foreach (Employee emp in query)
        {
            Response.Write(String.Format("Employee:{0} - Score:{1}<br/>", emp.Name, emp.Score));
        }
4. We may wish to do complex joins between collections of objects in a single query (to be bound to a datagrid, for example). This can be achieved through the LINQ syntax. Create a new class, which will hold the mapping between score and rating comment:
public class Rating
{
    public int Score { get; set; }
    public string Comment { get; set; }
    public Rating(int score, string comment)
    {
        Score = score;
        Comment = comment;
    }
}

And populate a generic list with comments against scores:
        List<Rating> theRatings = new List<Rating>();
        theRatings.Add(new Rating(10,"crap"));
        theRatings.Add(new Rating(8,"rubbish");
        theRatings.Add(new Rating(6,"average");
        theRatings.Add(new Rating(4,"good");
        theRatings.Add(new Rating(2,"excellent");
        theRatings.Add(new Rating(0,"fantastic");
5. The query can now be written to join the Employee list with the Rating list as follows:
        var query = from n in theData
                    join r in theRatings on n.Score equals r.Score into subQuery
                    from c in subQuery.DefaultIfEmpty( new Rating(n.Score, "Unrated") )
                    select new { n.Name, n.Score, c.Comment };

This query takes a little explaining:

  Line 1: as before, selects from the first generic collection and aliases it as variable n

  Line 2: joins the query with the second collection with the joining criterion and saves in a named subQuery object

  Line 3: the subquery is aliased, but with the proviso that any items in the second list which are null can be replaced with
             a new blank Rating object with the same score, but with an "Unrated" comment.

  Line 4: the select builds up a new anonymous object to return, made up of the properties of the first collection, and the
             matching comment column from the subQuery

6. The query now returns an enumeration of these new anonymous objects, if you check in Visual Studio, you will see it's of a complex IEnumerable type called System.Linq.Enumerable.SelectManyIterator<T,T> with the parts of the type specification made up of the varying objects we collected in our select. This means the iteration around the query has to be of type var and not Employee as before, but it does mean all the fields are available for us to use, inluding the new joined Comment field:
  foreach (var emp in query)
  {
    Response.Write(String.Format("Employee:{0} - Score:{1} - {2}<br/>", emp.Name, emp.Score, emp.Comment));
  }
  
Which is printed on screen as the complete set with comments:

Employee:fred Score:10 - fantastico
Employee:bob Score:8 - excellent
Employee:sue Score:6 - good
Employee:amanda Score:4 - average
Employee:graham Score:2 - rubbish
Employee:mary Score:0 - crap

And that is how you do simple object selects and joins in LINQ! Obviously there is more to the LINQ syntax, more selectors and iterators and collections, but that's the basics.