C# 为什么添加两个.OrderBy(或.OrderByDescending)语句会以相反的顺序应用排序?

C# 为什么添加两个.OrderBy(或.OrderByDescending)语句会以相反的顺序应用排序?,c#,sql-server,linq-to-sql,C#,Sql Server,Linq To Sql,我在今天重构的一些代码中遇到了以下问题 context.Entities.Where(x => x.ForeignKeyId == id) .OrderBy(x => x.FirstSortField) .OrderBy(x => x.SecondSortField); 最初,我去掉了.OrderByx=>x.FirstSortField,认为第一个OrderBy语句将被第二个OrderBy语句替换。在测试之后,我意识到它是通过SecondSortField、

我在今天重构的一些代码中遇到了以下问题

context.Entities.Where(x => x.ForeignKeyId == id)
    .OrderBy(x => x.FirstSortField)
    .OrderBy(x => x.SecondSortField);
最初,我去掉了.OrderByx=>x.FirstSortField,认为第一个OrderBy语句将被第二个OrderBy语句替换。在测试之后,我意识到它是通过SecondSortField、FirstSortField生成SQL as ORDER的

因此,等价物实际上是:

context.Entities.Where(x => x.ForeignKeyId == id)
    .OrderBy(x => x.SecondSortField)
    .ThenBy(x => x.FirstSortField);

有人能解释一下EF6这样做的原因吗?在我看来,将第一个排序字段替换为第二个排序字段更直观。

实际上,它非常简单,完全有意义:

查询的第一部分

context.Entities.Wherex=>x.ForeignKeyId==id 将被翻译成SQL,大致如下

从实体中选择* 通过以下方式添加第一个订单:

.OrderByx=>x.FirstSortField 它会被翻译成这样

从中选择* 从实体中选择* FirstSortField订购 然后将第二个顺序加上

.OrderByx=>x.FirstSortField .OrderByx=>x.SecondSortField 将翻译为:

从中选择* 从中选择* 从实体中选择* FirstSortField订购 第二索特菲尔德订购 实体框架足够聪明,可以简化为

从实体中选择* 按SecondSortField、FirstSortField订购
事实上,这很简单,完全有道理:

查询的第一部分

context.Entities.Wherex=>x.ForeignKeyId==id 将被翻译成SQL,大致如下

从实体中选择* 通过以下方式添加第一个订单:

.OrderByx=>x.FirstSortField 它会被翻译成这样

从中选择* 从实体中选择* FirstSortField订购 然后将第二个顺序加上

.OrderByx=>x.FirstSortField .OrderByx=>x.SecondSortField 将翻译为:

从中选择* 从中选择* 从实体中选择* FirstSortField订购 第二索特菲尔德订购 实体框架足够聪明,可以简化为

从实体中选择* 按SecondSortField、FirstSortField订购
这都是本地数据,但EF希望在构建表达式树和编写查询时实现逻辑等效

你应该研究这个概念。当使用稳定的排序算法时,将保留相等项的原始顺序

假设您有这样的数据,带有明显的名字/姓氏字段:

Brad Jones Tom Smith Sam Jones Jim Doe James Smith Ryan Smith 如果您最初仅按名字订购,则会得到以下结果:

Brad Jones James Smith Jim Doe Ryan Smith Sam Jones Tom Smith 如果您现在使用这个排序列表,并再次按姓氏排序,您将得到一个按两个字段排序的结果,其中后面的排序优先于前面的排序。。。但只有在排序稳定的情况下,才能保证准确的顺序:

这就给我们带来了一个问题,即.Net使用什么算法,以及它是否稳定,我们可以在备注部分找到:

此方法执行稳定排序

这里没有记录具体的算法。我认为这是一个很好的解决方案,但将其从文档中删除可能是有意的,以便在发现更好的解决方案时,让维护人员更新以获得满足稳定性要求的最佳可用选项


但是,这同样适用于本地数据。数据库将执行SQL告诉它们的操作。

这都是本地数据,但EF希望在构建表达式树和编写查询时执行逻辑等效操作

你应该研究这个概念。当使用稳定的排序算法时,将保留相等项的原始顺序

假设您有这样的数据,带有明显的名字/姓氏字段:

Brad Jones Tom Smith Sam Jones Jim Doe James Smith Ryan Smith 如果您最初仅按名字订购,则会得到以下结果:

Brad Jones James Smith Jim Doe Ryan Smith Sam Jones Tom Smith 如果您现在使用这个排序列表,并再次按姓氏排序,您将得到一个按两个字段排序的结果,其中后面的排序优先于前面的排序。。。但只有在排序稳定的情况下,才能保证准确的顺序:

这就给我们带来了一个问题,即.Net使用什么算法,以及它是否稳定,我们可以在备注部分找到:

此方法执行稳定排序

这里没有记录具体的算法。我认为这是一个很好的解决方案,但将其从文档中删除可能是有意的,以便在发现更好的解决方案时,让维护人员更新以获得满足稳定性要求的最佳可用选项


但是,这同样适用于本地数据。数据库将执行SQL告诉它们的操作。

我只能得出结论,我们实际上是在研究LINQ到SQL。在林帕德直到v。5,很容易犯这个错误,因为在创建新连接时很容易忽略EF6 DbContext驱动程序的选择。在Linqpad v6中,这种选择更为明显

我已经在EF6、EFCore3和5以及LINQtoSQL中测试了报告的行为。只有在后一种情况下,我才能看到gener 在ORDER BY中有两列的SQL语句

声明

Products.OrderByp=>p.Description.OrderByp=>p.LastSale …由LINQ转换为SQL:

选择[t0].[ID]、[t0].[Description]、[t0].[Discontracted]、[t0].[LastSale] 从[产品]到[t0] [t0].[LastSale],[t0].[Description]订购 推理在中进行了解释,可以归结为:LastSale是主要的订购字段,因为它在某种程度上覆盖了第一个OrderBy

所有EF查询都只有ORDER BY LastSale

我必须说,我同意EF的实施。如上所述,两个连续排序的结果取决于排序算法。这意味着我们可以肯定的是,任何LINQ查询的结果都将由LastSale订购,而LastSale组中的订购并不确定。然后,在我看来,SQL翻译更好的选择是将第二条OrderBy语句作为第一条语句的完全重写来处理,这样就可以看出,没有任何期望可以基于第一条语句。对我来说,这更直观


这条消息是:按多个字段排序时要显式。使用OrderBy-ThenBy。不要依赖数据库提供程序处理连续的OrderBy语句。

我只能得出结论,我们实际上是在看LINQ到SQL。在林帕德直到v。5,很容易犯这个错误,因为在创建新连接时很容易忽略EF6 DbContext驱动程序的选择。在Linqpad v6中,这种选择更为明显

我已经在EF6、EFCore3和5以及LINQtoSQL中测试了报告的行为。只有在后一种情况下,我才能看到一个生成的SQL语句,该语句的ORDERBY中有两列

声明

Products.OrderByp=>p.Description.OrderByp=>p.LastSale …由LINQ转换为SQL:

选择[t0].[ID]、[t0].[Description]、[t0].[Discontracted]、[t0].[LastSale] 从[产品]到[t0] [t0].[LastSale],[t0].[Description]订购 推理在中进行了解释,可以归结为:LastSale是主要的订购字段,因为它在某种程度上覆盖了第一个OrderBy

所有EF查询都只有ORDER BY LastSale

我必须说,我同意EF的实施。如上所述,两个连续排序的结果取决于排序算法。这意味着我们可以肯定的是,任何LINQ查询的结果都将由LastSale订购,而LastSale组中的订购并不确定。然后,在我看来,SQL翻译更好的选择是将第二条OrderBy语句作为第一条语句的完全重写来处理,这样就可以看出,没有任何期望可以基于第一条语句。对我来说,这更直观


这条消息是:按多个字段排序时要显式。使用OrderBy-ThenBy。不要依赖数据库提供程序处理连续的OrderBy语句。

有点太聪明了,因为语义上应该只保留OrderBy SECONSORTFIELD。子查询在没有TOPNo的情况下基本上是无序的,实体框架足够聪明,只需舍弃第一个OrderBy即可。请在发帖前检查这些内容。@GertArnold,你应该检查一下自己。EF用于覆盖orderbys,但目前由它组成。如果你不相信,你自己试试看,用你的眼睛看看。好吧,也许我疯了,但我看到两个链接的OrderBy在SQL中被翻译成一个OrderBy。问题涉及的EF6和EF core 3。@GertArnold您确定每个订单都使用不同的属性吗?我用EF6打开了LinqPad,它在.Net Core 3、3.1和5.0中都翻译了order by。这有点太聪明了,因为语义上应该只保留order by SecondSortField。子查询在没有TOPNo的情况下基本上是无序的,实体框架足够聪明,只需舍弃第一个OrderBy即可。请在发帖前检查这些内容。@GertArnold,你应该检查一下自己。EF用于覆盖orderbys,但目前由它组成。如果你不相信,你自己试试看,用你的眼睛看看。好吧,也许我疯了,但我看到两个链接的OrderBy在SQL中被翻译成一个OrderBy。问题涉及的EF6和EF core 3。@GertArnold您确定每个订单都使用不同的属性吗?我用EF6打开了LinqPad,它在.Net Core 3、3.1和5.0中转换了OrderBy。第一个OrderBy被第二个OrderBy替换。你的等价物不是等价物。@GertArnold,这是我最初认为它会做的,直到我开始测试。例如,如果您在LinqPad中尝试此操作,它为这两条语句生成的SQL是等效的。@JoelCoehoorn它不是LinqTo对象。查询非常简单地转换为一条ORDERBY语句。看到它就发生在我面前。@WyattEarp由于您在显示时似乎实际看到了生成的订单,这让我想知道这里的定义因素是什么。您也使用SQL server吗?您基于此的实际查询是否在某些看起来不重要的方面有所不同?真的,不是我不相信你
,正是这种差异让我着迷。[t0]是LINQ到SQL的前缀。EF生成其他前缀。第一个OrderBy被第二个替换。你的等价物不是等价物。@GertArnold,这是我最初认为它会做的,直到我开始测试。例如,如果您在LinqPad中尝试此操作,它为这两条语句生成的SQL是等效的。@JoelCoehoorn它不是LinqTo对象。查询非常简单地转换为一条ORDERBY语句。看到它就发生在我面前。@WyattEarp由于您在显示时似乎实际看到了生成的订单,这让我想知道这里的定义因素是什么。您也使用SQL server吗?您基于此的实际查询是否在某些看起来不重要的方面有所不同?真的,不是我不相信你,而是这种差异让我着迷。[t0]是LINQ到SQL的前缀。EF生成其他前缀。文档还声明:由于IOrderedEnumerable继承自IEnumerable,因此可以根据调用OrderBy、OrderByDescending、ThenBy或ThenByDescending的结果调用OrderBy或OrderByDescending。这样做会引入一个新的主排序,它会忽略以前建立的排序。stable属性确实保留了第二个排序中相等项的顺序,因此看起来排序与OrderBy调用相反的结果取决于要排序的数据的内容。@CodeMaster该语句有误导性。在我的例子中,在第一类之后,布拉德·琼斯排在吉姆·多伊之前。我相信他们想说的是,第二次调用OrderBy可以覆盖这一点,我们在第二个示例中看到,Brad Jones紧随其后。它忽略了顺序,因为它没有特别注意,但是因为顺序在IOrderedEnumerable中,所以可以得到可预测的结果。如果这是linq到实体,那么使用.ThenBy可能有其道理。但对于EF,这将被转换为SQL并执行OP想要的操作。文档还声明:由于IOrderedEnumerable继承自IEnumerable,因此您可以根据调用OrderBy、OrderByDescending、ThenBy或ThenByDescending的结果调用OrderBy或OrderByDescending。这样做会引入一个新的主排序,它会忽略以前建立的排序。stable属性确实保留了第二个排序中相等项的顺序,因此看起来排序与OrderBy调用相反的结果取决于要排序的数据的内容。@CodeMaster该语句有误导性。在我的例子中,在第一类之后,布拉德·琼斯排在吉姆·多伊之前。我相信他们想说的是,第二次调用OrderBy可以覆盖这一点,我们在第二个示例中看到,Brad Jones紧随其后。它忽略了顺序,因为它没有特别注意,但是因为顺序在IOrderedEnumerable中,所以可以得到可预测的结果。如果这是linq到实体,那么使用.ThenBy可能有其道理。但对于EF来说,这将被转换成SQL并执行OP想要的操作。由于延迟执行的性质,我也同意EF的实现。我看到,如果我在LinqPad中使用EF6更改为自定义类型的datacontext,我会看到预期的行为。谢谢你帮我理解!由于延期执行的性质,我也同意EF的实施。我看到,如果我在LinqPad中使用EF6更改为自定义类型的datacontext,我会看到预期的行为。谢谢你帮我理解!