LINQ join Clause -Left Outer Join

Using LINQ’s join clause, you can also perform  a left outer join. Like an inner join, a left outer join also returns a flat  result. An inner join omits any item that has no corresponding items from  another data source. As an example, if an author wrote no book, then he will be  omitted in the result of the query. The left outer join includes even the items that have no corresponding partner in the result. This is made possible using the DefaultIfEmpty method.

LINQ Left Outer Join
LINQ Left Outer Join

Figure 1 – Left Outer Join

Let’s take a look at an example of doing a left outer join.

Author[] authors = new Author[]                                                        
{                                                                                      
    new Author() { AuthorId = 1, Name = "John Smith" },                                
    new Author() { AuthorId = 2, Name = "Harry Gold" },                                
    new Author() { AuthorId = 3, Name = "Ronald Schwimmer" },                          
    new Author() { AuthorId = 4, Name = "Jerry Mawler" }                               
};                                                                                    
                                                                                       
Book[] books = new Book[]                                                              
{                                                                                      
    new Book() { AuthorId = 1, Title = "Little Blue Riding Hood" },                    
    new Book() { AuthorId = 3, Title = "The Three Little Piggy Banks" },               
    new Book() { AuthorId = 1, Title = "Snow Black" },                                 
    new Book() { AuthorId = 2, Title = "My Rubber Duckie" },                           
    new Book() { AuthorId = 2, Title = "He Who Doesn't Know His Name" },               
    new Book() { AuthorId = 1, Title = "Hanzel and Brittle" }                          
};                                                                                     
                                                                                       
var result = from a in authors                                                          
             join b in books on a.AuthorId equals b.AuthorId into booksByAuthors        
             from x in booksByAuthors.DefaultIfEmpty(new Book {AuthorId=0,Title="None"})
             select new { Author = a.Name, x.Title };                                   
                                                                                       
Console.WriteLine("{0, -20} {1}", "Author", "Book");                                   
foreach (var r in result)                                                              
{                                                                                      
    Console.WriteLine("{0, -20} {1}", r.Author, r.Title);                              
}

Example 1

Author Book
John Smith Little Blue Riding Hood
John Smith Snow Black
John Smith Hanzel and Brittle
Harry Gold My Rubber Duckie
Harry Gold He Who Doesn't Know His Name
Ronald Schwimmer The Three Little Piggy Banks
Jerry Mawler None

To do a left outer join using the join clause, you first need to group join  the two data sources. You then perform another query by using the created groups as the  data source. You need to call the DefaultIfEmpty  for each group so whenever a group contains no items, a specified default value  will be provided.

As you can see in the query expression in lines 19 to 22, the first two lines  of the query expression performs a group join by querying every author object  and joining every book object which has an equal AuthorId  property as the queried author’s AuthorId  property. The next line performs another query by using the grouped result of  the first query as the data source. Notice that we call the DefaultIfEmpty method of the group to yield a  default value if the group is empty. The DefaultIfEmpty  method accepts one argument, which is an instance of an object which has a  similar type as every item of the group. Since each group in our query contains Book items, we created a new instance of the Book class and specified some default values for  its properties using objection initialization syntax.

Based on our data sources, Jerry Mawler (AuthorId  4) has no corresponding book from the books data source. If we simply used  an inner join, Jerry Mawler will be gone in the results, but since we used left  outer join, Jerry Mawler was included and as you can see in the output of Figure  2, the default value you specified was shown as his book.

There is no direct equivalent of a left outer join when you want to use the  method syntax. Doing a left outer join using the method syntax requires the combination of  the GroupBy method and the SelectMany method.

var result = authors.GroupJoin(books,
                               author => author.AuthorId,
                               book => book.AuthorId,
                               (author, booksByAuthor) => 
                               new { Author = author, Books = booksByAuthor })
                    .SelectMany(x=>x.Books.DefaultIfEmpty(new Book{AuthorId = 0,Title = "None"}),
                               (x, y) => new { Author = x.Author.Name, y.Title });

The GroupJoin method simply groups each book by author and projects a new type with an Author  property assigned with the author and Books  property assigned with the group of books he wrote. We then nested a call to the SelectMany  method. The SelectManymethod here accepts two  parameters. The first is the collection selector which selects an item from the result yielded by the GroupJoinmethod. Notice that we call the DefaultIfEmpty method so if the Books property of  an item is empty, then a default set of values specified will be used. The  second parameter is the result selector and through here, you can project the  final result of the query.