LINQ中的多个“排序依据”


1582

我有两个表,moviescategories,然后首先按categoryID然后按Name得到一个有序列表。

电影表具有三列ID:Name和CategoryID。类别表具有两列ID和Name

我尝试了类似以下的方法,但是没有用。

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

这是为什么它不起作用的原因:括号中的lambda表达式应该返回一个可用于订购商品的值:m.CategoryID是一个可用于订购商品的数字。但是“ m.CategoryID,m.Name”在这种情况下没有意义。
chiccodoro

8
。然后是您要搜索的内容?
eka808

如果您有机会想按降序对它们进行排序,那么这里就有一条路。
RBT

Answers:


2831

这应该为您工作:

var movies = _db.Movies.OrderBy(c => c.Category).ThenBy(n => n.Name)

4
当然,感谢您的回答……但是,Var movies = _db.Movies.Orderby(c => c.Category).ThenBy(n => n.Name) 如果不是 我使用 Var movies = _db.Movies.Orderby(c => c.Category).OrderBy(n => n.Name) 2次“ orderBy”,结果为何会有所不同呢?

147
@devendra,结果是不同的,因为第二个“ OrderBy”在集合中起作用,这是第一个“ OrderBy”的结果,并对项目重新排序

68
ThenBy到底怎么这么不知所措?(编辑:好像它是在.NET 4.0中引入的,它说明了它是如何被忽视的。)
Jordan Gray

13
自从添加LINQ以来,就一直存在。此答案是.NET 4.0之前的版本。
内森·W

10
是的,我得出的结论是,基于3.5 的文档页面中的版本下拉列表中没有太仓促的内容;我应该一直在寻找版本信息。感谢您的更正。:)
乔丹·格雷

594

使用非lambda查询语法LINQ,您可以执行以下操作:

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

[编辑以评论注释]要控制排序顺序,请使用关键字ascending(这是默认值,因此并不特别有用)或descending,如下所示:

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

1
在这种语法中,没有办法在降序和非降序之间来回切换吗?
ehdv

1
实际上,您的答案等同于_db.Movies.Orderby(c => c.Category).OrderBy(n => n.Name)。更正确的是from row in _db.Movies orderby row.Category descending orderby row.Name select row
Lodewijk

9
@Lodewijk:我相信你完全倒退了。您的示例最终将以row.Name为主要列,以row.Category为辅助列,这等效于_db.Movies.Orderby(c => c.Category).OrderBy(n => n.Name)。您提供的两个摘要彼此等效,而不是与OP相同。
斯科特·斯塔福德,

6
对Linq使用SQL语法的唯一缺点是,并非所有功能都受支持,但并非全部都受支持
Joshua G

74

添新”:

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

那在我的盒子上起作用。它确实返回可以用于排序的内容。它返回一个带有两个值的对象。

类似于,但与按组合列排序不同,如下所示。

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

21
将其用于数字时要小心。
WoF_Angel 2012年

7
您可以根据需要使用OrderByDescending和ThenBy,或OrderBy和ThenByDescending。
Ali Shah Ahmed

6
我很确定,.OrderBy( m => new { m.CategoryID, m.Name })并且.OrderBy( m => new { m.Name, m.CategoryID })会产生相同的结果,而不是尊重预期的优先级。有时似乎纯粹是出于巧合,给您想要的订购。此外,m.CategoryID.ToString() + m.Name如果CategoryID为,则会产生不正确的排序int。例如,id = 123,name = 5times的东西将出现在id = 1234之后,name = something而不是之前的东西。进行可能会进行int比较的字符串比较也不是没有效率。
AaronLS 2013年

7
当我尝试使用匿名类型进行排序时,出现ArgumentException,消息为“至少一个对象必须实现IComparable。”。我看到其他人在这样做时必须声明一个比较器。参见stackoverflow.com/questions/10356864/…
罗伯特·高兰

4
这是绝对错误的。没有ICompariable实现的新匿名类型的排序无法正常工作,因为匿名类型的属性没有排序。它不知道是首先对CategoryID还是对Name进行排序,更不用说是否要按相反的顺序对其进行排序了。
Triynko

29

在DataContext上使用以下行将DataContext上的SQL活动记录到控制台-然后,您可以确切地看到linq语句从数据库中请求的内容:

_db.Log = Console.Out

以下LINQ语句:

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

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

产生以下SQL:

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

而在Linq中重复执行OrderBy,似乎会反转生成的SQL输出:

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

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

产生以下SQL(名称和类别标识已切换):

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

24

我创建了一些扩展方法(如下),因此您不必担心是否已订购IQueryable。如果要按多个属性排序,请按照以下步骤操作:

// 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);

如果您从要排序的属性列表中动态创建排序,这将特别有用。

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);
        }
    }
}

1
这个答案是金!我将对queryable.IsOrdered()的检查与这篇文章的答案结合起来,以得到采用一种排序方向的单一方法:stackoverflow.com/questions/388708
SwissCoder

1
这样,Linq实施应该放在首位!OrderBy设计不良...
Andrzej Martyna

1
@Marie您显然不了解用例。例如,如何从属性列表动态创建订单?这是唯一的方法。请不要拒绝投票和重新考虑。谢谢。
sjkm

很公平。我仍然看不到好处,但我会投我的票
玛丽

将这项工作nullable datetime
阿吉

16

尽管不是最简单的方法,但至少还有一种使用LINQ的方法。您可以使用使用的OrberBy()方法来实现IComparer。首先,你需要实现IComparerMovie这样的类:

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);
        }
    }
}

然后,您可以使用以下语法订购电影:

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

如果您需要将其中一项的顺序切换为降序,只需在相应Compare() 方法内切换x和y即可MovieComparer


1
我喜欢这种方法,因为您可以通过比较来做一些奇怪的事情,包括使用不同的算法准备好不同的比较对象,这使我比那时更通用。在学习thenby之前,这比我偏爱的解决方案要好,后者是创建一个实现IComparable接口的类。
Gerard ONeill 2015年

1
从2012年(.NET 4.5版)开始,您不必MovieComparer自己创建一个类。相反,你可以做_db.Movies.OrderBy(item => item, Comparer<Movie>.Create((x, y) => { if (x.CategoryId == y.CategoryId) { return x.Name.CompareTo(y.Name); } else { return x.CategoryId.CompareTo(y.CategoryId); } }));。当然,如果您更愿意将逻辑作为一个表达式而不是if... 编写else,则lamda (x, y) => expr会更简单。
Jeppe Stig Nielsen

3

如果使用通用存储库

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

其他

> _db.Module.Where(x=> ......).OrderBy(x => new { x.Level, x.Rank}).ToList();

1
匿名表达式将由实体框架核心进行本地解析。LINQ表达式无法翻译,将在本地进行评估。
Alhpe
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.