LINQ中的多重排序
技术背景
在使用LINQ(Language Integrated Query)进行数据库查询时,经常会遇到需要对查询结果进行多重排序的需求。多重排序可以按照多个字段对结果集进行排序,以满足特定的业务需求。
实现步骤
1. 使用Lambda表达式
1
| var movies = _db.Movies.OrderBy(c => c.Category).ThenBy(n => n.Name);
|
上述代码先按照Category
字段排序,再按照Name
字段排序。
2. 使用查询语法
1 2 3
| var movies = from row in _db.Movies orderby row.Category, row.Name select row;
|
3. 控制排序顺序
可以使用ascending
(默认)或descending
关键字来控制排序顺序。
1 2 3
| var movies = from row in _db.Movies orderby row.Category descending, row.Name select row;
|
4. 使用new
关键字
1
| var movies = _db.Movies.OrderBy( m => new { m.CategoryID, m.Name });
|
5. 按组合列排序
1
| var movies = _db.Movies.OrderBy( m => (m.CategoryID.ToString() + m.Name));
|
6. 记录SQL活动
可以使用以下代码将DataContext
的SQL活动记录到控制台,以便查看LINQ语句向数据库请求的具体内容。
7. 重复OrderBy
的影响
重复使用OrderBy
会反转SQL输出的排序结果。
1 2 3 4 5 6
| 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);
|
8. 使用扩展方法
创建扩展方法可以处理IQueryable
是否已经排序的情况。
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 29 30 31
| 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.SmartOrderBy(i => i.Property1).SmartOrderByDescending(i => i.Property2);
|
9. 使用IComparer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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());
|
10. 使用通用仓储
1
| lstModule = _ModuleRepository.GetAll().OrderBy(x => new { x.Level, x.Rank}).ToList();
|
或者
1
| _db.Module.Where(x => ......).OrderBy(x => new { x.Level, x.Rank}).ToList();
|
核心代码
以下是扩展方法和IComparer
实现的完整代码:
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| 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); } } }
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); } } }
|
最佳实践
- 当需要进行多重排序时,优先使用
OrderBy
和ThenBy
组合,避免重复使用OrderBy
导致排序结果不符合预期。 - 如果需要动态生成排序规则,使用扩展方法可以简化代码逻辑。
- 使用
IComparer
可以实现更复杂的排序逻辑。
常见问题
- 重复使用
OrderBy
导致排序结果异常:重复使用OrderBy
会反转SQL输出的排序结果,应使用OrderBy
和ThenBy
组合。 - 忘记处理
IQueryable
是否已经排序的情况:使用扩展方法可以避免这个问题。