[WebAPI] 讓ODATA傳回的結果依照程式碼的反向排序

  • 20100
  • 0
  • C#
  • 2013-10-17

摘要:讓ODATA傳回的結果依照程式碼的反向排序

雖說ODATA是讓排序與選擇的控制權更簡單的轉移到Client端上,
但你知道,有時會有一些特殊的需求...

這邊遇到的例子是客戶不想下orderby,但他希望取得資料的排序永遠可以從新到舊,且可以使用ODATA的$top與$skip。

其實另外寫API也未嘗不可,不過遇到這個問題也蠻有趣的。

 

首先,由新到舊通常的做法是用建立時間或Id序數做反向排序,寫法很單純:

        [Queryable]
        public IQueryable<Book> GetBooks()
        {
            return db.Books.OrderByDescending(book => book.Id);
        }

db是一個DbContext,資料是從SQL Server撈的。

在不使用ODATA的情況下,出來的資料很正常:

[{"ID":4,"Name":"三隻小豬"},{"ID":3,"Name":"白雪公主"},{"ID":2,"Name":"睡美人"},{"ID":1,"Name":"小紅帽"}]

但使用ODATA,加上$top=2,撈出前兩筆時,資料好像怪怪的:

[{"ID":1,"Name":"小紅帽"},{"ID":2,"Name":"睡美人"}]

刪的不是全撈的前兩筆,順序也沒有依照全撈的順序。

檢查一下SQL指令,發現變成這樣:

SELECT TOP (2)
 [Extent1].[Id],
 [Extent1].[Name]
 FROM [db].[Book] AS [Extent1]
ORDER BY [Extent1].[Id] ASC

雖稍有簡化,但重點在於.......ORDER BY 被改成了ASC!

 

實驗了一陣子,這邊說說最簡單的解決方法:

        [Queryable(EnsureStableOrdering = false)]
        public IQueryable<Book> GetBooks()
        {
            return db.Books.OrderByDescending(book => book.Id);
        }

只有第一行在宣告Queryable屬性時有差,就這樣?

沒錯,就這樣。這樣ODATA就不會去更動原本的排序了,不過應該是有些副作用,我還沒詳測就是了。

另外如果是使用ODataQueryOptions的ApplyTo的話,可以套用settings:

        public IQueryable<Book> GetBooks(ODataQueryOptions opts)
        {
            return (IQueryable<Book>)opts.ApplyTo(
                db.Books.OrderByDescending(book => book.Id),
                new ODataQuerySettings() { EnsureStableOrdering = false });
        }