分页显示是ASP.Net中常常用到的一个功能,不但能提供更好的用户体验,而且更重要的是可以大大减少网络通讯量,因为每一次页面加载只需要读取“一页”的数据量。但是ASP.Net 2.0中的GridView虽然可以通过把AllowPaging属性设为True来进行分页显示:
但如果没有定义支持分页检索数据的方法的话,这只是在显示上的分页,客户端还是一次性载入了全部数据,并没有达到减少网络负担。
要实现真正的分页显示就需要结合ObjectDataSource控件来“自定义”,注意ObjectDataSource的这几个属性:
EnablePaging:ObjectDataSource的SelectCommand是否支持分页
MaximumRowsParameterName和StartRowIndexParameterName:是指SelectCommand所指定的方法中用于分页的两个参数名称,前者用于指定每页的记录数,后者用于指定每页开始行的索引值。
这两个值是可以根据具体调用的方法参数来更改,上图中显示的是默认值。
SelectCountMehtod:用于指定获得记录总行数的方法,要不怎么知道有多少页啊:)
流程:
大体上数据从数据源(如SQL Server)到页面显示的流程如下图所示:
“自定义”就是要从底层一步步做起,以SQL Server 2005示例数据库AdventureWorks为例,现在要实现这样一个功能:根据ShipMethodID分页检索Sales.SalesOrderHeader表中的数据。
一、存储过程
在SQL Server 2005中使用ROW_NUMBER()函数可以直接得到行数,相比较SQL Server 2000中子查询的方法,不仅更加简便而且大大提高了执行效率。
--获得当前页的表数据
CREATE PROC [Sales].[GetOrderHeadeList]
(
@shipMethodID INT,
@maximumRows INT,
@startRowIndex INT
)
AS
SET NOCOUNT ON
SELECT SalesOrderID
,OrderDate
,DueDate
,SubTotal
,TaxAmt
,Freight
,TotalDue
FROM (SELECT ROW_NUMBER() OVER (ORDER BY OrderDate DESC) Row
,SalesOrderID
,OrderDate
,DueDate
,[Status]
,TaxAmt
,Freight
,TotalDue
FROM Sales.SalesOrderHeader
WHERE ShipMethodID=@shipMethodID) SalesOrder
WHERE Row>@startRowIndex AND ROW<=@startRowIndex+@maximumRows
还需要一个存储过程来获得总行数:
--获得总记录数
CREATE PROC [Sales].[GetTotalNumberOfOrderHeader]
(
@shipMethodID INT
)
AS
SET NOCOUNT ON
SELECT COUNT(*)
FROM Sales.SalesOrderHeader
WHERE ShipMethodID=@shipMethodID
二、数据访问层对象
调用存储过程读取数据,这里没什么特别的地方:
public class OrderHeader:IOrderHeader
{
#region IOrderHeader 成员
public List<OrderHeaderInfo> GetOrderHeaderList(int shipMethodID, int maximumRows, int startRowIndex)
{
SqlConnection conn = new SqlConnection
(ConfigurationManager.ConnectionStrings["AdventureWorksConnectionString"].ConnectionString);
SqlCommand cmd = new SqlCommand("Sales.GetOrderHeaderList", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@shipMethodID", SqlDbType.Int).Value = shipMethodID;
cmd.Parameters.Add("@maximumRows", SqlDbType.Int).Value = maximumRows;
cmd.Parameters.Add("@startRowIndex", SqlDbType.Int).Value = startRowIndex;
List<OrderHeaderInfo> list=new List<OrderHeaderInfo>();
conn.Open();
SqlDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
list.Add(new OrderHeaderInfo(dr.GetInt32(0), dr.GetDateTime(1), dr.GetDateTime(2),
dr.GetDecimal(3), dr.GetDecimal(4), dr.GetDecimal(5), dr.GetDecimal(6)));
}
dr.Close();
conn.Close();
return list;
}
public int GetTotalNumberOfOrderHeader(int shipMethodID)
{
SqlConnection conn = new SqlConnection
(ConfigurationManager.ConnectionStrings["AdventureWorksConnectionString"].ConnectionString);
SqlCommand cmd = new SqlCommand("Sales.GetTotalNumberOfOrderHeader", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@shipMethodID", SqlDbType.Int).Value = shipMethodID;
conn.Open();
int result = (int)cmd.ExecuteScalar();
conn.Close();
return result;
}
#endregion
}
三、 业务逻辑层对象
通过工厂方法获得数据访问层实例,调用读取方法:
public class OrderHeader
{
private IOrderHeader orderHeader = DataAccess.CreateOrderHeader();
public List<OrderHeaderInfo> GetOrderHeaderList(int shipMethodID, int maximumRows, int startRowIndex)
{
return orderHeader.GetOrderHeaderList(shipMethodID, maximumRows, startRowIndex);
}
public int GetTotalNumberOfOrderHeader(int shipMethodID, int maximumRows, int startRowIndex)
{
return orderHeader.GetTotalNumberOfOrderHeader(shipMethodID);
}
}
注意GetTotalNumberOfOrderHeader这个方法,即ObjectDataSource控件的SelectCountMethod要指定的值,该值为方法名称且不能指定参数,所以该方法的参数应与SelectCommand所指定的方法参数相一致,否则运行时会抛出异常。
四、 表示层(这里指ASP.Net页面)
GridView的设置很简单,最基本的,把AllowPaging设为True即可:
ObjectDataSource控件除了设置SelectMothod方法外,特别注意对开头所说的四个属性的设置:
EnablePaging设为True,指定SelectCountMethod方法。
运行结果
使用断点调试,会发现每次只从数据库读取10条记录,如果不使用自定义分页,那么每次需读取近万条数据!大大节省了每一次的通讯量。
PagingData.rar
原创文章,希望对大家有所帮助,不当之处请及时指正:)
数据访问相关教程,很多人推荐过了,还是值得在推荐一下:
http://www.asp.net/Learn/DataAccess/
英文不是很难,图文并茂,还有VB和C#源代码