Mehmet Ataş
Mehmet Ataş

Reputation: 11559

Linq to Entity Join and Group By

I am new to Linq to Entity and here is my test scenario:

Db Tables

I want to write a method which displays Count and Name of Tags used in Albums of a particular User using Linq.

Here is the method that I wrote and it works fine

    public static void TestStatistics(int userId)
    {
        // select AlbumTitle, TagName, TagCount where UserId = userId
        var results = from photo in dbSet.PhotoSet
                      join album in dbSet.AlbumSet on photo.AlbumId equals album.Id into albumSet
                      from alb in albumSet
                      where alb.UserId == userId
                      join photoTag in dbSet.PhotoTagSet on photo.Id equals photoTag.PhotoId into photoTagSet
                      from pt in photoTagSet
                      join tag in dbSet.TagSet on pt.TagId equals tag.Id
                      group new { alb, tag } by new { alb.Title, tag.Name }
                          into resultSet
                          orderby resultSet.Key.Name
                          select new
                          {
                              AlbumTitle = resultSet.Key.Title,
                              TagName = resultSet.Key.Name,
                              TagCount = resultSet.Count()
                          };

        foreach (var item in results)
        {
            Console.WriteLine(item.AlbumTitle + "\t" + item.TagName + "\t" + item.TagCount);
        }
    }

And this is the standart T-SQL query which does the same

SELECT  a.Title AS AlbumTitle, t.Name AS TagName , COUNT(t.Name) AS TagCount
FROM    TblPhoto p, TblAlbum a, TblTag t, TblPhotoTag pt
WHERE   p.Id = pt.PhotoId AND t.Id = pt.TagId AND p.AlbumId = a.Id AND a.UserId = 1
GROUP BY a.Title, t.Name 
ORDER BY t.Name

It is pretty obvious that standard T-SQL query is much simpler than the Linq query. I know Linq does not supposed to be simpler than T-SQL but this complexity difference makes me think that I am doing something terribly wrong. Besides the SQL query generated by Linq is extremly complex.

Is there any way to make the Linq query simpler?

UPDATE 1:

I made it a little simpler without using joins but using a approach like used in T-SQL. Actually it is now as simple as T-SQL. Still no navigation properties and no relations on db.

var results = from photo in dbSet.PhotoSet
              from album in dbSet.AlbumSet
              from photoTag in dbSet.PhotoTagSet
              from tag in dbSet.TagSet
              where photo.AlbumId == album.Id && photo.Id == photoTag.PhotoId &&
                    tag.Id == photoTag.TagId && album.UserId == userId
              group new { album, tag } by new { album.Title, tag.Name } into resultSet
              orderby resultSet.Key.Name
              select new {
                  AlbumTitle = resultSet.Key.Title,
                  TagName = resultSet.Key.Name,
                  TagCount = resultSet.Count()
              };

Upvotes: 7

Views: 13910

Answers (2)

sgmoore
sgmoore

Reputation: 16077

If every photo has at least one tag , then try

var results = (from r in PhotoTag 
    where r.Photo.Album.UserID == userId
    group r by new { r.Photo.Album.Title, r.Tag.Name } into resultsSet
    orderby resultsSet.Key.Name 
    select new 
    { 
        AlbumTitle = resultsSet.Key.Title , 
        TagName    = resultsSet.Key.Name , 
        TagCount   = resultsSet.Count() 
    }
    );

Upvotes: 6

BLoB
BLoB

Reputation: 9725

First things first, you need to setup foreignkeys in your database then rebuild EF and it will 'know' (i.e. navigation properties) about the relationships, which then allows you to omit all of your joins and use something along the lines of the following:

List<AlbumTag> query = (from ps in dbSet.PhotoSet
                         where ps.Album.UserId = userId
                         group new { album, tag } by new { ps.Album.Title, ps.PhotoTag.Tag.Name } into resultSet
                         orderby resultSet.Key.Name
                         select new AlbumTag()
                         {
                           AlbumTitle = resultSet.Key.Title,
                           TagName = resultSet.Key.Name,
                           TagCount = resultSet.Count()
                         }).ToList();

Upvotes: 6

Related Questions