2009-02-08 18 views
41

Uso de la sintaxis de extensión Estoy intentando crear un left-join usando LINQ en dos listas que tengo. Lo siguiente es de la ayuda de Microsoft pero lo he modificado para mostrar que la lista de mascotas no tiene elementos. Lo que estoy terminando es una lista de 0 elementos. Supongo que esto se debe a que se está produciendo una unión interna. Con lo que quiero terminar es con una lista de 3 elementos (los objetos de 3 personas) con datos nulos completados para los elementos faltantes. es decir, un Left-Join. es posible?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(); 

Respuesta

71

Creo que si desea utilizar los métodos de extensión es necesario utilizar el 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(); 

Es posible que tenga que jugar con la expresión de la selección. No estoy seguro de que pueda darle el deseo que desea en el caso en que tenga una relación de uno a varios.

creo que es un poco más fácil con la sintaxis de LINQ consulta

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 :) Exactamente lo que estaba buscando! – Ekaterina

+0

No sabía que pudiera obtener .ToList() poniendo corchetes alrededor del método de consulta, ¡gracias! – Haroon

+2

No llame a la sintaxis de consulta declarativa LINQ "Sintaxis LINQ". Ambos son "sintaxis LINQ". La denominación correcta es "Query Syntax" frente a "Method Syntax". http://msdn.microsoft.com/en-us/library/bb397947.aspx – Pluc

2

izquierda se une en LINQ son posibles con el método DefaultIfEmpty(). No tengo la sintaxis exacta para su caso, sin embargo ...

En realidad creo que si se acaba de cambiar a los animales domésticos pets.DefaultIfEmpty() en la consulta que podría funcionar ...

EDIT: Realmente no debe responder a las cosas cuando es tarde ...

15

Usted necesita obtener los objetos unidas en un conjunto y luego aplicar DefaultIfEmpty como JPunyon dijo:

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

Salidas:

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

Gracias Gishu - información muy útil. – Guy

+0

Creo que podrías reemplazar las 2 líneas: 'join pet ...' y 'from ownedPet ...' con una sola línea: 'from pet in pets.Where (x => person.Name == x.Owner. Nombre) .DefaultIfEmpty() ' –

5

que el mensaje de error cuando se enfrentan el mismo problema:

El tipo de una de las expresiones de la cláusula de combinación es incorrecta. La inferencia de tipo falló en la llamada a 'GroupJoin'.

Resuelto cuando utilicé el mismo nombre de propiedad, funcionó.

(...)

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 para esto. Me quedé mirando mi código durante 15 minutos, tratando de entender por qué 2 clases anónimas con los mismos tipos no eran lo mismo. Luego agregué nombres de propiedad explícitos ... – drdwilcox

0

Si usted tiene realmente una base de datos, esta es la manera más simple:

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