LINQ Inner-Join против Left-Join
Использование синтаксиса расширения. Я пытаюсь создать соединение слева с использованием LINQ в двух списках, которые у меня есть. Ниже приведена справка Microsoft, но я изменил ее, чтобы показать, что список домашних животных не имеет элементов. То, что я получаю, - это список из 0 элементов. Я предполагаю, что это происходит потому, что происходит внутреннее соединение. То, что я хочу закончить, - это список из 3 элементов (объекты 3 человека) с нулевыми данными, заполненными для отсутствующих элементов. т.е. левое соединение. Возможно ли это?
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 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();
Ответы
Ответ 1
Я думаю, что если вы хотите использовать методы расширения, вам нужно использовать 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();
Ответ 2
Вам нужно собрать объединенные объекты в набор, а затем применить 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
Ответ 3
При возникновении этой же проблемы возникает следующее сообщение об ошибке:
Тип одного из выражений в предложении соединения неверен. Ошибка ввода типа в вызове "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
(...)
Ответ 4
Здесь хорошая запись в блоге, которую только что опубликовал Фабрицио (автор LINQ в действии), который охватывает материал в вопросе, который я задал. Я ставил его здесь для справки, так как читатели вопроса найдут это полезным.
Преобразование запросов LINQ из синтаксиса запроса в синтаксис метода/оператора
Ответ 5
Левые соединения в LINQ возможны с помощью метода DefaultIfEmpty(). У меня нет точного синтаксиса для вашего случая, хотя...
На самом деле я думаю, что если вы просто смените домашних животных на pets.DefaultIfEmpty() в запросе, это может сработать...
EDIT: Я действительно не должен отвечать на вопросы, когда он опаздывает...
Ответ 6
Если у вас есть база данных, это самый простой способ:
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();