EntityFramework 7 OrderBy Skip Take-计算排序分页 SQL 翻译

时间:2022-08-13 15:26:56

先解释一下这个标题的意思,OrderBy 在 Linq 语句中,我们经常使用,比如 OrderBy(b => b.BlogId) 就是对 BlogId 字段进行升序排序,这是针对一个字段的排序,如果多个字段排序,我们可以使用 ThenBy,或者直接在 OrderBy 中对多个字段进行逗号分割,但有一种场景是,我们要对 OrderBy 增加计算功能,什么意思呢?看一段 SQL 代码:

SELECT [b].[BlogCateId], [b].[BlogId], [b].[Url]
FROM [Blog] AS [b]
ORDER BY ([b].[BlogId] * 2 + [b].[BlogCateId])
OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY

这篇博文的主题,其实就是如何用 Linq 翻译这段 SQL 代码,上面这段代码在 SQL Server 中执行没有任何问题,有人说了,很简单啊,比如翻译后的一段代码:

[Fact]
public void ContextLoad_Test()
{
using (var context = new BloggingContext())
{
var query = from b in context.Blogs
orderby b.BlogId * 2 + b.BlogCateId
select b;
var result = query.Skip(0).Take(100).ToList();
}
}

没错,最“直白”的翻译就是这样的,但测试运行后会抛出异常:

EntityFramework 7 OrderBy Skip Take-计算排序分页 SQL 翻译

异常信息:A query containing the Skip operator must include at least one OrderBy operation.

这段异常信息大概是说使用 Skip 包含至少一个 OrderBy,也就是说我们上面使用 orderby b.BlogId * 2 + b.BlogCateId + 34,这段代码并没有起到什么效果,或者说 EF 没有识别出来,总的来说我们这些翻译的 Linq 语句是错误的,但很奇怪的是,我使用 Google 搜索这段异常信息,居然没有搜索到任何的相关信息,难道没有人遇到这个异常?还是我的写法有问题?Skip 是 Linq 分页的关键字,上面报错也是针对 Skip 的,如果我们把 Skip 去掉会怎样呢?

EntityFramework 7 OrderBy Skip Take-计算排序分页 SQL 翻译

可以看到,我们不使用 Skip,只是使用 Take 进行 Top 查询,是没有任何问题的,还有个问题是,如果使用“计算”性质的 OrderBy,不管是 SQL Server Profiler,还是 EF7 Log 都捕获不到计算的表达式,比如上面的 Take 查询代码,使用 SQL Server Profiler,最后捕获到的 SQL 代码为:

EntityFramework 7 OrderBy Skip Take-计算排序分页 SQL 翻译

你会看到,居然连 OrderBy 也没有了,不知道是什么原因?前几天也遇到这样类似一个问题:EntityFramework 7 smallint short 奇怪问题(已解决),主要是使用 short where 查询,没有捕获到,最后发现是 Linq 写法问题,应该使用 equals 进行判断,现在发现这两个问题比较相似,郁闷的是,不知道这个 Linq 该如何翻译。

上面这样方式行不通,自己也没有头绪,然后就在 Google 上搜各种关键词,比如:linq orderby math skip,linq calculate math skip 等等,但都尝试了下,还是不行,给出几个参考资料:

网上关于 OrderBy 计算排序的资料,大部分是关于计算排序后获取 Count,然后再进行分组查询出来,比如这段 Linq 代码:

var query = from p in yourContext.Activation_Details
group p by new
{
ProductVersion = p.ProductVersion,
ProductID = p.ProductID,
SubProductID = p.SubProductID
}
into pgroup
let count = pgroup.Count()
orderby count
select new
{
Count = count,
ProductVersion = pgroup.Key.ProductVersion,
ProductID = pgroup.Key.ProductID,
SubProductID = pgroup.Key.SubProductID
};

但这不是我想要的,花了很多时间也没有找到正确的解决方式,最后换一种思路去思考这个问题,如果 OrderBy 在 Linq 中不能进行计算排序,那就针对这个排序计算进行“翻译”,什么意思呢?比如一开始的 ORDER BY ([b].[BlogId] * 2 + B.BlogCateId),其实就是对两个字段进行组合排序,一个是对 BlogId 进行翻倍,然后再加上 BlogCateId 的值,换一种方式其实也是可以的,比如下面这段代码:

var result = context.Blogs.OrderBy(b => b.BlogId * 2).ThenBy(b => b.BlogCateId).Skip(0).Take(100).ToList();

执行结果:

EntityFramework 7 OrderBy Skip Take-计算排序分页 SQL 翻译

不知道这样的写法和一开始上面的 SQL 是不是一样的效果,如果你有更好的“翻译” Linq 代码,还请指教。