我的番茄炒蛋
生活如此精彩,挑战无处不在!

导航

 
在DataGrid的web版控件中提供了自动分页的功能,但是我从来没用过它,因为它实现的分页只是一种假相。我们为什么需要分页?那是因为符合条件的记录可能很多,如果一次读取所有的记录,不仅延长获取数据的时间,而且也极度浪费内存。而分页的存在的主要目的正是为了解决这两个问题(当然,也不排除为了UI美观的需要而使用分页的)。而web版的DataGrid是怎样实现分页的了?它并没有打算解决上述两个问题,而还是一次读取所有的数据,然后以分页的样子表现出来。这是对效率和内存的极大损害!
        于是我自己写了一个分页管理器,关于它的描述和实现如下所示:
**///<summary>
    
/// IDataPaginationManager 用于实现数据查询的分页操作。
    
/// 当表中的数据记录很多时,用Apdater一次读出所有的数据即耗费时间又浪费内存,这时就要用到分页了。
    
/// DataPaginationManager每次从数据库中读取指定的一页,并且把历史页缓存在Stack中,这样,如果再次访问历史页,
    
/// 就不用再访问数据库了,直接从Stack中取出即可。
    
/// 
    
/// 作者:朱伟 sky.zhuwei@163.com 
    
/// </summary>

    public interface IDataPaginationManager
    
{
        
//complexIDName 如"ID"或"sta.ID"(用于复合查询)
        
//selectStr 中不允许包括Group By 和 Order By 等字段
        
//void Initialize(IDBAccesser accesser ,string selectStr ,string complexID_Name) ; 

        
int       ItemCount{get ;}
        
int          PageCount {get ;}
        
int          CurrentPageIndex{get ;}
        DataTable StartPage() ;
        DataTable NextPage() ;
        DataTable PrePage()  ;
        DataTable CurrentPage 
{get ;}
        DataTable GetPage(
int index) ; //只能随机访问曾经读取过的页

        
event PageChanged CurrentPageIndexChanged ;
    }


    
/**//// <summary>
    
/// DataPagination 是IDataPagination的默认实现。遵守SkyDataAccess协议--有一个表示唯一索引的字段"ID"
    
/// </summary>

    public class DataPaginationManager : IDataPaginationManager
    
{
        
private PaginationParas curParas = null ;
        
private IADOBase adoBase         = null ;
        
private int pageCount            = 0 ;    
        
private int itemCount            = 0 ;

        
private DataTable currentPage    = null ;
        
private int curPageIndex         = 0 ;                    

        
//用Stack来存储历史页,以实现前一页操作
        private Stack statusStackForward  = new Stack() ;
        
private Stack statusStackBackWard = new Stack() ;
        
private bool preForward = true ;//上一次是正向?

        
public  event PageChanged CurrentPageIndexChanged ;        

        IDataPagination 成员
#region IDataPagination 成员
        
public DataPaginationManager(IDBAccesser accesser ,string selectStr ,string complexID_Name ,int page_size)
        
{            
            
this.curParas = new PaginationParas() ;            
            
this.curParas.ComplexIDName = complexID_Name ;            
            
this.curParas.PageSize = page_size ;
            
this.curParas.SelectString = this.CheckSelectString(selectStr) ;    
            
this.curParas.ConnectString = accesser.ConnectString ;
            
this.curParas.DbType        = accesser.DataBaseType ;
            
this.curParas.DBTableName   = accesser.DbTableName  ;

            
this.InitializeAdoBase(accesser.DataBaseType ,accesser.ConnectString) ;            
            
this.pageCount = this.GetPageCount() ;
        }


        
public DataPaginationManager(PaginationParas paras)
        
{
            
this.curParas = paras ;
            
this.InitializeAdoBase(this.curParas.DbType ,this.curParas.ConnectString) ;            
            
this.pageCount = this.GetPageCount() ;
        }


        
private#region private
        
private void InitializeAdoBase(DataBaseType dbType ,string connStr)
        
{
            
switch(dbType)
            
{
                
case DataBaseType.SqlServer :
                
{
                    
this.adoBase = new SqlADOBase(connStr) ;
                    
break ;
                }

                
case DataBaseType.Ole :
                
{
                    
this.adoBase = new OleADOBase(connStr) ;
                    
break ;
                }

                
default:
                
{
                    
throw new Exception("The target DataBaseType is not implemented !")  ;
                }

            }

        }


        
private string CheckSelectString(string selectStr)
        
{
            
if((selectStr == null|| (selectStr == ""))
            
{
                
throw new Exception("SelectStr is invalid !")  ;
            }
            

            
string str = selectStr.ToLower() ;
            
if((str.IndexOf("order by"!= -1|| (str.IndexOf("group by"!= -1))
            
{
                
throw new Exception("SelectStr Can't contain 'order by' or 'group by' !")  ;
            }
    

            selectStr 
= selectStr.ToLower() ;
            
string ss = string.Format("select top {0} " ,this.curParas.PageSize) ;
            
return selectStr.Replace("select" ,ss );
        }


        
private string ConstructSelectString(bool first ,bool forward ,PageStatus curSta)
        
{
            
if(first)
            
{                
                
return this.curParas.SelectString ;
            }



            
string comp = " >= " ;
            
string curIDValue = curSta.preIDValueHead ;
            
if(forward)
            
{
                comp 
= " > " ;
                curIDValue 
= curSta.curIDValueEnd ;
            }
            

            
if(-1 == this.curParas.SelectString.IndexOf("where"))
            
{
                
return this.curParas.SelectString + string.Format(" where {0} {1} '{2}'" ,this.curParas.ComplexIDName ,comp ,curIDValue) ;
            }

            
            
return this.curParas.SelectString + string.Format(" and {0} {1} '{2}'" ,this.curParas.ComplexIDName ,comp ,curIDValue) ;            
        }


        
private int GetPageCount()
        
{
            
string str = null ;
            
int index = this.curParas.SelectString.IndexOf("where") ;
            
if(-1 == index)
            
{
                str 
= string.Format("Select Count(*) from {0}" ,this.curParas.DBTableName) ;
            }

            
else
            
{
                
string whereStr = this.curParas.SelectString.Substring(index) ;
                str 
= string.Format("Select Count(*) from {0} {1}" ,this.curParas.DBTableName ,whereStr) ;                    
            }


            DataSet ds 
= this.adoBase.DoQuery(str) ;
            
            
if(ds.Tables[0].Rows.Count != 0)
            
{
                
int num = int.Parse(ds.Tables[0].Rows[0][0].ToString()) ;
                
this.itemCount = num ;
                
int pageCount = num/this.curParas.PageSize ;
                
if(num%this.curParas.PageSize > 0)
                
{
                    pageCount 
+= 1 ;
                }


                
return pageCount ;
            }


            
this.itemCount = 0 ;
            
return 0 ;
        }

        
#endregion
        

        PageCount ,CurrentPageIndex ,CurrentPage
#region PageCount ,CurrentPageIndex ,CurrentPage
        
public int PageCount
        
{
            
get
            
{
                
return this.pageCount ;
            }

        }


        
public int ItemCount
        
{
            
get
            
{
                
return this.itemCount ;
            }

        }


        
public int CurrentPageIndex
        
{
            
get
            
{
                
return this.curPageIndex ;
            }

        }


        
public DataTable CurrentPage
        
{
            
get
            
{
                
return this.currentPage ;
            }

        }

        
#endregion


        StartPage
#region StartPage
        
public DataTable StartPage()
        
{
            
if(this.pageCount == 0)
            
{
                
return null ;
            }


            
this.statusStackBackWard.Clear() ;
            
this.statusStackForward.Clear() ;

            
string select = this.ConstructSelectString(true ,true ,null) ;
            DataSet ds 
= this.adoBase.DoQuery(select) ;

            PageStatus sta 
= new PageStatus() ;
            sta.curIDValueEnd  
= ds.Tables[0].Rows[ds.Tables[0].Rows.Count-1]["ID"].ToString() ;
            sta.preIDValueHead 
= ds.Tables[0].Rows[0]["ID"].ToString() ;
            sta.curTable 
= ds.Tables[0] ;
            
this.statusStackForward.Push(sta) ;

            
this.curPageIndex   = 0 ;
            
this.currentPage    = sta.curTable ;
            
this.ActivePageIndexChanged(this.curPageIndex) ;

            
return this.currentPage ;
        }

        
#endregion


        NextPage
#region NextPage
        
public DataTable NextPage()
        
{
            
if(this.curPageIndex >= this.pageCount-1)
            
{
                
return null ;
            }


            
if(this.statusStackBackWard.Count >0)
            
{                
                PageStatus staRes 
= (PageStatus)this.statusStackBackWard.Pop() ;
                
this.statusStackForward.Push(staRes) ;
                
if(! this.preForward)
                
{
                    
if(this.statusStackBackWard.Count > 0)
                    
{
                        staRes 
= (PageStatus)this.statusStackBackWard.Pop() ;
                        
this.statusStackForward.Push(staRes) ;
                    }

                }

                
                
return this.ReturnCurrentPage(staRes.curTable ,true) ;
            }


            PageStatus curSta 
= (PageStatus)this.statusStackForward.Peek() ;    
            
string select = this.ConstructSelectString(false ,true ,curSta) ;
            DataSet ds 
= this.adoBase.DoQuery(select) ;            

            PageStatus sta 
= new PageStatus() ;
            sta.curIDValueEnd  
= ds.Tables[0].Rows[ds.Tables[0].Rows.Count-1]["ID"].ToString() ;
            sta.preIDValueHead 
= curSta.curTable.Rows[0]["ID"].ToString() ;
            sta.curTable 
= ds.Tables[0] ;
            
this.statusStackForward.Push(sta) ;            

            
return this.ReturnCurrentPage(sta.curTable ,true) ;
        }

        
#endregion


        PrePage
#region PrePage
        
public DataTable PrePage()
        
{
            
if(this.curPageIndex < 1)
            
{
                
return null ;
            }


            PageStatus oldSta 
= (PageStatus)this.statusStackForward.Pop() ;    
            
this.statusStackBackWard.Push(oldSta) ;

            
if(this.preForward)
            
{
                
if(this.statusStackForward.Count > 0)
                
{
                    oldSta 
= (PageStatus)this.statusStackForward.Pop() ;    
                    
this.statusStackBackWard.Push(oldSta) ;
                }

            }
            

            
return this.ReturnCurrentPage(oldSta.curTable ,false) ;
        }

        
#endregion


        ReturnCurrentPage
#region ReturnCurrentPage
        
private DataTable ReturnCurrentPage(DataTable curPage ,bool foward)
        
{
            
if(curPage == null)
            
{
                
return null ;
            }


            
if(foward)
            
{
                
++ this.curPageIndex ;            
            }

            
else
            
{
                
-- this.curPageIndex ;    
            }


            
this.preForward = foward ;
            
this.currentPage = curPage ;
            
this.ActivePageIndexChanged(this.curPageIndex) ;

            
return this.currentPage ;
        }

        
#endregion


        GetPage
#region GetPage
        
//如果历史记录中有对应的page,则返回它,否则返回null
        public DataTable GetPage(int index)
        
{
            
if(index > (this.statusStackBackWard.Count + this.statusStackForward.Count -1|| index < 0)
            
{
                
return null ;
            }


            
int distance = index - this.curPageIndex ;

            
if(distance == 0)
            
{
                
return this.currentPage ;
            }
            
            
else if(distance > 0)
            
{
                
for(int i=0 ;i<distance ;i++)
                
{
                    
this.NextPage() ;
                }


                
return this.currentPage ;
            }

            
else
            
{
                
for(int i=distance ;i<0 ;i++)
                
{
                    
this.PrePage() ;
                }


                
return this.currentPage ;
            }
                        
        }

        
#endregion


        ActivePageIndexChanged
#region ActivePageIndexChanged
        
private void ActivePageIndexChanged(int index)
        
{
            
if(this.CurrentPageIndexChanged != null)
            
{
                
this.CurrentPageIndexChanged(index) ;
            }

        }

        
#endregion


        
#endregion

    }


    
public class PageStatus
    
{
        
public string     curIDValueEnd    = "" ; //本页最后一条记录ID
        public string     preIDValueHead   = "" ; //上页第一条记录ID
        public DataTable  curTable = null ;
    }


    
public class PaginationParas
    
{
        
public string ConnectString = null ;
        
public string SelectString  = null ;
        
public string ComplexIDName = null ;    
        
public string DBTableName   = null ;
        
public int    PageSize      = 0 ;
        
public DataBaseType  DbType = DataBaseType.SqlServer ;
    }


    
public delegate void PageChanged(int pageIndex) ;

如果你使用XCodeFactory生成的数据层代码,可以像下面这样使用分页管理器:

string selectStr = "Select ID ,Title , UploadUserName ,UploadTime ,Description ,IsCasePic ,CaseID from BinaryInformationDetail  "  ;            
            
this.curPageMgr = DataEntrance.GetPaginationMgr(typeof(BinaryInformationDetail) ,selectStr ,"ID" ,5) ;            
            DataTable dtStart 
= this.curPageMgr.StartPage() ;            
                
this.DataList1.DataSource = dtPic ;
                
this.DataList1.DataBind() ;
 否则,你需要通过DataPaginationManager的第二个构造函数来使用分页管理器。要提出的是,DataPaginationManager不能随机跳转到未访问过的页面(紧邻的下一页除外),这是由我们的实现方式(一次仅仅读取一页)决定的,我并不觉得随机跳转到任意页面是一种很必要的操作!
posted on 2005-08-20 23:16  bluesky  阅读(321)  评论(0编辑  收藏  举报