對於常常接觸的分頁你肯定你真的會嗎

對於一直奮鬥在crud「前線」的碼農, 天天面對的就是形形色色的crud代碼,不過寫了這麼多的crud你肯定面對你常常也得topage,getpage肯定沒什麼問題嗎?那麼今天我就來拋磚一下(目前僅在sqlserver下有效不過目測其餘數據庫也同樣)sql

你們通常來講都會封裝一個分頁方法,這是每一個開發者都會的技能,對於ef我今天就來說下正確的分頁姿式先上分頁代碼數據庫

通常咱們會定義一個分頁返回對象固然你也能夠用out返回count來實現async

    /// <summary>
    /// 分頁集合
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class PagedResult<T>
    {

        #region Ctor
        /// <summary>
        /// 初始化一個新的<c>PagedResult{T}</c>類型的實例。
        /// </summary>
        /// <param name="total">總記錄數。</param>
        /// <param name="data">當前頁面的數據。</param>
        public PagedResult(List<T> data, int total)
        {
            this.Total = total;
            this.Data = data;
        }
        #endregion

        #region Public Properties
        /// <summary>
        /// 獲取或設置總記錄數。
        /// </summary>
        public int Total { get; set; }
        /// <summary>
        /// 分頁數據
        /// </summary>
        public List<T> Data { get; set; }
        #endregion
    }

有了這個代碼後通常咱們會對iqueryable進行封裝分頁方法,先上一個簡單版本sqlserver

        public static async Task<PagedResult<T>> ToPageResultAsync<T>(this IQueryable<T> source, int pageIndex, int pageSize)
        {
            //設置每次獲取多少頁
            var take = pageSize <= 0 ? 1 : pageSize;
            //設置當前頁碼最小1
            var index = pageIndex <= 0 ? 1 : pageIndex;
            //須要跳過多少頁
            var skip = (index - 1) * take;
            //獲取每次總記錄數
            var count = await source.CountAsync();
            var data = await source.Skip(skip).Take(take).ToListAsync();
            return new PagedResult<T>(data, count);
            
            
        }

這樣咱們第一個版本的分頁代碼就封裝好了,可是對於這個熟悉的方法不少人會止步於此,畢竟過分優化是很愚蠢的,可是咱們會發現一個很重要的優化點是不少人會忽略的就是無心義查詢,直接上第二個版本性能

        public static async Task<PagedResult<T>> ToPageResultAsync<T>(this IQueryable<T> source, int pageIndex, int pageSize)
        {
            //設置每次獲取多少頁
            var take = pageSize <= 0 ? 1 : pageSize;
            //設置當前頁碼最小1
            var index = pageIndex <= 0 ? 1 : pageIndex;
            //須要跳過多少頁
            var skip = (index - 1) * take;
            //獲取每次總記錄數
            var count = await source.CountAsync();
            
            //當數據庫數量小於要跳過的條數就說明沒數據直接返回不在查詢list
            if (count <= skip)
                return new PagedResult<T>(new List<T>(0), count);
            var data = await source.Skip(skip).Take(take).ToListAsync();
            return new PagedResult<T>(data, count);
            
        }

細心的噴友可能發現了僅僅是多了一個判斷能夠減小跳大頁的狀況,可是對於這種狀況下咱們會發現若是在大數據量好比百萬往上的狀況下每每單個簡單的查詢會讓你感受性能的低下,明明就查詢返回了一條數據怎麼要這麼久,反而返回多數據的時候變快了,其實這裏就有一個問題就是當你大數據

返回的數據庫結果僅1條的狀況下若是你用了top 2那麼他就會一直找count下的數據直到知足2條(我的猜測),因此咱們再來優化下分頁代碼優化

        public static async Task<PagedResult<T>> ToPageResultAsync<T>(this IQueryable<T> source, int pageIndex, int pageSize)
        {
            //設置每次獲取多少頁
            var take = pageSize <= 0 ? 1 : pageSize;
            //設置當前頁碼最小1
            var index = pageIndex <= 0 ? 1 : pageIndex;
            //須要跳過多少頁
            var skip = (index - 1) * take;
            //獲取每次總記錄數
            var count = await source.CountAsync();
            
            //當數據庫數量小於要跳過的條數就說明沒數據直接返回不在查詢list
            if (count <= skip)
                return new PagedResult<T>(new List<T>(0), count);
            //獲取剩餘條數
            int remainingCount = count - skip;
            //當剩餘條數小於take數就取remainingCount
            var realTake = remainingCount < take ? remainingCount : take;
            var data = await source.Skip(skip).Take(realTake).ToListAsync();
            return new PagedResult<T>(data, count);
        }

當數據庫中剩餘的條數減去對應的跳過數目剩餘的數目若是不夠本次pagesize的時候就再也不須要按pagesize獲取數據了,因此對於本次查詢僅適用realTake就能夠了,到此爲止分頁的正確姿式就展現完了,若是這篇文章對你有幫助就給我點個贊吧謝謝this

相關文章
相關標籤/搜索