2013-04-03 118 views
1

我试图让这个左外连接工作,但我似乎遇到了一些问题。我已经拿到了MSDN left join article的示例代码。这个例子是在LINQ语法中的,但是我希望我的扩展方法语法,所以我也引用了这个SO问题。左外连接错误

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); 
     } 

我的查询基本上等同于我所引用的代码,但我得到这个错误:

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

这应该是非常简单的。我究竟做错了什么?

+0

查询语法更易于阅读。但是要回答你的问题,在你访问对象中的一个属性(从左连接)之前,你必须检查它是否为null。 (当使用Linq2Sql时,这是不需要的) – Magnus 2013-04-03 20:40:24

回答

1

你错过了样本查询部分:

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

您的查询它只是PetName = y.Name,所以每当有pets列表中没有相应的行你得到NullReferrenceException因为ynull

应该是:

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) }); 

或者你可以使用DefaultIdEmpty(TSource)方法重载:

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 }); 
+0

这很有效,但我认为这是'DefaultIfEmpty'的用途。这不涉及右手收集包含空值的情况吗? – Jeff 2013-04-03 20:41:38

+0

'DefaultIfEmpty'返回集合为空时的默认值(例如,连接的集合中没有对应的值)。对于所有的引用类型,它是'null'。这就是'LEFT JOIN'的工作原理。 – MarcinJuraszek 2013-04-03 20:42:38

+0

因此,因为我知道'DefaultIfEmpty'有一个重载的地方,我可以指定一个对象来使用,应该可以创建一个'Pet'对象:'Pet defaultPet = new Pet {Name =“”};'并使用'DefaultIfEmpty (defaultPet)对吗?'在这种情况下,我不需要在我的'SelectMany'中检查空值。 – Jeff 2013-04-03 20:46:24