代码改变世界

【开源】我的分页控件正式命名为QuickPager ASP.NET2.0分页控件

2008-06-26 19:34  金色海洋(jyk)  阅读(5030)  评论(24编辑  收藏  举报


分页控件正式命名为 QuickPager ASP.NET2.0分页控件

版本号:2.0.0.1
Framework:.net2.0
分页方式:PostBack 、URL (暂时没有实现URL的分页方式)
webform  (b/s)
支持多种数据库,分页算法,提取数据的方式都可以替换。



    分页,自从做b/s的项目起,就和分页打起了交到,一开始使用UserControl来做,很粗糙,也没有什么性能可言。但是分页又是不可避免的,一次提取全部的数据,咱用的资源太多,吃不消。于是乎就在不断的想办法,一是操作简单,一是性能要好。
    想要操作简单就要封装成自定义控件,要想性能好就要研究数据库的特点,SQL语句的分页算法,最最重要的就是要设置好合理的索引!往往索引是最重要的,但同时往往又是被忽略的

    于是一方面研究自定义服务器控件如何编写,一面研究如何写SQL语句才能让提取数据更快,由于一直在使用SQL Server2000,所以分页算法也都是围绕这个数据库转的。04年底终于初步实现了,至少自己用起来还是很顺手的。也就是在这时候发现了吴旗娃的分页控件。于是进行了一下对比:

    吴旗娃的分页控件是只负责UI的绘制,能够显示出来总记录数、总页数、当前页号、上一页、下一页、页号导航(1,2,3,4...)能够生成分页事件就可以了,其他的像是用什么分页算法、到哪个数据库(或者XML)、如何提取数据统统的不管,需要另行编码。

    这样做的有点就是:灵活。适用于多种数据源,甚至是XML文件,也可以自由选择分页算法,这样做确实是很灵活,但是随之而来的缺点就是:繁琐。

    对于只针对数据库来分页的情况,每次分页的时候,都要再写点提取数据的代码,真是麻烦。我很懒,自然不愿意接受这种情况,我要做到,只写几行简单的代码就可以实现分页的全部功能!我可以接受分页控件只能对SQL Server进行分页,只使用DataTable来传递数据,但是在调用的时候一定要简单简单再简单。

    于是我的分页控件(myPage)不仅负责UI的绘制,还要负责SQL语句的生成(根据属性组合SQL语句),还要到数据库里提取数据(当然这个功能要交给数据访问函数库来实现),最后还要能够自动处理翻页时产生的事件,自动绑定控件。

    以前的版本是基于 .net1.1编写的,主要的代码结构是04年底确定的,以后只是小修小补,一直未作大的改动,因为一直都可以使用,没有太大的问题。现在VS2008已经出来好久了,应该升级了。既然升级那么就升得彻底一点,不仅仅是把代码拷贝到vs2008里面,重新编译一边就ok了(这样做确实可以一次编译成功),要借此机会彻底改变一下结构。

    和吴旗娃的分页控件相比,以前的分页控件确实是缺乏灵活性,对多种数据库支持的不好,分页算法也不能灵活的更换和自由编写,只能用DataTable来承载数据,PostBack分页和URL分页也是使用了两个控件来实现。一切看起来都是很混乱的(不过很奇怪,运行起来却没有什么问题)。

    既然知道了有这么多的缺点,那么就要在升级的时候一一改正。按照职责分工,设置一个类负责UI的绘制,在设置一个类负责分页用得分页算法,在设置一个类来负责提取数据,最后把这几个类作为分页控件的成员,这样就可以互相调用,自由替换。结构图如下。


    好像有点过度设计的嫌疑,我也在想,用得着这么费事吗?真的需要这么写吗?

    (等等,这么看怎么有点像三层的结构,PageUI有点像UI层,PageSQL算是逻辑层吧(分页嘛,如何写SQL语句,这个可以算作是一种逻辑吧,如果不算的话也没什么),PageGetData就是数据访问了,当然还需要一个数据访问函数库,不过这个是分页控件外面的。我可不想写得像三层那样的麻烦。呵呵。)

    不过我也没有想把所有的数据库的所有可能的分页算法都写到数据库里,可以在分页控件的外面继承PageSQL类来写一个子类,在这个子类里面实现需要的分页算法,然后把这个实例赋值给分页控件就ok了。

    myPage.ManagerPageSQL = yourSQL;

    当然了,如果不给这个属性复制的话,分页控件会生成一个默认的PageSQL实例来自动赋值。PageUI和PageGetData也可以使用同样的方法换成适合自己的的要求实例(实现方法)。

    几个误区:

    1、存储过程。不是说使用了存储过程就代表着高效,那要看存储过程内部是什么样子的,如果存储过程内部采用组合SQL语句的方法,那么就丧失了存储过程最大的优势:缓存执行计划!试想,在存储过程里面组合SQL语句,你让SQL Server,如何保存执行计划呢?如果不能保存,那么和在程序里面组合SQL语句,然后提交给数据库有什么区别呢?

    我看到网上很多介绍分页算法的文章,大部分都是直接给出一个存储过程,在这个存储过程里面组合SQL语句,要知道,看这样的代码是多么的郁闷呀。

    当然,你可以为了能够缓存执行计划而一个表使用一个存储过程,就像吴旗娃的网站里提供的那个生成存储过程的模板提供的存储过程, 但是这样有很多的问题:会增加很多的存储过程、查询条件不容易灵活设置(查询字段越多越不好编写)、升级数据库(包括升级分页算法)。

    什么东东多了都是不好管理的,试想一个数据库里有500个存储过程,其中100个是用来分页的,100个是用来添加数据的,100个等等,是不是比较乱呢?

    查询,在做企业定制开发的程序的时候,最常听到的就是,你把列表里的所有字段都设置为可以查询的吧,而且可以多字段一起查询。呵呵,二、三十个字段在存储过程里面写成查询的方式,不太容易吧,而且需求还很很频繁的变化。

    以前的项目使用SQL Server2000 ,现在 SQL Server2005出来了,是不是要升级了,以前使用表变量(或者其他方式)的分页方式,听说05里面使用Row_Number()效率更高,编写起来也更容易,是不是要换一下呢?这个操作不太好做吧。

    而是用分页控件的形式就方便多了,因为是在分页控件内部组合分页算法的,只要保持属性(TabletName等)不变就可以了,用这些属性几乎和一组合成任意的分页算法,不怕数据库的升级、算法的更换。

    ( 这就是我的分页控件不采用存储过程的原因。)

    2、索引。要想提高分页的效率,必须要设置好索引,包括非聚集索引在内。

    索引在介绍分页算法的文章里,提到的几率并不多。并不是说只有在海量数据的情况下才需要索引,在数据量不多的情况下,有的是有也是需要的。

    前两天就遇到了一个,六七个表关联在一起,主表有一万多条记录(也是记录最多的表),关联比较多和复杂(有两个表需要使用两个关联字段,否则会出现重复记录),查询条件也比较多,而且还要使用三个字段来排序。这三个字段又不在同一个表里面。

    一开始记录不太多的情况,速度很快,后来记录达到一万多条以后,前几页也是比较快的,这是客户想要看看效率,就看了一下最后几页,这下惨了,五六秒钟之后才反映过来。反复试了几次确实很慢。这点数据量不会这么慢吧,想要说服客户也不是太容易(毕竟一条记录才五六个字段,加起来不超过20个字),还是自己想办法吧。

    打开查询分析器,拷贝视图里得SQL语句,然后查看执行计划,晕了,好多好多,一个一个看吧,好多都是可以利用索引来查找数据的,有一个需要全表扫描,占用了46%。就拿他下手吧,其实现在还不能完全看懂执行计划的图都表是什么意思,只能跟着感觉瞎猜。最后给最后一个排序字段加了一个非聚集索引(好像还给一个字段加了一个非聚集索引),问题解决了,查看最后几页,在两秒中内就可以显示出来数据(局域网)。这样可与也基本可以接受了。其实正式使用的时候,都会通过查询条件先过滤数据的。所以只要前100页能够很快显示出来就可以了。


    所以,要想提高分页的效率,一定要先想办法设置好合理的索引。

    
    我查了一下字典,page有翻页的含义,而pager是BP机、网络寻呼机的意思,不知道为什么有一个人的分页控件叫做 myPager。不管别人是怎么想的了,我的这个不算重名吧。

    mypage 的使用方法
 protected void Page_Load(object sender, EventArgs e)
        
{
            myPage.ControlGridID 
= GV.ID;       //设置显示数据的控件的ID值。

            
if (!Page.IsPostBack)
            
{
                
//属性会保存在ViewState里面,所以在第一次访问的时候赋值就可以了。
                SetPageInfo();
            }

        }


        
private void SetPageInfo()
        
{
            myPage.TableName 
= " V_List_Table";                //表名或者视图名
            myPage.TableShowColumns = "*";                //显示的字段
            myPage.TableIDColumns = "ID";            //主键
            myPage.TableOrderColumns = "col1,col2,col3";    //排序字段
            myPage.PageSize = 10;                           //一页的记录数
            myPage.NaviCount = 5;                           //页号导航的数量
            myPage.TableQuery = "";                         //查询条件

            myPage.BindFirstPage();                         
//显示第一页的数据

           
        }

    
    不要怕,不用给所有的属性都赋值的,至少给 
myPage.ControlGridID 
myPage.TableName
myPage.TableOrderColumns 

这是那个属性赋值,然后执行myPage.BindFirstPage();     就可以了。
                    
    先写这些吧,有些头晕了,呵呵。

分页控件的源代码下载网址:http://www.cnblogs.com/jyk/archive/2008/04/25/1170979.html
(在网页的下面

下载文件里面由一个测试网页:http://localhost:5561/test/Testlist.aspx?fid=1
请注意后面的参数。
在webconfig里面修改连接字符串和数据库类型。DataType ---- 1: MS SQL ;2:Oledb;3:ODBC。

直接看源码:

http://www.cnblogs.com/jyk/archive/2008/06/25/1229967.html 

http://www.cnblogs.com/jyk/archive/2008/06/25/1229973.html 

目前SQl Server2005 使用的分页算法

 set nocount on;
            with t_pager as (
               select *,rn = ROW_NUMBER() OVER (ORDER BY id desc) FROM test_indexorder
             )
            SELECT id,name,content,co1,co2,co3,co4,co5 from t_rn WHERE rn between 19007 and 19057;


2