先解釋一下這個標題的意思,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();
}
}
沒錯,最“直白”的翻譯就是這樣的,但測試運行后會拋出異常:
異常信息: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 去掉會怎樣呢?
可以看到,我們不使用 Skip,只是使用 Take 進行 Top 查詢,是沒有任何問題的,還有個問題是,如果使用“計算”性質的 OrderBy,不管是 SQL Server Profiler,還是 EF7 Log 都捕獲不到計算的表達式,比如上面的 Take 查詢代碼,使用 SQL Server Profiler,最后捕獲到的 SQL 代碼為:
你會看到,居然連 OrderBy 也沒有了,不知道是什么原因?前幾天也遇到這樣類似一個問題:EntityFramework 7 smallint short 奇怪問題(已解決),主要是使用 short where 查詢,沒有捕獲到,最后發現是 Linq 寫法問題,應該使用 equals 進行判斷,現在發現這兩個問題比較相似,郁悶的是,不知道這個 Linq 該如何翻譯。
上面這樣方式行不通,自己也沒有頭緒,然后就在 Google 上搜各種關鍵詞,比如:linq orderby math skip,linq calculate math skip 等等,但都嘗試了下,還是不行,給出幾個參考資料:
- Custom order by, is it possible?
- Linq OrderBy calculation of properties (with join)
- LINQ: .NET Language-Integrated Query
網上關于 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();
執行結果:
不知道這樣的寫法和一開始上面的 SQL 是不是一樣的效果,如果你有更好的“翻譯” Linq 代碼,還請指教。
文章列表