Skip Navigation LinksHome > View Post

Sorting associations in the Entity Framework

In Projection blows includes in Entity Framework we looked at using the Include method to pull associated types and how this is 'busted' by projection. It turns out this was fortunate finding because it led me to solve another issue I had with EF: there's no way to sort associated types pulled in via the Include method. For example:

from p in dataContext.Posts.Include("Comments")
select p;

What if I want the related Comments sorted in a particular order? There's no way to do this with EF v1.0 but I realised the solution presented in the previous post might provide an answer. Maybe I could sort in the sub-select:

var postsWithCount = from p in dataContext.Posts
    select new PostData {
        Post = p,
        Comments = p.Comments.OrderBy(c => c.PostedDate), // order in the sub-select
        // snipped for brevity...

Sadly though, this didn't work. It seems the only way is to sort in memory. If I'm loading the types in one query it seems reasonable to assume that there isn't a billion entries so this is an acceptable price to pay. And the Rebuild method (again, discussed in previous post) provides the ultimate opportunity:

private void Rebuild(PostData postData)
{
    Post post = postData.Post;
    post.User = postData.User;
    post.Comments.Attach(postData.Comments.OrderBy(c => c.PostedDate));
}

Not perfect, but acceptable.

 
Josh Post By Josh Twist
2:31 AM
12 Feb 2009

» Next Post: Using .Select() instead of foreach
« Previous Post: Projection blows includes in Entity Framework

Comments are closed for this post.

Posted by Alex James @ 12 Feb 2009 6:16 PM
Try something like this:

class Program
{
static void Main(string[] args)
{
using (BloggingEntities ctx = new BloggingEntities())
{
var results = from person in ctx.People
select new {Person = person,Blogs = person.Blogs.OrderByDescending(b=>b.ID)};

foreach (var result in results)
{
Func<Blogs,Blogs,Blogs[]> zipper = (Blogs first, Blogs second) => new Blogs[]{first,second};

Console.WriteLine("Name = {0}", result.Person.Firstname);
foreach (var zip in zipper.Zip(result.Blogs, result.Person.Blogs))
{
if (zip[0] != zip[1]) throw new Exception("Both sequences should be in the same order");
Console.WriteLine("\t{0}\t{1}", zip[0].Name, zip[1].Name);
}
}
}
}
}


public static class Utility
{
public static IEnumerable<R> Zip<T, V, R>(this Func<T, V, R> func, IEnumerable<T> sequence1, IEnumerable<V> sequence2)
{
IEnumerator<T> enumerator1 = sequence1.GetEnumerator();
IEnumerator<V> enumerator2 = sequence2.GetEnumerator();

while (enumerator1.MoveNext() && enumerator2.MoveNext())
{
yield return func(enumerator1.Current, enumerator2.Current);
}
enumerator1.Dispose();
enumerator2.Dispose();
}
}

This seems to work. I don't think it is guaranteed anywhere, but it seems as if as a sideeffect of how things are implemented it looks like when sorting "result.Blogs" it also sorts "result.Person.Blogs"

Which means of course your person.Blogs is sorted, and you can discard result.Blogs

As I said use at your own peril though.
Alex

© 2005 - 2014 Josh Twist - All Rights Reserved.