【问题标题】:Multiple "order by" in LINQLINQ 中的多个“排序依据”
【发布时间】:2010-09-22 20:38:25
【问题描述】:

我有两个表,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”在这种情况下没有意义。
  • .ThenBy 是您要搜索的内容?
  • 如果您想按降序对它们进行排序,here 是您的最佳选择。

标签: linq sql-order-by


【解决方案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”而不是Var movies = _db.Movies.Orderby(c => c.Category).ThenBy(n => n.Name),为什么结果会不同?
  • @devendra,结果不同,因为第二个“OrderBy”对作为第一个“OrderBy”结果的集合起作用并重新排序其项目
  • ThenBy 对 IOrderedEnumerable 进行操作(由 OrderBy 返回)
  • 请注意:.ThenBy() = 升序,.ThenByDescending() = 降序!! (听起来合乎逻辑,对吧?)
  • IQueryable 不包含 ThenBy 的定义
【解决方案2】:

使用非 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;

【讨论】:

  • 在这种语法中没有办法在降序和非降序之间来回翻转吗?
  • 其实你的答案相当于_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:我相信你完全倒退了。您的示例最终将以 row.Name 为主列,row.Category 为辅助列,相当于_db.Movies.Orderby(c => c.Category).OrderBy(n => n.Name)。您提供的两个 sn-ps 彼此等效,而不是 OP。
  • 对 Linq 使用 SQL 语法的唯一缺点是,并非所有函数都受支持,大部分但不是全部
【解决方案3】:

添加“新”:

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

这适用于我的盒子。它确实返回了一些可以用来排序的东西。它返回一个具有两个值的对象。

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

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

【讨论】:

  • 将其用于数字时要小心。
  • 您可以根据需要使用 OrderByDescending 和 ThenBy,或 OrderBy 和 ThenByDescending。
  • 我很确定.OrderBy( m => new { m.CategoryID, m.Name }).OrderBy( m => new { m.Name, m.CategoryID }) 会产生相同的结果,而不是尊重预期的优先级。有时,它似乎会为您提供您想要的订单,这纯粹是出于巧合。此外,如果 CategoryID 是 intm.CategoryID.ToString() + m.Name 将产生不正确的排序。例如,id=123,name=5times 的东西会出现在 id=1234,name=something 之后而不是之前。在可能发生 int 比较的情况下进行字符串比较也不是低效的。
  • 当我尝试对匿名类型进行排序时,我收到 ArgumentException 并显示消息“至少一个对象必须实现 IComparable。”。我看到其他人在这样做时必须声明一个比较器。见stackoverflow.com/questions/10356864/…
  • 这绝对是错误的。无法按没有 ICompariable 实现的新匿名类型进行排序,因为匿名类型的属性没有顺序。它不知道是先按 CategoryID 还是先按 Name 排序,更不用说它们是否按相反的顺序排序了。
【解决方案4】:

在您的 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(名称和CategoryId切换):

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

【讨论】:

    【解决方案5】:

    我创建了一些扩展方法(如下),因此您不必担心 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);
    

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

    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 的设计很糟糕...
    • 您可以轻松扩展此扩展以考虑可空值。参考:stackoverflow.com/a/36507021
    【解决方案6】:

    至少还有一种使用 LINQ 的方法可以做到这一点,虽然不是最简单的。 您可以使用OrberBy() 方法来完成此操作,该方法使用IComparer。首先你需要 像这样为Movie 类实现IComparer

    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 的相应方法。

    【讨论】:

    • 我喜欢这个比 thenby 更通用,因为你可以用比较做一些奇怪的事情,包括准备不同的比较对象和不同的算法。这比我在了解 thenby 之前的首选解决方案要好,后者是创建一个实现 IComparable 接口的类。
    • 自 2012 年(.NET 版本 4.5)以来,您不必自己创建类 MovieComparer;相反,你可以做_db.Movies.OrderBy(item =&gt; item, Comparer&lt;Movie&gt;.Create((x, y) =&gt; { if (x.CategoryId == y.CategoryId) { return x.Name.CompareTo(y.Name); } else { return x.CategoryId.CompareTo(y.CategoryId); } }));。当然,如果你更喜欢将逻辑写成一个表达式,而不是if...else,那么lamda (x, y) =&gt; expr 可以更简单。
    【解决方案7】:

    如果使用通用存储库

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

    其他

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

    【讨论】:

    • 匿名表达式将由实体框架核心在本地解析。无法翻译 LINQ 表达式,将在本地计算。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多