ChatGPT解决这个技术问题 Extra ChatGPT

LINQ 中的多个“排序依据”

我有两个表,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 就是要走的路。

s
syloc

这应该适合你:

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 的定义
S
Scott Stafford

使用非 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)。您提供的两个片段彼此等效,而不是 OP。
对 Linq 使用 SQL 语法的唯一缺点是并非所有函数都受支持,大多数但不是全部
P
Paul Zahra

添新”:

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 比较的情况下进行字符串比较也不是低效的。
当我尝试对匿名类型进行排序时,我收到带有消息“至少一个对象必须实现 IComparable。”的 ArgumentException。我看到其他人在这样做时必须声明一个比较器。请参阅stackoverflow.com/questions/10356864/…
这是绝对错误的。无法按没有 ICompariable 实现的新匿名类型进行排序,因为匿名类型的属性没有顺序。它不知道是先按 CategoryID 还是先按 Name 排序,更不用说它们是否按相反的顺序排序了。
O
Oliver Slay

在您的 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

P
Paul Zahra

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

这个答案是金!我将把对 queryable.IsOrdered() 的检查与这篇文章的答案结合起来,得到一个采用排序方向的单一方法:stackoverflow.com/questions/388708
这种方式 Linq 实现应该放在首位! OrderBy 的设计很糟糕......
您可以轻松扩展此扩展以考虑可空值。参考:stackoverflow.com/a/36507021
p
prudentcoder

使用 LINQ 至少还有一种方法可以做到这一点,尽管不是最简单的。您可以通过使用 IComparerOrberBy() 方法来完成。首先,您需要为 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());

如果您需要将其中一项的排序切换为降序,只需相应地在 MovieComparerCompare() 方法中切换 x 和 y。


我喜欢这个比 thenby 更通用,因为你可以用比较做一些奇怪的事情,包括准备不同的比较对象和不同的算法。这比我在了解 thenby 之前的首选解决方案要好,后者是创建一个实现 IComparable 接口的类。
自 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 可以更简单。
S
Saint-martin Guillaume

如果使用通用存储库

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

别的

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

匿名表达式将由实体框架核心在本地解析。无法翻译 LINQ 表达式,将在本地计算。