2012-07-19 20 views
22

que tienen los datos que se ve así:¿Cómo usar LINQ para seleccionar un objeto?

UserId | SongId 
-------- -------- 
1   1 
1   4 
1   12 
2   95 

También tengo la clase siguiente:

class SongsForUser 
{ 
    public int User; 
    public List<int> Songs; 
} 

Lo que me gustaría hacer es utilizar LINQ para seleccionar de mis datos para crear una colección de Objetos SongsForUser. A continuación es lo que he encontrado hasta el momento:

var userCombos = songs.UserSongs.Select(x => new SongsForUser() { User = x.UserId, 
                    Songs = /*What goes here?*/ }); 

¿Cómo hago para poblar mi lista de Songs?

El resultado debe ser dos objetos SongsForUser. Para el usuario 1 tendría 3 artículos en la lista Songs. Para el usuario 2 tendría 1 artículo en la lista Songs.

Respuesta

36
songs.UserSongs.GroupBy(x => x.User).Select(g => new SongsForUser() 
{ 
    User = g.Key, 
    Songs = g.Select(s => s.SongId).ToList() 
}); 
+1

Por qué seleccionar la ID de canción solo dentro de la proyección, en lugar de como parte de la agrupación ? –

+1

Esto arroja un error en la parte 'ToList'. Creo que puede ser porque esto es Linq para Entidades. Error: 'LINQ to Entities no reconoce el método 'System.Collections.Generic.List'1 [System.String] ToList [String] (System.Collections.Generic.IEnumerable'1 [System.String])', y este método no se puede traducir a una expresión de tienda. –

+0

@AbeMiessler asegúrese de tener 'using System.Linq' en la parte superior. –

19

te sospecho que desee:

var songsByUser = songs.UserSongs 
         .GroupBy(song => song.UserId, song => song.SongId) 
         .Select(g => new SongsForUser { User = g.Key, 
                 Songs = g.ToList() }); 

de explicar, después de la GroupBy usted tendrá un montón de grupos, donde la clave de cada grupo es el ID de usuario y los valores dentro del grupo son las identificaciones de canciones:

Key = 1, Values = 1, 4, 12 
Key = 2, Value = 95 

Luego estás a convertir ese tipo en su SongsForUser. Tenga en cuenta que no necesita incluir explícitamente el () al llamar al constructor en un inicializador de objetos; está implícito a menos que necesite especificar argumentos de constructor.

Usted podría hacerlo todo en un solo GroupBy llamada, por cierto:

var songsByUser = songs.UserSongs 
     .GroupBy(song => song.UserId, song => song.SongId, 
        (user, ids) => new SongsForUser { User = user, 
                Songs = ids.ToList() }); 

Personalmente lo general encontramos un Select llamada independiente a ser más legible.

También puede hacer todo esto con una expresión de consulta:

var songsByUser = from song in songs.UserSongs 
        group song.SongId by song.UserId into g 
        select new SongsForUser { User = g.Key, Songs = g.ToList() }; 

EDIT: Lo anterior es "proveedor neutral" pero parece que no funciona con LINQ a Entidades. Usted puede ser capaz de conseguir que funcione de esta manera:

var songsByUser = songs.UserSongs 
         .GroupBy(song => song.UserId, song => song.SongId) 
         .AsEnumerable() 
         .Select(g => new SongsForUser { User = g.Key, 
                 Songs = g.ToList() }); 

La llamada AsEnumerable obligará a la agrupación por hacer en la base de datos, pero la proyección final (incluyendo la llamada ToList) para hacer a nivel local. Sin embargo, debería verificar la eficacia del SQL generado.

0

Digamos que tiene lo siguiente:

public class SongsForUser 
{ 
    public int UserId; 
    public List<int> Songs; 
} 

Entonces una función como esta de aquí va a hacer. La lista está allí para tener algunos datos para probar con .

public void Group() 
    { 
     List<Tuple<int, int>> SongRelations = new List<Tuple<int, int>>(); 

     SongRelations.Add(new Tuple<int, int>(1, 1)); 
     SongRelations.Add(new Tuple<int, int>(1, 4)); 
     SongRelations.Add(new Tuple<int, int>(1, 12)); 
     SongRelations.Add(new Tuple<int, int>(2, 95)); 

     var list = SongRelations.GroupBy(s => s.Item1) 
           .Select(r => new SongsForUser() 
           { 
            UserId = r.Key, 
            Songs = r.Select(t => t.Item2).ToList(), 
           }); 
    } 

list contiene 2 elementos de tipo SongsForUser después. Uno con usuario 1 y una lista de canciones que contiene 1, 4 y 12 y uno con usuario 2 y una lista de canciones que contiene 95.

Cuestiones relacionadas