武裝你的WEBAPI-OData分頁查詢

来源:https://www.cnblogs.com/podolski/archive/2020/05/18/12902614.html
-Advertisement-
Play Games

本文屬於OData系列 目錄 "武裝你的WEBAPI OData入門" "武裝你的WEBAPI OData便捷查詢" "武裝你的WEBAPI OData分頁查詢" 武裝你的WEBAPI OData資源更新 武裝你的WEBAPI OData之EDM 武裝你的WEBAPI OData常見問題 武裝你的W ...


本文屬於OData系列

目錄


Introduction

分頁是數據請求避免不了的問題,數據很多的情況下,通過GET請求一次性返回所有的數據,不光性能底下,而且不好展示。

分頁的原理就是客戶端請求伺服器,伺服器返回的數據是有限的數據(限制於pageSize),同時返回一個數據的總量count,方便客戶端進行處理。也有另外一種實現,使用nextlink指示下一頁的位置。

傳統實現

傳統的實現,我比較喜歡LINQ的Skip和Take方法。

/// <summary>
/// 有參GET請求
/// </summary>
/// <returns></returns>
[HttpGet("page")]
[ProducesResponseType(typeof(ReturnData<Page<UserInfoModel>>), Status200OK)]
[ProducesResponseType(typeof(ReturnData<string>), Status404NotFound)]
public async Task<ActionResult> Get(string username, int pageNo, int pageSize)
{
    if (pageSize <= 0 || pageNo <= 0) return BadRequest(new ReturnData<string>("Error request"));
    IEnumerable<UserInfoModel> result;
    if (string.IsNullOrWhiteSpace(username))
        result = _userManager.Users.Select(w => ToUserInfoModel(w)).ToList();
    else
        result = _userManager.Users.Select(w => ToUserInfoModel(w)).ToList().Where(w => w.Username.Contains(username));
    var response = result.Skip((pageNo - 1) * pageSize).Take(pageSize);
    Page<UserInfoModel> page = new Page<UserInfoModel>() { PageNo = pageNo, PageSize = pageSize, Result = response, TotalCount = result.Count() };
    return Ok(new ReturnData<Page<UserInfoModel>>(page));
}

通過傳遞username、pageNo和pageSize即可實現分頁功能。

OData實現分頁

OData查詢不需要後端再自行設計接受參數、實現等內容,並且支持兩種方式實現分頁:客戶端模式和伺服器模式。首先我們需要補補幾個關鍵字的用法:(適用於OData V4)

$count

count關鍵字可以隨同查詢一起使用,使用$count=true的形式即可在查詢結果中追加返回符合查詢條件的所有的記錄的數量。

GET http://localhost:9000/api/devicedatas('ZW000001')?$count=true

註意這裡不是返回的當前結果的計數。

{
    "@odata.context": "http://localhost:9000/api/$metadata#DeviceDatas",
    "@odata.count": 80,
    "value": [
        {
            "id": "554b1ed8-6429-4ad3-83f9-45c7696547e6",
            "deviceId": "ZW000001",
            "timestamp": 1589544960000,
            "dataArray": []
        },
        ...

$skip

skip關鍵字可以指定跳過的記錄數量,使用$skip=10這種形式。

GET http://localhost:9000/api/devicedatas('ZW000001')?$skip=30

返回的結果是跳過了前面的N條記錄。

$top

top關鍵字指定截取的符合查詢條件中的前n條記錄,使用top=10這種形式。

GET http://localhost:9000/api/devicedatas('ZW000001')?$top=10

$skiptoken

skiptoken這個東西和前面的東西都不一樣。skiptoken必須要伺服器返回,一般來說是伺服器根據主鍵的形式返回結果,然後調用方直接調用。經常出現在nextlink中,用於伺服器分頁。

GET http://localhost:9000/api/devicedatas('ZW000001')?$skiptoken='554b1ed8-6429-4ad3-83f9-45c7696547e6'

註意這裡不是返回的當前結果的計數。

{
    "@odata.context": "http://localhost:9000/api/$metadata#DeviceDatas",
    "value": [
        {
            "id": "554b1ed8-6429-4ad3-83f9-45c7696547e6",
            "deviceId": "ZW000001",
            "timestamp": 1589544960000,
            "dataArray": []
        },
        ...

客戶端模式

客戶端模式是客戶端主導的分頁實現,分頁的頁數數量之類的,都需要由客戶端指定,對客戶端來說,比較靈活。主要使用到count、skip和top三個關鍵字。

  1. 預設情況,伺服器返回所有的記錄。
  2. 假設按照每頁10條記錄進行分頁,那麼我們首次請求(請求第一頁)應該使用$count=true&$skip=0&$top=10獲取第一頁數據,同時帶有數據計數。
  3. 根據第一次請求獲得數據計數,可以快速計算總共的分頁數量。比如返回count=72,那麼總共的頁數應該是72/10 + 1 =8頁(最後一頁只有2個數據)
  4. 生成每個頁碼的鏈接,第二頁應該是$count=true&$skip=10&$top=10
GET http://localhost:9000/api/devicedatas('ZW000001')?$count=true&$skip=10&$top=10
  • 這幾條命令需要先啟用,可以在startup.cs中修改:
app.UseMvc(
    routeBuilder =>
    {
        // the following will not work as expected
        // BUG: https://github.com/OData/WebApi/issues/1837
        // routeBuilder.SetDefaultODataOptions( new ODataOptions() { UrlKeyDelimiter = Parentheses } );
        routeBuilder.ServiceProvider.GetRequiredService<ODataOptions>().UrlKeyDelimiter = Parentheses;

        // global odata query options
        //routeBuilder.EnableDependencyInjection();
        routeBuilder.Select().Expand().Filter().OrderBy().MaxTop(600).Count().SkipToken();

        routeBuilder.MapVersionedODataRoutes("odata", "api", modelBuilder.GetEdmModels());
    });

服務端模式

客戶端模式靈活,但是有一個問題不好處理:客戶端在兩次請求的過程中,數據發生了變化,那會遇到一些意想不到的問題,比如說數據刪除了其中的一些,那麼某條數據很有可能會同時出現在兩個頁。因此,可以讓伺服器幫我們做分頁,伺服器管理所有的數據,對兩次請求的數據變化也能及時感知,不會出現這個問題。

服務端模式需要使用到skiptoken和pagesize設置。

服務端模式,客戶端請求集合,伺服器返回部分數據,同時提供一個nextlink,客戶端直接請求這個鏈接,就可以獲得更多的數據。

skiptoken啟用可以參考上面客戶端模式的代碼。pagesize是伺服器最多每頁返回多少條數據的設置,可以在上面全局指定,也可以在具體的方法上面指定。

[ODataRoute]
[EnableQuery(PageSize = 1)]
[ProducesResponseType(typeof(ODataValue<IEnumerable<DeviceInfo>>), Status200OK)]
public IActionResult Get()
{
    return Ok(_context.DeviceInfoes.AsQueryable());
}

試著使用原始的方式進行請求。

GET http://localhost:9000/api/DeviceInfoes?$count=true

返回結果如下,能看到,返回的數據的結尾,多了一個@odata.nextLink,這個直接點擊,就可以直接請求下一組數據。在下一組數據中又會有在下一組數據的地址,直到最後一組數據。

{
    "@odata.context": "http://localhost:9000/api/$metadata#DeviceInfoes",
    "@odata.count": 3,
    "value": [
        {
            "deviceId": "ZW000001",
            "name": null,
            "deviceType": null,
            "imagePath": null,
            "layout": []
        }
    ],
    "@odata.nextLink": "http://localhost:9000/api/DeviceInfoes?$count=true&$skiptoken=deviceId-'ZW000001'"
}

註意:

  • 我這裡主鍵使用的是字元串類型,並且用的是EF CORE 3.0,直接請求會返回伺服器錯誤,需要自行指定string的比較模式,可以使用AsEnumerable()在System.Linq中處理。如果使用的主鍵是數值型,那麼應該不會有這個問題。參考這裡
  • 可以在請求中同時應用skip等客戶端模式的語法,構造自己需要的數據。

看完伺服器模式,感覺這模式有點僵硬啊,只能一條一條地獲取下一個鏈接,我要直接跳幾頁的時候怎麼辦呢?

首先你需要瞭解分頁的模式,我們請求http://services.odata.org/V4/TripPinService/People返回的nextlink會是這樣子的:

"@odata.nextLink": "https://services.odata.org/V4/TripPinService/People?%24skiptoken=8"

我這裡使用到了官方提供的一個地址,返回了8條數據,同時指示了下一個鏈接的位置,很明顯,這個skiptoken=8是從第9個開始的,因此指定的只是一個開頭的地址,我們可以自行修改成其他數字。(前面說到skiptoken必須要服務生成,指的是後面的查詢模式需要是由伺服器生成。)

那麼對於第三頁就是skiptoken=16。但是由於伺服器指定了分頁的大小8,我們查詢還是不方便,可以通過繼承EnableQueryAttribute實現,將這個[MyEnableQueryAttribute]替代剛剛的[EnableQuery]搬運

public class MyEnableQueryAttribute : EnableQueryAttribute
{
    public override IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions)
    {
        int pagesize = xxx;
        var result = queryOptions.ApplyTo(queryable, new ODataQuerySettings { PageSize = pagesize }); 
        return result;
    }
} 

總結

OData使用客戶端模式的分頁和服務端的分頁都能夠很方便地實現分頁查詢。一個GET查詢全部搞定,梭哈!不要問就是梭!

參考資料


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 啟言:每個函數定義都有鏈接器可識別的獨一無二的編譯後的函數名稱 種類:C 語言鏈接性、C++ 語言鏈接性,可能有如下的編譯器翻譯 spiff( int ) _spiff // C spiff( int ) _spiff_i // C++ (函數重載) spiff(double, double) _s ...
  • SunnyUI.Net, 基於 C# .Net WinForm 開源控制項庫、工具類庫、擴展類庫、多頁面開發框架 Blog: https://www.cnblogs.com/yhuse Gitee: https://gitee.com/yhuse/SunnyUI GitHub: https://git ...
  • 過程很簡單,傳遞ViewModel到附加屬性,附加屬性引發相關事件和取消事件,從而引發VM中的委托。 修正版本2 2020年5月19日 07點03分 徹底一模一樣了,和之前寫的。 不過更順了 添加內容 public class AttachModel: Animatable { public sta ...
  • 最近公司很多業務都要求上雲,雲上的資料庫購買了mysql(估計是應為便宜吧),所以我用abp框架開發的應用都要逐步切換到mysql。經過一陣摸索踩坑,總算是切換成功了,所以先記錄下方便後續切換使用,也順便分享給有需要的朋友。 一、集成MySQL 其實集成mysql主要是參照官方文檔進行就可以了(官方 ...
  • 上一篇文章(https://www.cnblogs.com/meowv/p/12896898.html)已經成功將博客項目跑起來了,那麼本篇主要是將之前遺留的問題解決,現在的代碼看起來可能還是比較混亂,有大量與之無關的代碼存在裡面,對於強迫症患者來說真的是零容忍。 在程式員界,總有一批強迫症患者,他 ...
  • 0.前言 到目前為止,我們看了一下如何聲明EF Core的初步使用,也整體的看了下EF Core的映射關係配置以及導航屬性的配置。 這一篇,我帶大家分享一下,我在工作中需要的EF Core的用法。 1. 初始化 在實際開發中,一般都是先設計好數據表再進行開發,所以很少用到EF Core的數據遷移功能 ...
  • 找到layoutControlGroup1屬性的Padding 設置All 為0,下圖為設置完成後的: 有問題記錄一下。 ...
  • 最近在做.net core項目,然後就把VS 2017更新到最新版本,以及更新.net core sdk。頻繁的更新開發工具,結果就出現了“調用目標發生了異常”,本來運行、調試都是好好的,更新了一下版本,就出現這樣的問題,雖然不影響運行使用,但是每執行到一段代碼,就彈出“調用目標發生了異常”,特別煩 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...