在DataGrid的web版控件中提供了自动分页的功能,但是我从来没用过它,因为它实现的分页只是一种假相。我们为什么需要分页?那是因为符合条件的记录可能很多,如果一次读取所有的记录,不仅延长获取数据的时间,而且也极度浪费内存。而分页的存在的主要目的正是为了解决这两个问题(当然,也不排除为了UI美观的需要而使用分页的)。而web版的DataGrid是怎样实现分页的了?它并没有打算解决上述两个问题,而还是一次读取所有的数据,然后以分页的样子表现出来。这是对效率和内存的极大损害!
于是我自己写了一个分页管理器,关于它的描述和实现如下所示:

/**////<summary>
///IDataPaginationManager用于实现数据查询的分页操作。
///当表中的数据记录很多时,用Apdater一次读出所有的数据即耗费时间又浪费内存,这时就要用到分页了。
///DataPaginationManager每次从数据库中读取指定的一页,并且把历史页缓存在Stack中,这样,如果再次访问历史页,
///就不用再访问数据库了,直接从Stack中取出即可。
///
///作者:朱伟sky.zhuwei@163.com
///</summary>
publicinterfaceIDataPaginationManager


{
//complexIDName如"ID"或"sta.ID"(用于复合查询)
//selectStr中不允许包括GroupBy和OrderBy等字段
//voidInitialize(IDBAccesseraccesser,stringselectStr,stringcomplexID_Name);


intItemCount
{get;}

intPageCount
{get;}

intCurrentPageIndex
{get;}
DataTableStartPage();
DataTableNextPage();
DataTablePrePage();

DataTableCurrentPage
{get;}
DataTableGetPage(intindex);//只能随机访问曾经读取过的页

eventPageChangedCurrentPageIndexChanged;
}


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


{
privatePaginationParascurParas=null;
privateIADOBaseadoBase=null;
privateintpageCount=0;
privateintitemCount=0;

privateDataTablecurrentPage=null;
privateintcurPageIndex=0;

//用Stack来存储历史页,以实现前一页操作
privateStackstatusStackForward=newStack();
privateStackstatusStackBackWard=newStack();
privateboolpreForward=true;//上一次是正向?

publiceventPageChangedCurrentPageIndexChanged;


IDataPagination成员#regionIDataPagination成员
publicDataPaginationManager(IDBAccesseraccesser,stringselectStr,stringcomplexID_Name,intpage_size)


{
this.curParas=newPaginationParas();
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();
}

publicDataPaginationManager(PaginationParasparas)


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


private#regionprivate
privatevoidInitializeAdoBase(DataBaseTypedbType,stringconnStr)


{
switch(dbType)


{
caseDataBaseType.SqlServer:


{
this.adoBase=newSqlADOBase(connStr);
break;
}
caseDataBaseType.Ole:


{
this.adoBase=newOleADOBase(connStr);
break;
}
default:


{
thrownewException("ThetargetDataBaseTypeisnotimplemented!");
}
}
}

privatestringCheckSelectString(stringselectStr)


{
if((selectStr==null)||(selectStr==""))


{
thrownewException("SelectStrisinvalid!");
}

stringstr=selectStr.ToLower();
if((str.IndexOf("orderby")!=-1)||(str.IndexOf("groupby")!=-1))


{
thrownewException("SelectStrCan'tcontain'orderby'or'groupby'!");
}

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

privatestringConstructSelectString(boolfirst,boolforward,PageStatuscurSta)


{
if(first)


{
returnthis.curParas.SelectString;
}


stringcomp=">=";
stringcurIDValue=curSta.preIDValueHead;
if(forward)


{
comp=">";
curIDValue=curSta.curIDValueEnd;
}

if(-1==this.curParas.SelectString.IndexOf("where"))


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

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

privateintGetPageCount()


{
stringstr=null;
intindex=this.curParas.SelectString.IndexOf("where");
if(-1==index)


{
str=string.Format("SelectCount(*)from{0}",this.curParas.DBTableName);
}
else


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

DataSetds=this.adoBase.DoQuery(str);

if(ds.Tables[0].Rows.Count!=0)


{
intnum=int.Parse(ds.Tables[0].Rows[0][0].ToString());
this.itemCount=num;
intpageCount=num/this.curParas.PageSize;
if(num%this.curParas.PageSize>0)


{
pageCount+=1;
}

returnpageCount;
}

this.itemCount=0;
return0;
}
#endregion


PageCount,CurrentPageIndex,CurrentPage#regionPageCount,CurrentPageIndex,CurrentPage
publicintPageCount


{
get


{
returnthis.pageCount;
}
}

publicintItemCount


{
get


{
returnthis.itemCount;
}
}

publicintCurrentPageIndex


{
get


{
returnthis.curPageIndex;
}
}

publicDataTableCurrentPage


{
get


{
returnthis.currentPage;
}
}
#endregion


StartPage#regionStartPage
publicDataTableStartPage()


{
if(this.pageCount==0)


{
returnnull;
}

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

stringselect=this.ConstructSelectString(true,true,null);
DataSetds=this.adoBase.DoQuery(select);

PageStatussta=newPageStatus();
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);

returnthis.currentPage;
}
#endregion


NextPage#regionNextPage
publicDataTableNextPage()


{
if(this.curPageIndex>=this.pageCount-1)


{
returnnull;
}

if(this.statusStackBackWard.Count>0)


{
PageStatusstaRes=(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);
}
}

returnthis.ReturnCurrentPage(staRes.curTable,true);
}

PageStatuscurSta=(PageStatus)this.statusStackForward.Peek();
stringselect=this.ConstructSelectString(false,true,curSta);
DataSetds=this.adoBase.DoQuery(select);

PageStatussta=newPageStatus();
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);

returnthis.ReturnCurrentPage(sta.curTable,true);
}
#endregion


PrePage#regionPrePage
publicDataTablePrePage()


{
if(this.curPageIndex<1)


{
returnnull;
}

PageStatusoldSta=(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);
}
}

returnthis.ReturnCurrentPage(oldSta.curTable,false);
}
#endregion


ReturnCurrentPage#regionReturnCurrentPage
privateDataTableReturnCurrentPage(DataTablecurPage,boolfoward)


{
if(curPage==null)


{
returnnull;
}

if(foward)


{
++this.curPageIndex;
}
else


{
--this.curPageIndex;
}

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

returnthis.currentPage;
}
#endregion


GetPage#regionGetPage
//如果历史记录中有对应的page,则返回它,否则返回null
publicDataTableGetPage(intindex)


{
if(index>(this.statusStackBackWard.Count+this.statusStackForward.Count-1)||index<0)


{
returnnull;
}

intdistance=index-this.curPageIndex;

if(distance==0)


{
returnthis.currentPage;
}
elseif(distance>0)


{
for(inti=0;i<distance;i++)


{
this.NextPage();
}

returnthis.currentPage;
}
else


{
for(inti=distance;i<0;i++)


{
this.PrePage();
}

returnthis.currentPage;
}
}
#endregion


ActivePageIndexChanged#regionActivePageIndexChanged
privatevoidActivePageIndexChanged(intindex)


{
if(this.CurrentPageIndexChanged!=null)


{
this.CurrentPageIndexChanged(index);
}
}
#endregion

#endregion
}

publicclassPageStatus


{
publicstringcurIDValueEnd="";//本页最后一条记录ID
publicstringpreIDValueHead="";//上页第一条记录ID
publicDataTablecurTable=null;
}

publicclassPaginationParas


{
publicstringConnectString=null;
publicstringSelectString=null;
publicstringComplexIDName=null;
publicstringDBTableName=null;
publicintPageSize=0;
publicDataBaseTypeDbType=DataBaseType.SqlServer;
}

publicdelegatevoidPageChanged(intpageIndex);
如果你使用XCodeFactory生成的数据层代码,可以像下面这样使用分页管理器:
stringselectStr="SelectID,Title,UploadUserName,UploadTime,Description,IsCasePic,CaseIDfromBinaryInformationDetail";
this.curPageMgr=DataEntrance.GetPaginationMgr(typeof(BinaryInformationDetail),selectStr,"ID",5);
DataTabledtStart=this.curPageMgr.StartPage();
this.DataList1.DataSource=dtPic;
this.DataList1.DataBind();
否则,你需要通过DataPaginationManager的第二个构造函数来使用分页管理器。要提出的是,DataPaginationManager不能随机跳转到未访问过的页面(紧邻的下一页除外),这是由我们的实现方式(一次仅仅读取一页)决定的,我并不觉得随机跳转到任意页面是一种很必要的操作!