LINQ query with a join on an intersection table

January 8, 2010

I was working on my MVC blog engine when I needed to write a query that would return blog entries that contained a tag name. This query is simple to write in SQL. It’s a query from the blog entry, to the blog entry tag, to the blog tab tables."

I was working on my MVC blog engine when I needed to write a query that would return blog entries that contained a tag name. This query is simple to write in SQL. It’s a query from the blog entry, to the blog entry tag, to the blog tab tables.

##SQL

select 
be.*
from
BlogEntry be
left join BlogEntryCategory bec on bec.BlogEntry_ID = be.BlogEntry_ID
join BlogCategory bc on bc.BlogCategory_ID = bec.BlogCategory_ID
and bc.Category like '%' + @Category + '%'

I quickly discovered that this is not as straight forward as I thought it would be in LINQ. I first stared out trying to write joins but the whole “into” grouping was not cool. I then just used my sql proc but I lost my ability to leverage the IQueryable interface which I wanted to that I could chain other clauses against it later (such as the pagination I intend to implement).

I found a post on stackoverflow that talked about multiple left outer joins. Towards the bottom was a post was an answer from “aikr473” with an example of using Lambda expressions in the joins. This is exactly what I needed (I voted the answer up).

##LINQ

public IQueryable<BlogEntry> FindBlogEntriesByCategory(string category)
{
    return (from entries in db.BlogEntries
            from catlink in db.BlogEntryCategories.Where(e => e.BlogEntry_ID == entries.BlogEntry_ID).DefaultIfEmpty()
            from Category in db.BlogCategories.Where(c => c.BlogCategory_ID == catlink.BlogCategory_ID && c.Category.Contains(category))
            orderby entries.Published descending
            select entries);
}

This allows me to set up the base query the way I want it, which is a result set sorted by the most recent posts. It also allows me to chain on to this statement later so that I can do something like a take or skip in a paging scenario.

To give you an idea of how I’ll use this:

##Controller

//
// GET: /Blog/Listing/3
public ActionResult Listing(string id, int? page)
{
    var entries = repository.FindBlogEntriesByCategory(id);
    var paginatedEntries = new PaginatedList<BlogEntry>(entries, page ?? 0, pageSize);
    return View("Index", paginatedEntries);
}
PaginatedList class (example taken from NerdDinner)

public class PaginatedList<T> : List<T>
{
    public int PageIndex { get; set; }
    public int PageSize { get; set; }
    public int TotalCount { get; set; }
    public int TotalPages { get; set; }
 
    public PaginatedList(IQueryable<T> source, int pageIndex, int pageSize)
    {
        PageIndex = pageIndex;
        PageSize = pageSize;
        TotalCount = source.Count();
        TotalPages = (int)Math.Ceiling(TotalCount / (double)PageSize);
 
        this.AddRange(source.Skip(pageIndex * pageSize).Take(pageSize));
    }
 
    public bool HasPreviousPage
    {
        get
        {
            return (PageIndex > 0);
        }
    }
 
    public bool HasNextPage
    {
        get
        {
            return (PageIndex + 1 < TotalPages);
        }
    }
}

This gives me a lot of flexibility on how I can pull the data out of sql repository, which may change based on the context of the area in my site that a person is visiting.