ListView控件本身并没有分页功能,不过借助于ASP.NET中新增加的DataPager控件,我们可以非常方便地对ListView中的数据设置分页,这几乎不需要开发人员写一行代码,将ListView控件放到页面上,设置好布局和DataSource,然后再添加一个DataPager控件,将它的PagedControlID属性设置成ListView的ID,PageSize中设置每页要显示的数据条数,然后在Fields中设置好分页的样式(当然你完全可以不用去管样式,ASP.NET会根据内置的样式来定义分页UI),运行Web程序,你就会看到一个支持分页的ListView页面,整个过程非常简单。在ASP.NET 3.5中,微软将数据绑定控件和分页控件分离开来了,这样用户就可以在页面的任何地方设置分页,并根据自己的需要定义各种不同的分页样式,同时,分页控件可以控制任意一个数据绑定控件,它们之间通过PagedControlID属性来指定,基本上对于分页操作,开发人员可以随心所欲了。如何使用ListView控件和DataPager控件不是本文的重点,感兴趣的读者可以去查一下微软的MSDN,我想它应该比我讲得要详细得多。
先说说数据分页的原理。我们在开发数据绑定页面的Web应用程序时,常常会遇到数据量比较多的情况,为了防止页面变得过大加载数据慢的问题,大家都会将一个页面上要显示的数据通过分页来完成,用户访问页面时通过分页功能来查看不同页面中的数据,这是一个非常好的解决办法,而且几乎所有的程序设计人员和开发人员都会不约而同乐此不彼地采用分页的方式来显示页面上的数据,这没有什么问题! 问题在于分页的方式。
一般情况下,最简单的实现方法是一次性将所有页面的数据读到缓存媒介中(这个媒介一般都是服务器的内存),然后每次只显示一页的数据。这种方式实现起来很容易,而且ASP.NET之前几乎所有的支持分页的数据绑定控件都是采用的这种方式,以至于很多ASP.NET的初学者都采用了这样的方式来开发分页数据绑定页面,并且没有觉察出任何问题。是的,程序开发中采用最简单有效的方法一般都是不会出现什么问题的,况且微软提供的标准控件都是这样做的,会有什么问题呢?对于一些小的Web应用程序而言,这确实没有什么问题,因为它涉及到的数据量比较小,即使我们将所有的数据都读到内存中,充其量也才几兆,多一点十几兆,几十兆。如果这些数据都是纯文本的话(一般而言我们保存在数据库中的数据都是文本信息),几十兆的数据已经是成千上万条记录了,现在的服务器硬件条件都比较好,内存都在G级以上,处理这点数据根本不在话下。但是,如果数据库中的一个表的记录达到上亿条,并且有些字段存储的是文件数据(也就是二进制数据),这样一次性将所有的数据读到内存中就不是一个理想的做法了,这个时候就需要采用“真分页”方式读取数据。
大部分情况下,我们还是需要采用“真分页”的方式来获取数据的。给定每页记录的起始位置(或者页面的索引),再给定一个每页显示的数据的条数和总记录数,我们希望每次取到的只是当前页面的数据。每次当用户分页时,根据这些条件从数据库中取一部分数据绑定到页面上,这样可以大大减少服务器的开销,并且再大的数据量也不是问题。这种方式似乎是理想的,然而结合用户的需求,我们会发觉即使采用“真分页”方式对数据进行分页获取,也还是会遇到问题。试想,在当今Ajax横行的Web世界里,利用Ajax方式改善用户体验的站点层出不穷,如果你恰好有一个采用Ajax方式提供的分页数据绑定页面,那问题就会出现了。由于Ajax的用户体验效果是页面的局部刷新,在分页数据绑定页面中,用户点击分页按钮后页面会以较快的速度更新分页后的数据,这个体验对用户来说是相当不错的,但是贪婪的用户有可能会想试试频繁地点击分页按钮,甚至于疯狂的用户狂点分页按钮,这个时候你的应用程序由于需要非常频繁地去数据库中获取分页数据而来不及更新页面上的数据而出现脚本错误,最终给用户的体验就是页面的分页功能不正常,程序崩溃了。
采用“真分页”和“假分页”相结合的方式可以很有效得解决上面提到的这个问题。我将上面提到的第一种数据分页方式称之为“假分页”,而将第二种数据分页方式称之为“真分页”。这两种分页方式的结合,就是说一次性读取n页的数据到缓存中,分页时根据需要判断是否从缓存中直接获取数据还是重新从数据库中加载数据到缓存里。毕竟,从缓存中加载数据效率要高得多。这样,每次用户点击分页按钮时,只要数据存在于缓存里,就可以以非常快的速度加载数据,如果缓存过期或者用户要获取的数据超出了缓存,就从数据库中重新加载新的n页数据到缓存中。当然,更新缓存的过程你可以在Ajax中采用同步采用,以限制用户在这个过程中的UI操作。