LINQ Inner-Join против левого соединения - PullRequest
41 голосов
/ 08 февраля 2009

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

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

Ответы [ 6 ]

75 голосов
/ 08 февраля 2009

Я думаю, что если вы хотите использовать методы расширения, вам нужно использовать 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 Query это немного проще

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();
15 голосов
/ 08 февраля 2009

Вам нужно собрать объединенные объекты в набор, а затем применить 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
5 голосов
/ 02 марта 2009

У меня следующее сообщение об ошибке при столкновении с такой же проблемой:

Неверно указан тип одного из выражений в предложении соединения. Ошибка вывода типа при вызове '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

(...)

3 голосов
/ 10 февраля 2009

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

Преобразование запросов LINQ из синтаксиса запроса в синтаксис метода / оператора

2 голосов
/ 08 февраля 2009

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

На самом деле, я думаю, что если вы просто измените домашних животных на домашних.

РЕДАКТИРОВАТЬ: Я действительно не должен отвечать на вещи, когда уже поздно ...

0 голосов
/ 18 августа 2015

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

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();
...