42 votes

LINQ Inner-Join contre Left-Join

En utilisant la syntaxe d'extension, j'essaie de créer une jointure à gauche en utilisant LINQ sur deux listes que je possède. L'exemple suivant est tiré de l'aide de Microsoft, mais je l'ai modifié pour montrer que la liste des animaux n'a pas d'éléments. Ce que j'obtiens est une liste de 0 élément. Je suppose que c'est parce qu'une jointure interne a lieu. Ce que je veux obtenir, c'est une liste de 3 éléments (les 3 objets Person) avec des données nulles pour les éléments manquants, c'est-à-dire une jonction à gauche. Est-ce possible ?

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

75voto

tvanfosson Points 268301

Je pense que si vous voulez utiliser des méthodes d'extension, vous devez utiliser la fonction 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();

Vous devrez peut-être jouer avec l'expression de sélection. Je ne suis pas sûr qu'elle vous donne ce que vous voulez dans le cas où vous avez une relation 1 à plusieurs.

Je pense que c'est un peu plus facile avec la syntaxe 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();

17voto

Gishu Points 59012

Vous devez rassembler les objets joints dans un ensemble et ensuite appliquer DefaultIfEmpty comme JPunyon l'a dit :

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

Sorties :

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

5voto

João Vieira Points 822

J'ai obtenu le message d'erreur suivant lorsque j'ai été confronté à ce même problème :

Le type de l'une des expressions de la clause de jointure est incorrect. L'inférence de type a échoué dans l'appel à 'GroupJoin'.

Résolu lorsque j'ai utilisé le même nom de propriété, cela a fonctionné.

(...)

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

(...)

3voto

Guy Points 16718

Voici un bon article de blog qui vient d'être publié par Fabrice (auteur de LINQ in Action) et qui couvre le sujet de la question que j'ai posée. Je le mets ici à titre de référence car les lecteurs de la question le trouveront utile.

Conversion des requêtes LINQ de la syntaxe des requêtes à la syntaxe des méthodes/opérateurs

2voto

Jason Punyon Points 21244

Les jointures à gauche dans LINQ sont possibles avec la méthode DefaultIfEmpty(). Je n'ai pas la syntaxe exacte pour votre cas cependant...

En fait, je pense que si vous remplacez pets par pets.DefaultIfEmpty() dans la requête, cela pourrait fonctionner...

EDIT : Je ne devrais vraiment pas répondre aux questions quand il est tard...

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X