1

I'm trying to get this left outer join working, but I seem to be encountering some issues. I have taken the example code from the MSDN left join article. This example is in LINQ syntax, but I want mine in extension method syntax, so I have also referenced this SO question.

Person magnus = new Person { FirstName = "Magnus", LastName = "Hedlund" };
Person terry = new Person { FirstName = "Terry", LastName = "Adams" };
Person charlotte = new Person { FirstName = "Charlotte", LastName = "Weiss" };
Person arlene = new Person { FirstName = "Arlene", LastName = "Huff" };

Pet barley = new Pet { Name = "Barley", Owner = terry };
Pet boots = new Pet { Name = "Boots", Owner = terry };
Pet whiskers = new Pet { Name = "Whiskers", Owner = charlotte };
Pet bluemoon = new Pet { Name = "Blue Moon", Owner = terry };
Pet daisy = new Pet { Name = "Daisy", Owner = magnus };

// Create two lists.
List<Person> people = new List<Person> { magnus, terry, charlotte, arlene };
List<Pet> pets = new List<Pet> { barley, boots, whiskers, bluemoon, daisy };

var query = people
            .GroupJoin(pets,
            p1 => p1.FirstName,
            p2 => p2.Owner.FirstName,
            (p1, p2) => new {p1,p2})
            .SelectMany(x => x.p2.DefaultIfEmpty(),
             (x, y) => new { FirstName = x.p1.FirstName, PetName = y.Name });

        foreach (var v in query)
        {
            Console.WriteLine("{0,-15}{1}", v.FirstName + ":", v.PetName);
        }

My query is basically identical to the code I referenced, but I am getting this error:

NullReferenceException
Object reference not set to an instance of an object. 

This should be extremely simple. What am I doing wrong?

Community
  • 1
  • 1
Jeff
  • 2,283
  • 9
  • 32
  • 50
  • Query syntax is much easier to read. But to answer your question, before you access a property in the object (from the left join) you have to check it for `null`. (When using Linq2Sql this is however not needed) – Magnus Apr 03 '13 at 20:40

1 Answers1

1

You're missing that part of sample query:

PetName = (subpet == null ? String.Empty : subpet.Name)

On your query it's just PetName = y.Name, so whenever there is no corresponding row in pets list you're getting NullReferrenceException because y is null.

Should be:

var query = people
            .GroupJoin(pets,
            p1 => p1.FirstName,
            p2 => p2.Owner.FirstName,
            (p1, p2) => new { p1, p2 })
            .SelectMany(x => x.p2.DefaultIfEmpty(),
                (x, y) => new { FirstName = x.p1.FirstName, PetName = (y == null ? String.Empty : y.Name) });

Or you can use DefaultIdEmpty(TSource) method overload:

var def = new Pet { Name = string.Empty };

var query = people
            .GroupJoin(pets,
            p1 => p1.FirstName,
            p2 => p2.Owner.FirstName,
            (p1, p2) => new { p1, p2 })
            .SelectMany(x => x.p2.DefaultIfEmpty(def),
                (x, y) => new { FirstName = x.p1.FirstName, PetName = y.Name });
MarcinJuraszek
  • 124,003
  • 15
  • 196
  • 263
  • That works, but I thought that was the purpose of `DefaultIfEmpty`. Doesn't that take care of the cases where the right hand collection contains null values? – Jeff Apr 03 '13 at 20:41
  • `DefaultIfEmpty` returns default value where collection is empty (e.g. there is no corresponding values in joined collection). For all reference types it's `null`. That's how `LEFT JOIN` works. – MarcinJuraszek Apr 03 '13 at 20:42
  • So since I know `DefaultIfEmpty` has an overload where I can specify an object to use, it should be possible to create a `Pet` object: `Pet defaultPet = new Pet { Name = "" };` and use `DefaultIfEmpty(defaultPet) right?` In this case, I would not need to check for null values in my `SelectMany`. – Jeff Apr 03 '13 at 20:46