Multiple “order by” in LINQ
我有两张表:movies和categories,我先按categoryID排序,然后按名称排序。
Movie表有三列:ID、名称和CategoryID。类别表2有列、ID和名称。
我试过以下的方法,但没用。
1
| var movies = _db.Movies.OrderBy( m => { m.CategoryID, m.Name }) |
- 这就是为什么这行不通的原因:括号中的lambda表达式应该返回一个可用于排序项的值:m.categoryID是一个可用于排序项的数字。但是在这种情况下,"m.categoryid,m.name"没有意义。
- 包括system.linq;:)
- 看看linq:order by中的SQL查询。它解释了如何将常见的SQL查询转换为LINQ语法。其中一个例子涉及多个列的排序。
- 。那你要搜索的是什么?
- 如果您想按降序对它们进行排序,可以这样做。
这应该对你有用:
1
| 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",为什么结果不同?
- @devendra,结果不同,因为第二个"orderby"处理第一个"orderby"的结果集合,并重新排序其项
- 我到底是怎么一直不知道埃多克斯一〔2〕的?!(编辑:看起来它是在.NET 4.0中引入的,这解释了它是如何在未被注意的情况下从我身边溜走的。)
- 自从添加Linq以来,就一直存在。此答案是.NET 4.0之前的版本。
- 是的,我得出的结论是,基于3.5,过于仓促,没有出现在文档页面的版本下拉列表中;我应该一直向下查找版本信息。谢谢你的纠正。:)
- 如何应用"var movies=_db.movies.orderby(c=>c.category).take(5).thenby(n=>n.name)"之类的内容?
- @Jero2rome,把take移到表达式的末尾:"var movies=_db.movies.orderby(c=>c.category)。thenby(n=>n.name)。take(5)"。
- Thenby操作iorderenumerable(由orderby返回)
- 如何在查询时使用条件thenby。IE:仅当用户选择(多个选择选项)按顺序排序时才使用thenby排序。
- _ db.movies.orderby(c=>new c.category,c.name)-这也应该有效吗?
- Ruchan——你可以存储iorderenumerable,只有在你确定需要它之后才使用then by操作符。估计到时候会返回另一个IORDerenumerable,允许您继续应用Thenbys,如果您认为合适的话。
- 请注意:.ThenBy()为升序,.ThenByDescending()为降序!!(听起来很有逻辑,哈?)
- iQueryable不包含Thenby的定义
- 如果希望数据按降序排列,可以按如下方式使用:Var movies = _db.Movies.OrderByDescending(c => c.Category).ThenByDescending(n => n.Name)。
- @用户545425,因为每个下一个orderby都不知道上一个,并按传递给它的列对所有集合进行排序。Thenby知道以前的结果,sort只将他的排序添加到结果中。
- orderby应该像smartorderby一样实现。多亏了这个,很多人不会掉进这个陷阱。stackoverflow.com/a/44438959/669692
- 很奇怪。通常,排序时,按最重要的属性进行排序,最后一个,而不是第一个。另外(在逻辑语言中),您会认为当属性具有相同的值时,排序不会产生随机结果,您不会期望发生任何事情。然而,在dot-net中,你必须小心使用一个特殊的调用"thenby"。休斯敦大学。查看文档时,不清楚这一点,因此by将保留前一排序的顺序。
使用非lambda查询语法linq,可以执行以下操作:
1 2 3
| var movies = from row in _db.Movies
orderby row.Category, row.Name
select row; |
[编辑地址注释]要控制排序顺序,请使用关键字ascending(这是默认值,因此不是特别有用)或descending,如下所示:
1 2 3
| var movies = from row in _db.Movies
orderby row.Category descending, row.Name
select row; |
- 在这种语法中,没有一种在降序和非降序之间来回切换的方法,是吗?
- 实际上,你的答案相当于_db.Movies.Orderby(c => c.Category).OrderBy(n => n.Name)。更正确的是from row in _db.Movies orderby row.Category descending orderby row.Name select row。
- @罗德维克:我相信你已经完全颠倒了。您的示例将以row.name为主列,row.category为辅,相当于_db.Movies.Orderby(c => c.Category).OrderBy(n => n.Name)。您提供的两个代码片段彼此等效,而不是操作的代码片段。
- 对Linq使用SQL语法的唯一缺点是,并非所有的函数都受支持,大多数函数都不受支持。
添加"新":
1
| var movies = _db.Movies.OrderBy( m => new { m.CategoryID, m.Name }) |
在我的盒子里可以用。它会返回一些可用于排序的内容。它返回具有两个值的对象。
类似,但不同于按组合列排序,如下所示。
1
| var movies = _db.Movies.OrderBy( m => (m.CategoryID.ToString() + m.Name)) |
- 在使用数字时要小心。
- 你的答案很好,因为它在jslinq中有效(我没有"thenby()"),但我还有一个问题。当我想要CategoryID降序和Name升序时,如何使用它?
- 根据需要,可以使用orderbyDescending和ThenBy,或orderby和ThenByDescending。
- @Arvangen,这样做的一个扭曲的方式是使用"orderby(m=>new-m.categoryid,m.name))"之类的东西。我还没有测试过,但它应该做你想做的。
- 我非常肯定,.OrderBy( m => new { m.CategoryID, m.Name })和.OrderBy( m => new { m.Name, m.CategoryID })将产生相同的结果,而不是尊重预期的优先权。有时它会给你一个纯粹是巧合的命令。另外,如果categoryID是int的话,m.CategoryID.ToString() + m.Name将产生错误的订单。例如,id=123、name=5次的内容将出现在id=1234之后,name=something而不是before。在可能发生int比较的情况下进行字符串比较也不是低效的。
- 当我尝试对匿名类型排序时,我得到一个带有消息"至少有一个对象必须实现IComparable"的ArgumentException。我看到其他人在这样做时必须声明比较器。请参阅stackoverflow.com/questions/10356864/…。
- 我的错,这适用于字符串属性;我用int属性进行测试。
- 它不起作用…我有例外。
- OrderBy( m => new { m.CategoryID, m.Name })不起作用,它抛出异常"至少有一个对象必须实现IComparable"。
- 这绝对是错误的。由于匿名类型的属性没有顺序,因此无法按没有IComparable实现的新匿名类型排序。它不知道是先按categoryID还是先按name排序,更不用说按相反的顺序排序了。
- 我刚才提到,如果你不能使用匿名类型,那么它是有效的。
- 匿名类型m => new { m.CategoryID, m.Name }的技术不适用于linq-to-objects,因为正如上面评论中已经提到的,匿名类型既不是IComparable也不是IComparable<>。它可以也可以不与其他LINQ提供程序(LINQ to SQL?)取决于所讨论的"引擎"是否能够将包含匿名类型的表达式树转换为有意义的类型(如有效的SQL查询)。
在DataContext上使用以下行将DataContext上的SQL活动记录到控制台-然后您可以确切地看到LINQ语句从数据库请求什么:
以下LINQ语句:
1 2 3
| var movies = from row in _db.Movies
orderby row.CategoryID, row.Name
select row; |
和
1
| var movies = _db.Movies.OrderBy(m => m.CategoryID).ThenBy(m => m.Name); |
生成以下SQL:
1 2 3
| SELECT [t0].ID, [t0].[Name], [t0].CategoryID
FROM [dbo].[Movies] as [t0]
ORDER BY [t0].CategoryID, [t0].[Name] |
然而,在linq中重复orderby,似乎会反转产生的SQL输出:
1 2 3 4
| var movies = from row in _db.Movies
orderby row.CategoryID
orderby row.Name
select row; |
和
1
| var movies = _db.Movies.OrderBy(m => m.CategoryID).OrderBy(m => m.Name); |
生成以下SQL(名称和CategoryID已切换):
1 2 3
| SELECT [t0].ID, [t0].[Name], [t0].CategoryID
FROM [dbo].[Movies] as [t0]
ORDER BY [t0].[Name], [t0].CategoryID |
我已经创建了一些扩展方法(如下),因此您不必担心iQueryable是否已经被订购。如果要按多个属性排序,请执行以下操作:
1 2 3
| // 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); |
如果您动态地创建排序(例如,从要排序的属性列表中创建排序),这尤其有用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| 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);
}
}
} |
- 这个答案是黄金!我将把queryable.isordered()的检查与本文的答案结合起来,得到一个排序方向的方法:stackoverflow.com/questions/388708
- 这样的LINQ实现应该首先进行!orderby设计得不好…
- 这让我想起我的同事们到处乱开空头支票以防万一。为了防止开发人员不确定他们的数据是否是有序的,屏蔽功能似乎是一种糟糕的实践。
- @玛丽,你显然还没有理解用例。例如,如何从属性列表动态创建排序?这是唯一的办法。请不要投反对票和重新考虑。谢谢您。
- 够公平的。我仍然看不到好处,但我会收回我的选票
使用LINQ至少还有一种方法可以做到这一点,尽管这不是最简单的方法。您可以通过使用IComparer的OrberBy()方法来实现。首先你需要为Movie类实现一个IComparer,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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);
}
}
} |
然后您可以使用以下语法订购电影:
1
| var movies = _db.Movies.OrderBy(item => item, new MovieComparer()); |
如果需要将其中一个项目的顺序切换为降序,只需切换Compare()中的x和y即可。相应的MovieComparer方法。
- 我喜欢这比那时候更普遍,因为你可以做一些奇怪的比较,包括有不同的比较对象和不同的算法准备去。在了解到创建一个实现IComparable接口的类之前,这比我首选的解决方案要好。
- 自2012年以来(.NET版本4.5),您不必自己创建一个类MovieComparer;相反,您可以创建_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); } }));。当然,如果你更喜欢把逻辑写成一个表达式,而不是if…else,那么lamda (x, y) => expr可以更简单。
如果使用通用存储库
1 2
| > lstModule = _ModuleRepository.GetAll().OrderBy(x => new { x.Level,
> x.Rank}).ToList(); |
其他的
1
| > _db.Module.Where(x=> ......).OrderBy(x => new { x.Level, x.Rank}).ToList(); |
- 匿名表达式将由实体框架核心在本地解析。无法转换Linq表达式,将在本地对其进行计算。