[轉]在Entity Framework中使用LINQ語句分頁


本文轉自:http://diaosbook.com/Post/2012/9/21/linq-paging-in-entity-framework

 

我們知道,內存分頁效率很低。並且,如果是WebForm的項目,頁面上會有巨型ViewState,這必然不好。我自己博客用的是一個存儲過程做的分頁,用到現在都挺好,沒有任何效率問題。后來想到,既然項目里有Entity Framework,那為什么不利用EF完成分頁呢~

稍做研究之后發現,EF分頁其實很簡單。不過一樣寫文章了,光貼代碼是不負責的,還是得稍微介紹一下相關的知識。

一、頁數計算

關於分頁的基本原理,網上有很多文章,我就不多敘述了。但我發現很多介紹分頁的文章里,計算頁碼公式都掐掉了,廣為流傳的版本是:

totalPage = totalRecord / pageSize +1
稍微推敲一下就會發現,這個公式在totalRecord和pageSize正好整除時,會多一頁。比如10條記錄,每頁5條,應該是2頁的,但結果是3。然而,不寫+1又不行,因為要考慮到記錄總數小於頁尺寸的情況。現在,許多網站的分頁都有這個問題。而我自己用的是下面這個公式,無論如何都不會掐掉的:
totalPage =(totalRecord + pageSize -1)/ pageSize
注意,C#里的“/”是整除。所以這兩個公式不是數學意義上的計算公式,呵呵。

二、分頁存儲過程的原理

(題外話:SQL Server 2012有了新的分頁語法,本文只針對SQL Server 2008 R2及以往的版本)

任何分頁存儲過程的核心語句最后都會拼接為類似下面的這種SQL語句:

SELECT TOP(10)-- pageSize
       *
FROM   (
           SELECT *,
                  ROW_NUMBER() OVER(ORDER BY [Temp].[PubDate] DESC) AS
                  [row_number]
           FROM   [dbo].[Post] AS [Temp]) AS [Temp]
WHERE  [Temp].[row_number]>10--(pageIndex -1)* pageSize
ORDER BY
       [Temp].[PubDate] DESC
這段SQL獲取的是Post表,按PubDate逆序排列后,第二頁的數據。

ROW_NUMBER()函數的作用是給查詢結果生成行號,這是為了給后面的where語句用的。這個函數的具體介紹請看MSDN:http://msdn.microsoft.com/en-us/library/ms186734.aspx

三、LINQ to SQL分頁

我們的目標很明確,就是最終要將LINQ語句翻譯到與上面相符的SQL語句。在LINQ to SQL中,Enumerable.Take<TSource>最終會翻譯為TOP(n),而Enumerable.Skip<TSource>和Take結合起來使用,就會翻譯成ROW_NUMBER()…OVER…的語句。

所以,上面貼出的SQL代碼的等價LINQ是:

Enumerable.OrderByDescending(p => p.PubDate).Skip(10).Take(10);
 

四、在Entity Framework中對某表分頁

用EF對某表做完映射后,我們可以封裝一個分頁方法,比如:

private static List<Post> GetPostList(int pageIndex, int pageSize)
{
    int startRow = (pageIndex - 1) * pageSize;
    using (var db = new EdiBlogEntities())
    {
        var query = db.Post.OrderByDescending(p => p.PubDate).Skip(startRow).Take(pageSize);
        return query.ToList();
    }
}
對於方法的返回類型,我選擇List是因為調用它的程序不需要訪問導航屬性。如果程序中有訪問導航屬性的需要,就不能關閉當前的database context,應該要返回IEnumerable<T>類型。

測試代碼:

static void Main(string[] args)
{
    int pageIndex = 2;
    int pageSize = 10;
    int totalPage = (GetPostCount() + pageSize - 1) / pageSize;

    Console.WriteLine("List of Posts. Page {0} / {1}, showing {2} rows per page.", pageIndex, totalPage, pageSize);
    Console.WriteLine("---------------------------------------------------------");
    foreach (var item in GetPostList(pageIndex, pageSize))
    {
        Console.WriteLine("{0}, {1}", item.Title, item.PubDate);
    }
}

private static int GetPostCount()
{
    using (var db = new EdiBlogEntities())
    {
        return db.Post.Count();
    }
}
 

運行結果:

和之前的存儲過程分頁是一致的:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM