• [转]在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();
        }
    }
     

    运行结果:

    和之前的存储过程分页是一致的:

  • 相关阅读:
    spring多个数据源配置
    BZOJ 1878: [SDOI2009]HH的项链 离线树状数组
    Codeforces Round #321 (Div. 2) E. Kefa and Watch 线段树hash
    Codeforces Round #321 (Div. 2) D. Kefa and Dishes 状压dp
    Codeforces Round #321 (Div. 2) C. Kefa and Park dfs
    Codeforces Round #321 (Div. 2) B. Kefa and Company 二分
    Codeforces Round #321 (Div. 2) A. Kefa and First Steps 水题
    Codeforces Round #268 (Div. 1) B. Two Sets 暴力
    Codeforces Round #268 (Div. 1) A. 24 Game 构造
    2015北京网络赛 F Couple Trees 暴力倍增
  • 原文地址:https://www.cnblogs.com/freeliver54/p/3754783.html
Copyright © 2020-2023  润新知