1664

I have two tables, movies and categories, and I get an ordered list by categoryID first and then by Name.

The movie table has three columns ID, Name and CategoryID. The category table has two columns ID and Name.

I tried something like the following, but it didn't work.

var movies = _db.Movies.OrderBy( m => { m.CategoryID, m.Name })
Sudhir Dehade
  • 99
  • 1
  • 11
Sasha
  • 18,966
  • 8
  • 36
  • 55
  • 2
    Here is why this can't work: The lambda expression in the parentheses is supposed to return a value which can be used to order the items: m.CategoryID is a number which can be used to order the items. But "m.CategoryID, m.Name" doesn't make sense in this context. – chiccodoro Oct 22 '10 at 13:22
  • 11
    .ThenBy is what you're searching? – eka808 Jul 10 '14 at 16:38
  • 2
    If by any chance you want to sort them in descending order there [here](https://stackoverflow.com/a/1635506/465053) is the way to go. – RBT Jul 19 '17 at 12:50

7 Answers7

2950

This should work for you:

var movies = _db.Movies.OrderBy(c => c.Category).ThenBy(n => n.Name)
syloc
  • 4,092
  • 5
  • 31
  • 48
Nathan W
  • 50,657
  • 24
  • 92
  • 142
  • 5
    Thanks for the answer of course... But instead of `Var movies = _db.Movies.Orderby(c => c.Category).ThenBy(n => n.Name)` if I use `Var movies = _db.Movies.Orderby(c => c.Category).OrderBy(n => n.Name)` 2 times "orderBy" why is the result different? –  Jan 20 '11 at 16:44
  • 152
    @devendra, result is different because second "OrderBy" works over the collection which is result of first "OrderBy" and reorder its items –  Jan 20 '11 at 16:45
  • 72
    How on earth have I gone all this time without knowing about `ThenBy`?! (__Edit:__ looks like it was introduced in .NET 4.0, which explains how it slipped past me unnoticed.) – Jordan Gray Nov 21 '13 at 15:05
  • 13
    This has been there since LINQ was added. This answer is pre .NET 4.0. – Nathan W Dec 09 '13 at 12:55
  • 10
    Yes, I concluded that too hastily based on 3.5 not being in the version dropdown in [the documentation page](http://msdn.microsoft.com/en-us/library/bb534743.aspx); I should have looked all the way down for the version information. Thanks for the correction. :) – Jordan Gray Dec 09 '13 at 13:37
  • How can you apply something like, "Var movies = _db.Movies.OrderBy(c => c.Category).Take(5).ThenBy(n => n.Name) " ? – jero2rome Jul 10 '14 at 07:36
  • @jero2rome, Move the Take to the end of the expression: "Var movies = _db.Movies.OrderBy(c => c.Category).ThenBy(n => n.Name).Take(5)" – Tim Jul 15 '14 at 18:08
  • 2
    ThenBy operates on IOrderedEnumerable (which is returned by OrderBy) – Cosmin Aug 28 '14 at 16:43
  • _db.Movies.OrderBy(c => new {c.Category, c.Name}) - this should also work? – hB0 Sep 25 '14 at 08:32
  • Please note: `.ThenBy()` = Ascending, and `.ThenByDescending()` = Descending!! (sounds logical, huh?) – T_D May 24 '16 at 13:17
  • IQueryable does not contain a definition for ThenBy – Christine Jul 20 '16 at 17:53
  • If you want your data to be in descending order, you can use as below: `Var movies = _db.Movies.OrderByDescending(c => c.Category).ThenByDescending(n => n.Name)` – Dhruv Rangunwala May 11 '17 at 18:27
  • OrderBy should be implemented like SmartOrderBy. Thanks to this so many people wouldn't fall into this trap. https://stackoverflow.com/a/44438959/669692 – Andrzej Martyna Mar 22 '18 at 19:49
  • Very strange. Normally when you sort, you sort by the most important attribute last, not first. Also (in a logical language) you'd think a sort would not produce random results when the attributes have the same values, you'd expect nothing to happen. Yet in dot net, you have to be careful and use a special call "ThenBy". Uh. Looking at the documentation, it is not be made clear ThenBy will preserve the order of the previous sort. – Gerry Apr 18 '18 at 21:49
608

Using non-lambda, query-syntax LINQ, you can do this:

var movies = from row in _db.Movies 
             orderby row.Category, row.Name
             select row;

[EDIT to address comment] To control the sort order, use the keywords ascending (which is the default and therefore not particularly useful) or descending, like so:

var movies = from row in _db.Movies 
             orderby row.Category descending, row.Name
             select row;
Scott Stafford
  • 40,202
  • 22
  • 116
  • 163
  • 1
    There's not a way to flip back and forth between descending and non in this syntax is there? – ehdv Jan 21 '11 at 18:32
  • 1
    Actually, your answer is the equivalent to `_db.Movies.Orderby(c => c.Category).OrderBy(n => n.Name)`. More correct is `from row in _db.Movies orderby row.Category descending orderby row.Name select row` – Lodewijk Aug 31 '11 at 08:38
  • 9
    @Lodewijk: I believe you have that exactly backwards. Your example will end up having row.Name being the primary column and row.Category secondary, which is the equivalent of `_db.Movies.Orderby(c => c.Category).OrderBy(n => n.Name)`. The two snippets you provide are equivalent to each other, not to the OP's. – Scott Stafford Oct 20 '11 at 17:59
  • 6
    The only downside to using the SQL syntax for Linq is that not all of the functions are supported, most but not all – Joshua G Jul 19 '15 at 19:09
77

Add "new":

var movies = _db.Movies.OrderBy( m => new { m.CategoryID, m.Name })

That works on my box. It does return something that can be used to sort. It returns an object with two values.

Similar, but different to sorting by a combined column, as follows.

var movies = _db.Movies.OrderBy( m => (m.CategoryID.ToString() + m.Name))
Paul Zahra
  • 8,906
  • 7
  • 49
  • 65
Alex
  • 5,511
  • 1
  • 33
  • 24
  • 22
    Be careful when using that for numbers. – WoF_Angel Sep 06 '12 at 12:15
  • 1
    Your answer is great, because it works in JSLINQ (where I dont have "ThenBy()"), but I have one more problem. How to use this when I want to have CategoryID descending and Name ascending? – Arvangen Dec 20 '12 at 14:16
  • 8
    You can use OrderByDescending and ThenBy, or OrderBy and ThenByDescending, depending upon your need. – Ali Shah Ahmed Mar 21 '13 at 08:08
  • 1
    @Arvangen, a twisted way to do this would be to go with something like "OrderBy(m => new { - m.CategoryID, m.Name })". I haven't tested it out, but it should do what you want – Tipx Apr 15 '13 at 19:30
  • 6
    I'm pretty sure that `.OrderBy( m => new { m.CategoryID, m.Name })` and `.OrderBy( m => new { m.Name, m.CategoryID })` will produce the same results rather than respecting the intended priority. It will sometimes appear to give you the ordering you want purely by coincidence. Additionally `m.CategoryID.ToString() + m.Name` will produce incorrect orderings if CategoryID is an `int`. For example, something with id=123, name=5times will appear after id=1234, name=something instead of before. It's also not inefficient to do string comparisons where int comparisons could occur. – AaronLS May 06 '13 at 16:40
  • 7
    When I try to order by on an anonymous type, I get an ArgumentException with the message "At least one object must implement IComparable.". I see others having to declare a comparer when doing this. See https://stackoverflow.com/questions/10356864/linq-orderby-anonymous-object-with-projection-comparer . – Robert Gowland Jul 31 '14 at 20:06
  • My bad, this works with string properties; I was testing with int properties. – Robert Gowland Aug 01 '14 at 18:24
  • 2
    `OrderBy( m => new { m.CategoryID, m.Name })` doesn't work, it throws exception "At least one object must implement IComparable". – Thariq Nugrohotomo Mar 24 '15 at 09:40
  • 5
    This is absolutely wrong. Ordering by a new anonymous type that has no ICompariable implementation cannot work, because there is not order to the properties of an anonymous type. It wouldn't know whether to sort on CategoryID first or Name first, let alone if they were to be sorted in opposite orders. – Triynko Aug 31 '15 at 19:08
  • I just mentioned the anonymous type does work if you can't use ThenBy. – Alex Sep 01 '15 at 00:17
  • 2
    The technique with the anonymous type, `m => new { m.CategoryID, m.Name }`, __will not work with Linq-to-Objects__ because as others already said in comments above, an anonymous type is neither `IComparable` nor `IComparable<>`. It may and may not work with other LINQ providers (Linq-to-Sql?) depending on whether the "engine" in question has a way of translating the expression tree containing the anonymous type into something meaningful (like a valid SQL query). – Jeppe Stig Nielsen Jul 27 '18 at 07:29
32

use the following line on your DataContext to log the SQL activity on the DataContext to the console - then you can see exactly what your linq statements are requesting from the database:

_db.Log = Console.Out

The following LINQ statements:

var movies = from row in _db.Movies 
             orderby row.CategoryID, row.Name
             select row;

AND

var movies = _db.Movies.OrderBy(m => m.CategoryID).ThenBy(m => m.Name);

produce the following SQL:

SELECT [t0].ID, [t0].[Name], [t0].CategoryID
FROM [dbo].[Movies] as [t0]
ORDER BY [t0].CategoryID, [t0].[Name]

Whereas, repeating an OrderBy in Linq, appears to reverse the resulting SQL output:

var movies = from row in _db.Movies 
             orderby row.CategoryID
             orderby row.Name
             select row;

AND

var movies = _db.Movies.OrderBy(m => m.CategoryID).OrderBy(m => m.Name);

produce the following SQL (Name and CategoryId are switched):

SELECT [t0].ID, [t0].[Name], [t0].CategoryID
FROM [dbo].[Movies] as [t0]
ORDER BY [t0].[Name], [t0].CategoryID
Oliver Slay
  • 775
  • 7
  • 9
28

I have created some extension methods (below) so you don't have to worry if an IQueryable is already ordered or not. If you want to order by multiple properties just do it as follows:

// We do not have to care if the queryable is already sorted or not. 
// The order of the Smart* calls defines the order priority
queryable.SmartOrderBy(i => i.Property1).SmartOrderByDescending(i => i.Property2);

This is especially helpful if you create the ordering dynamically, f.e. from a list of properties to sort.

public static class IQueryableExtension
{
    public static bool IsOrdered<T>(this IQueryable<T> queryable) {
        if(queryable == null) {
            throw new ArgumentNullException("queryable");
        }

        return queryable.Expression.Type == typeof(IOrderedQueryable<T>);
    }

    public static IQueryable<T> SmartOrderBy<T, TKey>(this IQueryable<T> queryable, Expression<Func<T, TKey>> keySelector) {
        if(queryable.IsOrdered()) {
            var orderedQuery = queryable as IOrderedQueryable<T>;
            return orderedQuery.ThenBy(keySelector);
        } else {
            return queryable.OrderBy(keySelector);
        }
    }

    public static IQueryable<T> SmartOrderByDescending<T, TKey>(this IQueryable<T> queryable, Expression<Func<T, TKey>> keySelector) {
        if(queryable.IsOrdered()) {
            var orderedQuery = queryable as IOrderedQueryable<T>;
            return orderedQuery.ThenByDescending(keySelector);
        } else {
            return queryable.OrderByDescending(keySelector);
        }
    }
}
Paul Zahra
  • 8,906
  • 7
  • 49
  • 65
sjkm
  • 3,706
  • 2
  • 22
  • 42
  • 1
    This answer is gold! I'll combine the check for queryable.IsOrdered() with the answer from this post, to have a single method that takes a sort direction: https://stackoverflow.com/questions/388708/ – SwissCoder Mar 14 '18 at 08:56
  • 1
    This way Linq implementation should go in the first place! OrderBy is badly designed... – Andrzej Martyna Mar 22 '18 at 19:46
  • You can easily extend this extension in order to consider nullable values. Refer to: https://stackoverflow.com/a/36507021 – sjkm Jul 27 '19 at 14:56
17

There is at least one more way to do this using LINQ, although not the easiest. You can do it by using the OrberBy() method that uses an IComparer. First you need to implement an IComparer for the Movie class like this:

public class MovieComparer : IComparer<Movie>
{
    public int Compare(Movie x, Movie y)
    {
        if (x.CategoryId == y.CategoryId)
        {
            return x.Name.CompareTo(y.Name);
        }
        else
        {
            return x.CategoryId.CompareTo(y.CategoryId);
        }
    }
}

Then you can order the movies with the following syntax:

var movies = _db.Movies.OrderBy(item => item, new MovieComparer());

If you need to switch the ordering to descending for one of the items just switch the x and y inside the Compare() method of the MovieComparer accordingly.

prudentcoder
  • 351
  • 2
  • 8
  • 1
    I like this as being more general than thenby since you can do weird things with the compare including having different compare objects with different algorithms ready to go. This is better than my preferred solution before learning about thenby which was to create a class that implements the IComparable interface. – Gerard ONeill Sep 29 '15 at 16:29
  • 2
    Since 2012 (.NET version 4.5) you do not have to create a class `MovieComparer` yourself; instead you can do `_db.Movies.OrderBy(item => item, Comparer.Create((x, y) => { if (x.CategoryId == y.CategoryId) { return x.Name.CompareTo(y.Name); } else { return x.CategoryId.CompareTo(y.CategoryId); } }));`. Of course, if you prefer to write the logic as one expression, instead of `if`...`else`, then the lamda `(x, y) => expr` can be simpler. – Jeppe Stig Nielsen Jul 27 '18 at 07:37
4

If use generic repository

> lstModule = _ModuleRepository.GetAll().OrderBy(x => new { x.Level,
> x.Rank}).ToList();

else

> _db.Module.Where(x=> ......).OrderBy(x => new { x.Level, x.Rank}).ToList();
  • 2
    Anonymous expressions will be parsed locally by entity framework core. The LINQ expression could not be translated and will be evaluated locally. – alhpe Mar 26 '19 at 12:25