2009-02-08 8 views
41

Mit Extension-Syntax versuche ich eine linke Verknüpfung mit LINQ auf zwei Listen, die ich habe. Das folgende ist von der Microsoft-Hilfe, aber ich habe es geändert, um zu zeigen, dass die Haustierliste keine Elemente hat. Was ich am Ende bin, ist eine Liste von 0 Elementen. Ich nehme an, dass dies daran liegt, dass ein Inner-Join stattfindet. Was ich möchte, ist eine Liste von 3 Elementen (die 3 Personen-Objekte) mit Null-Daten für die fehlenden Elemente ausgefüllt. d.h. eine Linke Verbindung. Ist das möglich?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(); 

Antwort

71

ich denke, wenn Sie Erweiterungsmethoden verwenden möchten, müssen Sie die 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(); 

Sie müssen verwenden können mit der Auswahl Ausdruck rumspielen. Ich bin mir nicht sicher, ob es dir Wünsche geben würde, wenn du eine 1: n-Beziehung hast.

Ich denke, es ist ein wenig einfacher mit der LINQ-Abfragesyntax

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 :) Genau wonach ich gesucht habe! – Ekaterina

+0

Ich wusste nicht, dass Sie .ToList() erhalten können, indem Sie Klammern um die Abfrage-Methode setzen, danke! – Haroon

+2

Bitte rufen Sie nicht die LINQ-deklarative Abfragesyntax "LINQ-Syntax" auf. Sie sind beide "LINQ-Syntax". Die richtige Bezeichnung lautet "Abfragesyntax" vs "Method Syntax". http://msdn.microsoft.com/en-us/library/bb397947.aspx – Pluc

2

Linke Verknüpfungen in LINQ sind mit der DefaultIfEmpty() -Methode möglich. Ich habe nicht die genaue Syntax für Ihren Fall aber ...

Eigentlich denke ich, wenn man nur Haustiere pets.DefaultIfEmpty() in der Abfrage ändert es funktionieren könnte ...

EDIT: ich wirklich Antwort sollte nicht Dinge, wenn sein Ende ...

15

Sie müssen die verbundenen Objekte in eine Reihe bekommen und dann gelten DefaultIfEmpty wie JPunyon sagte:

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

Ausgänge:

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

Danke Gishu - sehr nützliche Info. – Guy

+0

Ich denke, du könntest die 2 Zeilen ersetzen: 'join pet ...' und 'from ownedPet ...' mit der einzelnen Zeile: 'from pet in pets.Where (x => person.Name == x.Owner. Name) .DefaultIfEmpty() ' –

5

ich die folgende Fehlermeldung, wenn das gleiche Problem konfrontiert:

Die Art einer der Ausdrücke in der Join-Klausel ist falsch. Typinferenz fehlgeschlagen beim Aufruf von 'GroupJoin'.

Gelöst, wenn ich den gleichen Namen verwendete, funktionierte es.

(...)

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 für diese. Ich starrte 15 Minuten lang auf meinen Code und versuchte zu verstehen, warum zwei anonyme Klassen mit denselben Typen nicht gleich waren. Dann habe ich explizite Eigenschaftsnamen hinzugefügt ... – drdwilcox

0

Wenn Sie tatsächlich eine Datenbank haben, ist dies die am meisten einfache Weise:

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