sintaxis inner framework consultas c# .net linq linq-to-entities

c# - inner - linq to entities join



¿Cómo usar LINQ para seleccionar un objeto? (3)

Digamos que tienes lo siguiente:

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

Entonces una función como esta aquí servirá. La lista es solo para tener algunos datos para probar.

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 el usuario 1 y una lista de canciones que contienen 1, 4 y 12 y otro con el usuario 2 y una lista de canciones que contienen 95.

Tengo datos que se ven así:

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

También tengo la siguiente clase:

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

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

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

¿Cómo haría para poblar mi lista de Songs ?

Así que el resultado debe ser dos objetos SongsForUser. Para el usuario 1 tendría 3 elementos en la lista de Songs . Para el usuario 2 tendría 1 elemento en la lista de Songs .


Sospecho que quieres:

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

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

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

Entonces solo estás convirtiendo eso en tu tipo SongsForUser . Tenga en cuenta que no necesita incluir explícitamente el () al llamar al constructor en un inicializador de objetos, es implícito a menos que necesite especificar los argumentos del constructor.

Podrías hacer todo esto en una llamada GroupBy , por cierto:

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

Personalmente, generalmente encuentro que una llamada Select separada es más legible.

También puedes 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() };

EDITAR: Lo anterior es "proveedor neutral" pero parece que no está funcionando con LINQ para Entidades. Es posible que puedas hacer que funcione así:

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 que la agrupación se realice en la base de datos, pero la proyección final (incluida la llamada ToList ) se realizará localmente. Sin embargo, debe comprobar la eficacia del SQL generado.


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