导言

和默认翻页方式相比,自定义分页能提高几个数量级的效率。当我们的需要对大量数据分页的时候就需要考虑自定义分页,然而实现自定义分页相比默认分页需要做更多工作。对于排序自定义分页数据也是这样,在本教程中我们就会扩展前面的例子来实现自定义分页数据的排序。

注意:既然本教程是基于前一个的,因此我们需要把前面教程示例页面EfficientPaging.aspx的<asp:Content>元素中的代码复制到本教程SortParameter.aspx示例页面中。关于如何进行这样的复制操作请参看为删除数据添加客户端确认

Step 1: 回顾自定义分页技术

要实现自定义分页,我们需要使用一些方法根据开始行索引和最大行参数返回一个记录的子集。在前面的教程中,我们看了如何使用微软SQL SERVER 2005的ROW_NUMBER()来实现。简而言之,ROW_NUMBER()为每一个查询返回的行分配一个行号。下面这个查询演示了如何使用这个技术按照ProductName排序获取的11至20的产品数据。

对于按照一种固定的排序规则进行分页,上述技术就能满足了(比如按照ProductName排序),但是如果我们希望获取按照不同的排序表达式排序后的记录,理想地,我们应该在OVER子句中使用参数重写上述查询,代码如下:

SQL

1

2

3

4

5

SELECT ProductID, ProductName, ...

FROM

(SELECT ProductID, ProductName, ..., ROW_NUMBER() OVER (ORDER BY @sortExpression) AS RowRank

FROM Products) AS ProductsWithRowNumbers

WHERE RowRank > 10 AND RowRank <= 20

可惜,ORDER BY子句中不能使用参数。而我们只能创建存储过程来接受@sortExpression输入参数,使用如下任意一种方法:

  • 为所有的排序表达式硬编码查询,使用IF/ELSE T-SQL语句来决定执行哪个查询
  • 使用CASE语句来根据输入参数@sortExpression实现动态ORDER BY表达式,详细请看The Power of SQL CASE Statements中的Used to Dynamically Sort Query Results部分。
  • 使用字符串来保存查询语句然后使用sp_executesql系统存储过程来动态执行查询

上述每一种实现方法都有各自的缺点。第一个方案和其余两个相比可维护性比较差,因为它需要为每一个可能的查新表达式创建一句查询。因此,如果你又在GridView中加入了一个允许排序的字段,还需要去修改存储过程。对于第二个方案如果我们的数据库列不是字符串类型的话,排序就会引发一定的效率问题,而且可维护性和第一种一个一样也不是很好。至于最后一个动态组合SQL语句的方案,如果你允许用户自己输入参数并传入存储过程的话则可能带来SQL注入攻击的危害。

虽然没有一种方案是完美的,但是我认识第三种是这三个方案中最佳的。因为它是使用动态SQL语句的,所以灵活性比前两者都好。而且,只有当攻击者能随意把参数传入存储过程才能进行SQL注入攻击。既然DAL使用参数化查询,ADO.NET会防止这些恶意参数传入数据库,也就是说只有当攻击者人直接执行存储过程的时候才会有SQL注入的隐患。

要实现这个功能,让我们在Northwind数据库中新建称作GetProductsPagedAndSorted的一个存储过程。这个存储过程接受三个参数:@sortExpression,nvarchar(100)类型的输入参数,用来指定排序方式,它会直接拼接在ORDER BY子句后面。@startRowIndex 和 @maximumRows都是整数输入参数,和前面教程中的一样。你可以参考下面的脚本建立GetProductsPagedAndSorted存储过程:

代码

CREATE PROCEDURE dbo.GetProductsPagedAndSorted

(

@sortExpression nvarchar(100),

@startRowIndex int,

@maximumRows int

)

AS

-- 确保指定了 @sortExpression

IF LEN(@sortExpression) = 0

SET @sortExpression = 'ProductID'

-- 组合查询

DECLARE @sql nvarchar(4000)

SET @sql = 'SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued, CategoryName, SupplierName

FROM (SELECT ProductID, ProductName, p.SupplierID, p.CategoryID, QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued,

c.CategoryName, s.CompanyName AS SupplierName,

ROW_NUMBER() OVER (ORDER BY ' + @sortExpression + ') AS RowRank

FROM Products AS p

INNER JOIN Categories AS c ON

c.CategoryID = p.CategoryID

INNER JOIN Suppliers AS s ON

s.SupplierID = p.SupplierID) AS ProductsWithRowNumbers

WHERE     RowRank > ' + CONVERT(nvarchar(10), @startRowIndex) +

' AND RowRank <= (' + CONVERT(nvarchar(10), @startRowIndex) + ' + ' + CONVERT(nvarchar(10), @maximumRows) + ')'

-- 执行SQL查询

EXEC sp_executesql @sql

存储过程一开始先确保@sortExpression参数的值已经被指定。如果未被指定则按照ProductID排序。接下来,开始构建动态的SQL查询。注意到,在这里的动态SQL查询和前面的用来从Products表获取所有行的查询有些不同。在前面的例子中,我们使用子查询获取每一个产品关联的分类和供应商名。在GetProductsPagedAndSorted中我们只能使用JOINS因为结果需要根据分类或者供应商名来排序。

我们通过连接静态的查询语句和@sortExpression, @startRowIndex, @maximumRows参数来组成动态查询。因为@startRowIndex和@maximumRows是整数参数,所以必须在连接前把它们转化为nvarchar类型。在动态SQL查询连接完毕后就可以使用sp_executesql来执行。

先来花一些时间使用各种@sortExpression、@startRowIndex和@maximumRows参数的值来测试存储过程。在服务器资源管理器中右键点击存储过程然后选择执行。IDE会启动运行存储过程对话框,我们输入各种输入参数(见图1)。比如,要让结果按照分类名排序,就把@sortExpression参数的值设置为CategoryName;如果要按照公司名排序就用CompanyName。所有参数的值都正确设置后点击OK。结果就会在输出窗口中显示。图2显示了按照UnitPrice倒序,从11到20的记录。

图1:试着设置存储过程的三个输入参数

图2:存储过程的结果显示在了输入窗口中

Step 2: 添加数据访问和业务逻辑层

既然我们已经建立了GetProductsPagedAndSorted存储过程,下一步就是要通过我们的应用程序构架来执行它。我们需要为DAL和BLL添加一个正确的方法。首先让我们为DAL添加一个方法。打开Northwind.xsd强类型DataSet,右键点击ProductsTableAdapter,从菜单中选择添加查询选项。和前面教程中做的一样,我们需要配置一个新的DAL方法来使用建立的存储过程-GetProductsPagedAndSorted。选择使用已有存储过程选项。


 

图3:选择一个已有的存储过程

在下一步中,我们通过从下拉列表中选择GetProductsPagedAndSorted存储过程来使用它。

4:使用GetProductsPagedAndSorted存储过程

在下一屏幕中,我们选择它返回表格信息。

5:指示存储过程返回表格信息

最后,我们创建DAL方法来填充DataTable和返回DataTable,分别命名为FillPagedAndSorted和GetProductsPagedAndSorted。

6:选择方法名

现在,我们已经扩展了DAL,让我们来看看BLL吧。打开ProductsBLL类文件并且新增一个方法GetProductsPagedAndSorted。这个方法接受三个参数-sortExpression,startRowIndex和maximumRows。仅仅是简单地调用DAL的GetProductsPagedAndSorted方法,代码如下:

代码

[System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Select, false)]

public Northwind.ProductsDataTable GetProductsPagedAndSorted(string sortExpression, int startRowIndex, int maximumRows)

{

return Adapter.GetProductsPagedAndSorted(sortExpression, startRowIndex, maximumRows);

}

Step 3: 配置ObjectDataSource来传入SortExpression参数

好了,我们已经为DAL和BLL添加了方法来调用GetProductsPagedAndSorted存储过程。剩下的工作就是配置SortParameter.aspx页面的ObjectDataSource来根据用户请求的排序为新的BLL方法传入SortExpression参数。

首先,我们把ObjectDataSource的SelectMethod从GetProductsPaged修改为GetProductsPagedAndSorted。可以通过配置数据源向导的属性窗口来修改或者直接在声明代码中修改。下一步,我们需要提供ObjectDataSource的SortParameterName 属性。属性设置后,ObjectDataSource才会把GridView的SortExpression属性传入SelectMethod。特别地,ObjectDataSource会根据SortParameterName的值来寻找输入仓储,既然BLL中GetProductsPagedAndSorted方法的输入参数叫做sortExpression,我们这里的ObjectDataSource的SortExpression属性也应该设置为“sortExpression”。

在这两步修改后,ObjectDataSource的声明应该如下:

ASP.NET

1

2

3

<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" OldValuesParameterFormatString="original_{0}"

SelectMethod="GetProductsPagedAndSorted" TypeName="ProductsBLL" EnablePaging="True" SelectCountMethod="TotalNumberOfProducts" SortParameterName="sortExpression">

</asp:ObjectDataSource>

注意:和前面教程说的一样,请确保ObjectDataSource的SelectParameters集合中sortExpression、startRowIndex和maximumRows输入参数。

要让GridView开启排序,请首先检查Sorting多选框是否已经选中。把GridView的AllowSorting属性设置为true以后就能让每列的标题文字呈现为LinkButton。用户点击标题的LinkButton就会引发如下几个步骤:

1.       GridView把它的SortExpression 属性的值修改为当前点击的标题所在列的SortExpression的值。

2.       ObjectDataSource调用BLL的GetProductsPagedAndSorted方法,把GridView的SortExpression属性的值作为sortExpression参数传入方法(还有正确的startRowIndex、maximumRows输入参数的值)。

3.       BLL调用DAL的GetProductsPagedAndSorted方法。

4.       DAL执行GetProductsPagedAndSorted存储过程并传入@sortExpression参数(和@startRowIndex、@maximumRows输入参数)。

5.       存储过程把正确的记录子集数据返回BLL,BLL返回到ObjectDataSource;数据被绑定到GridView之后渲染成HTML显示给用户。

图7显示了按照UnitPrice正序排列地第一页记录集。

7:按照UnitPrice排列的果

虽然现在我们的程序能正确按照产品名、分类名、位数量和价格进行排序,但是如果我们选择按照供应商名来排序会得到一个运行时异常,如图8。

8:按照供应商名排序会得到一个运行时异常

之所以会引发这个异常时因为GridView的SupplierName BoundField绑定列的SortExpression设置为SupplierName。然而,这列在供应商表中实际叫做CompanyName,SupplierName是我们为这个列起的别名。因为ROW_NUMBER()功能只能使用真实列名,所以,我们需要把BoundField的SortExpression从“SupplierName”修改为“CompanyName”(如图9),图10显示了修改后按照供应商排序的记录。

图9:把SupplierName BoundField的SortExpression修改为“CompanyName” (译者注:图片可能不对)

图10:结果现在能按照供应商名排序了

总结

前面教程中我们实现了自定义分页,只能在设计时固定一种排序方式。简单来说要想又自定义分页又提供自定义排序实现不了。在本教程中,我们通过引入@sortExpression来扩展存储过程解决了这个限制。

在创建了存储过程和DAL、BLL中的新方法后,我们就能通过配置ObjectDataSource把GridView当前SortExpression的值传入BLL的SelectMethod中来实现排序和自定义分页。

转载于:https://www.cnblogs.com/Fskjb/archive/2010/02/24/1672403.html

ASP.NET 2.0数据教程之二十六::排序自定义分页数据相关推荐

  1. 2021年大数据ELK(二十六):探索数据(Discovery)

    全网最详细的大数据ELK文章系列,强烈建议收藏加关注! 新文章都已经列出历史文章目录,帮助大家回顾前面的知识重点. 目录 探索数据(Discovery) 一.使用探索数据功能 二.导入更多的Apach ...

  2. 【Vue2.0】—vue-router(二十六)

    [Vue2.0]-vue-router(二十六) 一.vue-router 的理解 它是vue 的一个插件库,专门用来实现 SPA 应用 二.对 SPA 应用的理 单页 Web 应用(single p ...

  3. 2021年大数据Hadoop(二十六):YARN三大组件介绍

    全网最详细的Hadoop文章系列,强烈建议收藏加关注! 后面更新文章都会列出历史文章目录,帮助大家回顾知识重点. 目录 本系列历史文章 前言 Yarn三大组件介绍 ResourceManager No ...

  4. SAP UI5 初学者教程之二十六 - OData 服务配合 Mock 服务器的使用步骤详解试读版

    一套适合 SAP UI5 初学者循序渐进的学习教程 教程目录 SAP UI5 本地开发环境的搭建 SAP UI5 初学者教程之一:Hello World SAP UI5 初学者教程之二:SAP UI5 ...

  5. 计算机网络教程网线制作,图吧小白教程 篇二十六:手把手教你自制网线(夹网线水晶头)...

    图吧小白教程 篇二十六:手把手教你自制网线(夹网线水晶头) 2019-11-19 23:07:38 31点赞 309收藏 27评论 创作立场声明:咕咕咕 教程最后还是出了,不过咱现在用啥还是直接网购号 ...

  6. Scott Mitchell 的ASP.NET 2.0数据教程之二十四:: 分页和排序报表数据

    下载代码示例 | 下载PDF格式英文教程 导言 分页和排序是在WEB应用程序中展现数据常见的功能.比如,当我们在一个网上书店搜索ASP.NET书籍的时候,可能有几百本相关书籍,但是我们只希望每页显示1 ...

  7. ASP.NET 2.0数据教程之二十四:: 分页和排序报表数据

    导言 分页和排序是在WEB应用程序中展现数据常见的功能.比如,当我们在一个网上书店搜索ASP.NET书籍的时候,可能有几百本相关书籍,但是我们只希望每页显示10条有效记录.而且,我们还希望结果能根据标 ...

  8. SAP UI5 应用开发教程之二十六 - OData 服务配合 Mock 服务器的使用步骤详解

    从本教程 24 步骤开始,我们接触了 OData 模型.本地学习 SAP UI5 应用开发时,如果直接消费远端的 OData 服务,就会遇到跨域错误. 步骤 24 SAP UI5 初学者教程之二十四 ...

  9. Android开发笔记(一百二十六)自定义音乐播放器

    MediaRecorder/MediaPlayer 在Android手机上面,音频的处理比视频还要复杂,这真是出人意料.在前面的博文< Android开发笔记(五十七)录像录音与播放>中, ...

最新文章

  1. php yii2支付宝开发,yii2 支付宝支付教程 [ 2.0 版本 ]
  2. redis主从复制,读写分离
  3. qduoj - WHY吃糖果(二分套二分)
  4. 前端学习(3175):react-hello-react之解决跨域问题
  5. 支付宝客户端架构分析:自动化日志收集及分析
  6. Codevs 1205 单词反转(Vector以及如何输出string)
  7. 并发的线程入门到并发安全原理解析,offer拿到手软
  8. 创建型设计模式(1)—— 单例模式(Singleton Pattern)
  9. 12c emcc Algorithm negotiation fail
  10. 记录ubuntu18.04安装QQ过程
  11. 小程序优购商城项目总结
  12. Aras Innovator: Catagoy, Itemtype, Item, Relationship的视图
  13. 解决 Elasticsearch 查询时 Fielddata is disabled on text fields by default 错误
  14. godot 外部编辑器配置
  15. 【胡侃系列】基于多元回归模型的双十一购物狂欢节天猫商城销售额预测
  16. 科大奥瑞物理实验——光纤传感器实验
  17. 请编程实现输出自然数1到1000中含有数字2的数的个数。
  18. 实施工程师面常见问题
  19. cent os 7 与cent os 6区别
  20. 为什么计算机32到64位,为你解答win764位和32位有什么区别

热门文章

  1. 职场励志小故事——【强盗师徒】
  2. LCD1602实现两屏切换
  3. 表空间main恢复后报脱机状态[-3408]
  4. 广州大学计算机学院刘文斌,​吴文波
  5. 超级app当道,发现他们有一个共同的技术线路
  6. 小程序生态加持下,轻松构建超级App
  7. 专访陈庆吉:心怀敬畏之心,麒麟贵金属要做跨界融合典范
  8. 二维码生成图片 canvas.toDataURL()降低图片质量,以减少图片上传大小
  9. C++类和对象(中)(6个默认成员函数)
  10. 日薪一斤包谷与6000万年薪