2009-02-08 5 views
41

Использование синтаксиса расширения. Я пытаюсь создать соединение слева с использованием LINQ в двух списках, которые у меня есть. Ниже приведена справка Microsoft, но я изменил ее, чтобы показать, что список домашних животных не имеет элементов. То, что я получаю, - это список из 0 элементов. Я предполагаю, что это происходит потому, что происходит внутреннее соединение. То, что я хочу закончить, - это список из 3 элементов (объекты 3 человека) с нулевыми данными, заполненными для отсутствующих элементов. т.е. левое соединение. Это возможно?LINQ Inner-Join vs Left-Join

Person magnus = new Person { Name = "Hedlund, Magnus" }; 
Person terry = new Person { Name = "Adams, Terry" }; 
Person charlotte = new Person { Name = "Weiss, Charlotte" }; 

//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 daisy = new Pet { Name = "Daisy", Owner = magnus }; 

List<Person> people = new List<Person> { magnus, terry, charlotte }; 
//List<Pet> pets = new List<Pet> { barley, boots, whiskers, daisy }; 
List<Pet> pets = new List<Pet>(); 

// Create a list of Person-Pet pairs where 
// each element is an anonymous type that contains a 
// Pet's name and the name of the Person that owns the Pet. 
var query = 
    people.Join(pets, 
       person => person, 
       pet => pet.Owner, 
       (person, pet) => 
        new { OwnerName = person.Name, Pet = pet.Name }).ToList(); 

ответ

71

Я думаю, если вы хотите использовать методы расширения вы должны использовать GroupJoin

var query = 
    people.GroupJoin(pets, 
        person => person, 
        pet => pet.Owner, 
        (person, petCollection) => 
         new { OwnerName = person.Name, 
           Pet = PetCollection.Select(p => p.Name) 
               .DefaultIfEmpty() } 
        ).ToList(); 

вы, возможно, придется играть с выражением выбора. Я не уверен, что это даст вам желание, которого вы хотите в случае, когда у вас есть отношения «один ко многим».

Я думаю, что это немного проще с помощью синтаксиса LINQ запросов

var query = (from person in context.People 
      join pet in context.Pets on person equals pet.Owner 
      into tempPets 
      from pets in tempPets.DefaultIfEmpty() 
      select new { OwnerName = person.Name, Pet = pets.Name }) 
      .ToList(); 
+1

Wohoo :) Ровно что я искал! – Ekaterina

+0

Я не знал, что вы можете получить .ToList(), поставив скобки вокруг метода запроса, спасибо! – Haroon

+2

Пожалуйста, не вызывайте синтаксис декларативного запроса LINQ «Синтаксис LINQ». Они оба являются «синтаксисом LINQ». Правильное название - «Синтаксис запроса» и «Синтаксис метода». http://msdn.microsoft.com/en-us/library/bb397947.aspx – Pluc

2

Левые соединения в LINQ возможны с помощью метода DefaultIfEmpty(). У меня нет точного синтаксиса для вашего дела, хотя ...

На самом деле я думаю, что если вы просто изменить любимец pets.DefaultIfEmpty() в запросе он может работать ...

EDIT: Я на самом деле не должны отвечать на вещи, когда его поздно ...

15

Вы должны получить соединенные объекты в набор, а затем применить DefaultIfEmpty, как JPunyon сказал:

Person magnus = new Person { Name = "Hedlund, Magnus" }; 
Person terry = new Person { Name = "Adams, Terry" }; 
Person charlotte = new Person { Name = "Weiss, Charlotte" }; 

Pet barley = new Pet { Name = "Barley", Owner = terry }; 
List<Person> people = new List<Person> { magnus, terry, charlotte }; 
List<Pet> pets = new List<Pet>{barley}; 

var results = 
    from person in people 
    join pet in pets on person.Name equals pet.Owner.Name into ownedPets 
    from ownedPet in ownedPets.DefaultIfEmpty(new Pet()) 
    orderby person.Name 
    select new { OwnerName = person.Name, ownedPet.Name }; 


foreach (var item in results) 
{ 
    Console.WriteLine(
     String.Format("{0,-25} has {1}", item.OwnerName, item.Name)); 
} 

Выходы:

Adams, Terry    has Barley 
Hedlund, Magnus   has 
Weiss, Charlotte   has 
+0

Спасибо Gishu - очень полезная информация. – Guy

+0

Я думаю, что вы могли бы заменить 2 строки: «присоединяйтесь к любимому ...» и «из ownPet ...» с одной строкой: 'от домашнего животного в домашних животных. Где (x => person.Name == x.Owner. Name) .DefaultIfEmpty() ' –

3

Вот хорошее сообщение в блоге, которое только что опубликовано Fabrice (автор LINQ в действии) whic h покрывает материал в вопросе, который я задал. Я помещаю его здесь для справки, поскольку читатели вопроса найдут это полезным.

Converting LINQ queries from query syntax to method/operator syntax

5

Я следующее сообщение об ошибке, когда столкнулись с этой же проблемой:

тип одного из выражений в предложении присоединиться к неверен. Ошибка ввода типа в вызове «GroupJoin».

Решение, когда я использовал одно и то же имя свойства, он работал.

(...)

join enderecoST in db.PessoaEnderecos on 
    new 
     { 
     CD_PESSOA   = nf.CD_PESSOA_ST, 
     CD_ENDERECO_PESSOA = nf.CD_ENDERECO_PESSOA_ST 
     } equals 
    new 
    { 
     enderecoST.CD_PESSOA, 
     enderecoST.CD_ENDERECO_PESSOA 
    } into eST 

(...)

+0

+1 для этого. Я сидел, уставившись на свой код в течение 15 минут, пытаясь понять, почему 2 анонимных класса с одинаковыми типами не совпадают. Затем я добавил явные имена свойств ... – drdwilcox

0

Если вы на самом деле есть база данных, это самый простой способ:

var lsPetOwners = (from person in context.People 
        from pets in context.Pets 
         .Where(mypet => mypet.Owner == person.ID) 
         .DefaultIfEmpty() 
        select new { OwnerName = person.Name, Pet = pets.Name } 
        ).ToList();