views:

107

answers:

6

I would like to get the list of albums (Distinct) which was sung by the artistId=1

I am very new to LINQ to SQL and do not know how to join multiple tables. Please see the database diagram below: alt text

SingBy is the middle table between Track and Artist.

How could I achieve this?

A: 
var albums = from singer in artist
      from sb in singby
      from t in track
      from a in album
    where singer.artistId == 1 && 
      sb.artistId == 1 && 
      sb.trackId == t.trackId && 
      a.albumId == track.albumId
    select a;

I'm sure there must be a better way. You should look into creating Navigation Properties on your entities. Navigation Properties are like foreign keys.

Edit - corrected to get albums, not artists.

Chad
A: 

I would like to get the list of albums (Distinct) which was sung by the artistId=1

DBDataContext = new DBDataContext();
album[] = db.artists.Where(a => a.artistId == 1) /* Your artist */
  .SelectMany(a => a.singbies) /* Check if `singby` converted to `singbies` */
  .Select(sb => sb.track) /* The tracks */
  .Select(t => t.album) /* The albums */
  .GroupBy(al => al.albumId) /* Group by id */ /* "Distinct" for objects */
  .Select(alG => alG.First()) /* Select first of each group */
  .ToArray();
Graphain
+1  A: 

If you have all the foreign key relationship defined, you should be able to issue call like below:

dc.GetTable<Album>().Where(a => a.Track.Singby.ArtistId == 1).ToList();

This is relying on Linq to perform lazy load for Track and Singby automatically when required. Obviously this is not optimal to use when you have a large set of data in the db and performance is critical. You can chain the query with GroupBy or Distinct operation to return only the distinct set such as

dc.GetTable<Album>().Where(a => a.Track.Singby.ArtistId == 1).Distinct().ToList();
Fadrian Sudaman
+1 @TTCG: good answer, but actually the dinstcint won't work
andy
Isn't there usually more than 1 track on an album?
David B
The track and singby are all entityset generated by Linq and they above are just expression and will be translated into SQL statement which will perform the necessary join with condition. So the expression a.Track.Singby isn't assuming that there is only one track and one singby.
Fadrian Sudaman
have you ran it, or the equivalent of it?
andy
No I havent with this. But I have done something similar or equivalent to it
Fadrian Sudaman
I'm pretty sure you can't do a Distinct on an entity, because the SQL equivalent, or better still, the sql that gets generated, will not do the intended Distinct.In SQL you can't do SELECT DISTINCT(*). I mean, you can, but it won't give you what you want.
andy
Thanks for your advices. But 'a.Track' will not work, because Albums is one-to-many relationship to TracksSo, there is only 'a.Tracks'
TTCG
A: 
List<int> Ids = dc.Albums.Where(a => a.Track.Singby.ArtistId == 1).Select(a=> a.albumId).Distinct().ToList();
List<Album> distinctAlbums = dc.Albums.Where(a => distinctAlbumIds.Contains(a.albumId)).ToList();

Hey TTCG, above is the simplest way to do it. This is because doing a Distinct on a List of objects won't do it based on the albumId.

Either you do it in two steps as above, or, you write your own Album Comparer which specifies uniqueness based on AlbumId and pass it to the Distinct call on a List.

NOTE:

The above will only work if you've defined the constraints in your DBML, but better still in your DB.

For best practices, always define your relationships IN THE DATABASE when using Linq to SQL, as Linq to SQL is not like EF, or NHibernate, in that is does not "abstract" your db, it simply reflects it. It's a tool for Data Driven Design, not Domain Driven, so define the relationships in the db.

andy
-1 for craziness. How can one follow such advice?
David B
why? explain, cheers!
andy
(1) How can an Album Comparer you create be sent to a database for evaluation? It would only work locally. (2) The one track per album flaw. (3) Relationship properties from associations are abstractions of the value-based relationships existing (though not necessarily declared) in the database. After designing is through, LinqToSql does not require or notice such constraints in the database. (4) It is also possible to decorate classes with LinqToSql attributes without the designer - Domain Driven.
David B
1. You're totally right, I must have been high when I wrote that. 2. not sure, again, high. 3. I know, that's why I said BEST PRACTICE, and I stand by it. LINQ to SQL should not be used to "abstract" the db in the way that EF and NHibernate mappings do. To do so is to fight the framework. 4. Yes, but the again, you're negating the whole point of Linq To Sql, which is extremely RAD dev, get up and running kinda thing
andy
A: 
IEnumerable<Album> query =
  from album in myDC.Albums
  let artists = 
    from track in album.Tracks
    from singBy in track.SingBys
    select singBy.Artist
  where artists.Any(artist => artist.ArtistId == 1)
  select album;
David B
+1  A: 

Now, I wrote the codes like the following and it works.

var albums = (from a in db.artists
                          where a.artistId == 1
                          join sb in db.singbies on a equals sb.artist
                          join t in db.tracks on sb.track equals t
                          join al in db.albums on t.album equals al
                          select al).Distinct();

return albums.ToList() as List<album>;

I tested the Chad's version and it works too. I would like to know which way is better and good for query optimization? Thanks all.

TTCG