分页显示是
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 条记录,如果不使用自定义分页,那么每次需读取近万条数据!大大节省了每一次的通讯量。
源代码下载,包含生成存储过程的T-SQL,注意修改一下web.config中的连接字符串值,文件夹Web是网站项目,其他为类库项目,新建解决方案添加一下:
PagingData.rar
PagingData.rar
原创文章,希望对大家有所帮助,不当之处请及时指正:)
数据访问相关教程,很多人推荐过了,还是值得在推荐一下:
英文不是很难,图文并茂,还有
VB
和
C#
源代码
Feedback
只有数据源支持IEnumerable接口才可以使用GridView的排序功能如DataView、DataTable和DataSet。但即使把读取到的数据转换到以上三种类型,排序也仅仅是针对当前页的数据。
可以通过增加一个排序参数来实现,修改一下存储过程:
CREATE
PROC [Sales].[GetOrderHeaderList]
(
@shipMethodID INT,
@sortField NVARCHAR(4000),
@maximumRows INT,
@startRowIndex INT
)
AS
SET
NOCOUNT ON
SELECT
SalesOrderID
,OrderDate
,DueDate
,SubTotal
,TaxAmt
,Freight
,TotalDue
FROM (SELECT ROW_NUMBER()
OVER (ORDER BY CASE @sortField
WHEN 'SubTotal' THEN SubTotal
WHEN 'TaxAmt' THEN TaxAmt
WHEN 'Freight' THEN Freight
WHEN 'TotalDue' THEN TotalDue
END) Row
,SalesOrderID
,OrderDate
,DueDate
,SubTotal
,TaxAmt
,Freight
,TotalDue
FROM Sales.SalesOrderHeader
WHERE ShipMethodID=@shipMethodID)SalesOrder
WHERE Row>@startRowIndex AND ROW<=@startRowIndex+@maximumRows
在
GridView
的
Sorting
事件中传入参数即可