主頁(yè) > 知識(shí)庫(kù) > 在ASP.NET 2.0中操作數(shù)據(jù)之二十五:大數(shù)據(jù)量時(shí)提高分頁(yè)的效率

在ASP.NET 2.0中操作數(shù)據(jù)之二十五:大數(shù)據(jù)量時(shí)提高分頁(yè)的效率

熱門標(biāo)簽:外呼電話機(jī)器人成本 百應(yīng)電話機(jī)器人外呼系統(tǒng) 400電話辦理怎么樣 地圖標(biāo)注軟件免費(fèi)下載 蘇州如何辦理400電話 網(wǎng)絡(luò)電話外呼系統(tǒng)上海 聯(lián)通官網(wǎng)400電話辦理 西寧呼叫中心外呼系統(tǒng)線路商 臨沂智能電話機(jī)器人加盟

導(dǎo)言

  如我們?cè)谥暗慕坛汤镉懻摰哪菢?分頁(yè)可以通過(guò)兩種方法來(lái)實(shí)現(xiàn):

  1.默認(rèn)分頁(yè)– 你僅僅只用選中data Web control的 智能標(biāo)簽的Enable Paging ; 然而,當(dāng)你瀏覽頁(yè)面的時(shí)候,雖然你看到的只是一小部分?jǐn)?shù)據(jù),ObjectDataSource 還是會(huì)每次都讀取所有數(shù)據(jù)

  2.自定義分頁(yè)– 通過(guò)只從數(shù)據(jù)庫(kù)讀取用戶需要瀏覽的那部分?jǐn)?shù)據(jù),提高了性能. 顯然這種方法需要你做更多的工作.

  默認(rèn)的分頁(yè)功能非常吸引人,因?yàn)槟阒恍枰x中一個(gè)checkbox就可以完成了.但是它每次都讀取所有的數(shù)據(jù),這種方式在大數(shù)據(jù)量或者并發(fā)用戶多的情況下就不合適.在這樣的情況下,我們必須通過(guò)自定義分頁(yè)來(lái)使系統(tǒng)達(dá)到更好的性能.

  自定義分頁(yè)的一個(gè)重點(diǎn)是要寫(xiě)一個(gè)返回僅僅需要的數(shù)據(jù)的查詢語(yǔ)句.幸運(yùn)的,Microsoft SQL Server 2005 提供了一個(gè)新的keyword,通過(guò)它我們可以寫(xiě)出讀取需要的數(shù)據(jù)的查詢.在本教程里,我們將學(xué)習(xí)在GridView里如何使用Microsoft SQL Server 2005 的這個(gè)新的keyword來(lái)實(shí)現(xiàn)自定義分頁(yè).自定義分頁(yè)和默認(rèn)分頁(yè)的界面看起來(lái)一樣,但是當(dāng)你從一頁(yè)轉(zhuǎn)到另一頁(yè)時(shí),在效率上差了幾個(gè)數(shù)量級(jí).

注意:自定義分頁(yè)帶來(lái)的性能提升程序取決于數(shù)據(jù)的總量和數(shù)據(jù)庫(kù)的負(fù)載.在本教程的最后我們會(huì)用數(shù)據(jù)來(lái)說(shuō)明自定義分頁(yè)帶來(lái)的性能方面的好處.

第一步: 理解自定義分頁(yè)的過(guò)程

  給數(shù)據(jù)分頁(yè)的時(shí)候,頁(yè)面顯示的數(shù)據(jù)取決于請(qǐng)求的是哪一頁(yè)和每頁(yè)顯示多少條.比如,想象以下我們給81個(gè)product分頁(yè),每頁(yè)顯示10條.當(dāng)我們?yōu)g覽第一頁(yè)時(shí),我們需要的是product 1 到 product 10.當(dāng)瀏覽第二頁(yè)時(shí),我們需要的是product 11 到 product 20,以次類推.

  對(duì)于需要讀取什么數(shù)據(jù)和分頁(yè)的頁(yè)面怎么顯示,有三個(gè)相關(guān)的變量:

  1.Start Row Index – 頁(yè)面里顯示數(shù)據(jù)的第一行的索引; 這個(gè)值可以通過(guò)頁(yè)的索引乘每頁(yè)顯示的記錄的條數(shù)加1得到. 例如, 如果一頁(yè)顯示10條數(shù)據(jù), 那么對(duì)第一頁(yè)來(lái)說(shuō)(第一頁(yè)的索引為0), 第一行的索引為0 * 10 + 1, or 1; 對(duì)第二頁(yè)來(lái)說(shuō)(索引為1), 第一行的索引為1 * 10 + 1, 即 11.

 ?。?Maximum Rows – 每頁(yè)顯示的最多記錄的條數(shù). 之所以稱為“maximum” rows 是由于最后一頁(yè)顯示的數(shù)據(jù)可能會(huì)比page size要小. 比如, 當(dāng)以每頁(yè)10條記錄來(lái)顯示81條時(shí), 最后一頁(yè)也就是第九頁(yè)只包含一條記錄. 沒(méi)有頁(yè)面顯示的記錄條數(shù)會(huì)大于Maximum Rows 的值.

 ?。?Total Record Count – 顯示數(shù)據(jù)的總條數(shù). 不需要知道頁(yè)面顯示什么數(shù)據(jù),但是記錄總數(shù)會(huì)影響到分頁(yè). 比如, 如果對(duì)81條product記錄分頁(yè),每頁(yè)10條,那么總頁(yè)數(shù)為9.

  對(duì)默認(rèn)分頁(yè)來(lái)說(shuō),Start Row Index是由頁(yè)索引和每頁(yè)的記錄數(shù)加1得到,Maximum Rows 就是每頁(yè)的記錄數(shù).使用默認(rèn)分頁(yè)時(shí),不管是呈現(xiàn)哪頁(yè)的數(shù)據(jù),都是要讀取全部的數(shù)據(jù),所有每行的索引都是已知的,這樣獲取Start Row Index變的沒(méi)有價(jià)值.而且,記錄的總條數(shù)是可以通過(guò)DataTable的總條數(shù)來(lái)獲取的.

  自定義分頁(yè)只返回從Start Row Index 開(kāi)始的Maximum Rows條記錄.在這里有兩個(gè)要注意的地方:

  1.我們必須把整個(gè)要分頁(yè)的數(shù)據(jù)和一個(gè)row index關(guān)聯(lián)起來(lái),這樣才能從指定的Start Row Index 開(kāi)始返回需要的數(shù)據(jù).

 ?。?我們需要提供用來(lái)分頁(yè)的數(shù)據(jù)的總條數(shù).

  在后面的兩步里我們將寫(xiě)出和上面兩點(diǎn)相關(guān)的SQL.除此之外,我們還將在DAL和BLL里完成相應(yīng)的方法.

第二步: 返回需要分頁(yè)的記錄的總條數(shù)

  在我們學(xué)習(xí)如何返回顯示頁(yè)面需要的數(shù)據(jù)之前,我們先來(lái)看看怎么獲取數(shù)據(jù)的總條數(shù).因?yàn)樵谂渲媒缑娴臅r(shí)候需要用到這個(gè)信息.我們使用SQL的COUNT aggregate function來(lái)實(shí)現(xiàn)這個(gè).比如,返回Products表的總記錄條數(shù),我們可以用如下的語(yǔ)句:

SELECT COUNT(*)
FROM Products

我們?cè)贒AL里添加一個(gè)方法來(lái)返回這個(gè)信息.這個(gè)方法名為TotalNumberOfProducts() ,它會(huì)執(zhí)行上面的SQL語(yǔ)句.

  打開(kāi)App_Code/DAL 文件夾里的 Northwind.xsd .然后在設(shè)計(jì)器里右鍵點(diǎn)ProductsTableAdapter ,選擇Add Query.和我們?cè)谝郧暗慕坛汤飳W(xué)習(xí)的那樣,這樣會(huì)允許我們添加一個(gè)新的DAL方法,這個(gè)方法被調(diào)用時(shí)會(huì)執(zhí)行指定的SQL或存儲(chǔ)過(guò)程.和前面的TableAdapter 方法一樣,為這個(gè)添加一個(gè)SQL statement.

圖 1: 使用 SQL Statement

在下一個(gè)窗體我們可以指定創(chuàng)建哪種SQL .由于查詢只返回一個(gè)值–Products表的總記錄條數(shù)–我們選擇“SELECT which returns a singe value”.

圖 2: 使用 SELECT Statement that Returns a Single Value來(lái)配置SQL

下一步是寫(xiě)SQL語(yǔ)句.

圖 3: 使用SELECT COUNT(*) FROM Products 語(yǔ)句

最后給這個(gè)方法命名為TotalNumberOfProducts.

圖 4: 將方法命名為 TotalNumberOfProducts

  點(diǎn)擊結(jié)束后,DAL里添加了一個(gè)TotalNumberOfProducts方法.這個(gè)方法返回的值可為空,而Count語(yǔ)句總是返回一個(gè)非空的值.
我們還需要在BLL中加一個(gè)方法.打開(kāi)ProductsBLL類文件,添加一個(gè)TotalNumberOfProducts方法,這個(gè)方法要做的只是調(diào)用DAL的TotalNumberOfProducts方法.

public int TotalNumberOfProducts()
{
 return Adapter.TotalNumberOfProducts().GetValueOrDefault();
}

  DAL的TotalNumberOfProducts方法返回一個(gè)可空的整型,而需要ProductsBLL類的TotalNumberOfProducts方法返回一個(gè)標(biāo)準(zhǔn)的整型.調(diào)用GetValueOrDefault方法,如果可為空的整型為空,則返回默認(rèn)值,0.

第三步: 返回需要的數(shù)據(jù)記錄

  下一步我們要在DAL和BLL里創(chuàng)建接受Start Row Index 和Maximum Rows 的方法,然后返回合適的記錄.我們首先看看需要的SQL語(yǔ)句.我們面臨的挑戰(zhàn)是需要為整個(gè)分頁(yè)的記錄分配索引,用來(lái)返回從Start Row Index 開(kāi)始的Maximum Records number of records條記錄.

  如果在數(shù)據(jù)庫(kù)表里已經(jīng)有一個(gè)列作為索引,那么一切會(huì)變的很簡(jiǎn)單.我們首先會(huì)想到Products表的ProductID字段可以滿足這個(gè)條件,第一個(gè)Product的ProductID為1,第二個(gè)為2,以此類推.然而當(dāng)一個(gè)product被刪除后,這個(gè)序列會(huì)留下間隔來(lái),所以這個(gè)方法不行.

  有兩種可以把整個(gè)要分頁(yè)的數(shù)據(jù)和一個(gè)row index關(guān)聯(lián)起來(lái)的方法.

  1.使用SQL Server 2005的ROW_NUMBER() Keyword – SQL Server 2005的新特性,它可以將記錄根據(jù)一定的順序排列,每條記錄和一個(gè)等級(jí)相關(guān) 這個(gè)等級(jí)可以用來(lái)作為每條記錄的row index.

 ?。?使用SET ROWCOUNT – SQL Server的 SET ROWCOUNT statement 可以用來(lái)指定有多少記錄需要處理; table variables 是可以存放表格式的T-SQL 變量, 和temporary tables類似. 這個(gè)方法在Microsoft SQL Server 2005 和SQL Server 2000都可以用 (ROW_NUMBER() 方法只能在SQL Server 2005里用).

  這個(gè)思路是,為要分頁(yè)的數(shù)據(jù)創(chuàng)建一個(gè)table變量,這個(gè)table變量里有一個(gè)作為主健的IDENTITY列.這樣需要分頁(yè)的每條記錄在table變量里就和一個(gè)row index(通過(guò)IDENTITY列)關(guān)聯(lián)起來(lái)了.一旦table變量產(chǎn)生,連接數(shù)據(jù)庫(kù)表的SELECT語(yǔ)句就被執(zhí)行,獲取需要的記錄.SET ROWCOUNT用來(lái)限制放到table變量里的記錄的數(shù)量.

  當(dāng)SET ROWCOUNT的值指定為Start Row Index 加上Maximum Rows時(shí),這個(gè)方法的效率取決于被請(qǐng)求的頁(yè)數(shù).對(duì)于比較前面的頁(yè)來(lái)說(shuō)– 比如開(kāi)始幾頁(yè)的數(shù)據(jù)– 這種方法非常有效. 但是對(duì)接近尾部的頁(yè)來(lái)說(shuō),這種方法的效率和默認(rèn)分頁(yè)時(shí)差不多.

  本教程用ROW_NUMBER()來(lái)實(shí)現(xiàn)自定義分頁(yè).如果需要知道更多的關(guān)于table變量和SET ROWCOUNT的技術(shù),請(qǐng)看 A More Efficient Method for Paging Through Large Result Sets.

以下語(yǔ)句用來(lái)使用ROW_NUMBER()將一個(gè)等級(jí)和返回的每條記錄關(guān)聯(lián):

SELECT columnList,
 ROW_NUMBER() OVER(orderByClause)
FROM TableName

ROW_NUMBER()返回一個(gè)根據(jù)指定排序的表示每條記錄的等級(jí)的值.比如,我們可以用以下居于查看根據(jù)價(jià)格來(lái)排序(降序)的每個(gè)product的等級(jí):

SELECT ProductName, UnitPrice,
 ROW_NUMBER() OVER(ORDER BY UnitPrice DESC) AS PriceRank
FROM Products

圖5 是在Visual Studio里運(yùn)行以上代碼的結(jié)果. 注意product根據(jù)價(jià)格排序,每行有一個(gè)等級(jí).

圖 5: 返回的記錄里每行有一個(gè)Price Rank

  注意: ROW_NUMBER() 只是 SQL Server 2005里很多排級(jí)的功能中的一種. 想了解更多的ROW_NUMBER()的討論,包括其它的排級(jí)功能,請(qǐng)看 Returning Ranked Results with Microsoft SQL Server 2005.

  當(dāng)使用OVER從句里的ORDER BY 列名(UnitPrice)來(lái)排級(jí)時(shí),SQL Server會(huì)對(duì)結(jié)果排序.為了提升大數(shù)據(jù)量查詢時(shí)的性能,可以為用來(lái)排序的列加上非聚集索引.更多的性能考慮參考Ranking Functions and Performance in SQL Server 2005.

  ROW_NUMBER()返回的等級(jí)信息無(wú)法直接在WHERE從句中使用.而在From后面的Select里可以返回ROW_NUMBER(),并在WHERE從句里使用.比如,下面的語(yǔ)句使用一個(gè)From后的Select返回ProductName,UnitPrice,和ROW_NUMBER()的結(jié)果,然后使用一個(gè)WHERE從句來(lái)返回price rank在11到20之間的product.

SELECT PriceRank, ProductName, UnitPrice
FROM
 (SELECT ProductName, UnitPrice,
 ROW_NUMBER() OVER(ORDER BY UnitPrice DESC) AS PriceRank
 FROM Products
 ) AS ProductsWithRowNumber
WHERE PriceRank BETWEEN 11 AND 20

更進(jìn)一步,我們可以根據(jù)這個(gè)方法返回給定Start Row Index 和Maximum Rows 的頁(yè)的數(shù)據(jù).

SELECT PriceRank, ProductName, UnitPrice
FROM
 (SELECT ProductName, UnitPrice,
 ROW_NUMBER() OVER(ORDER BY UnitPrice DESC) AS PriceRank
 FROM Products
 ) AS ProductsWithRowNumber
WHERE PriceRank > i>StartRowIndex/i> AND
 PriceRank = (i>StartRowIndex/i> + i>MaximumRows/i>)

  注意:我們?cè)诒窘坛痰暮竺鏁?huì)看到, ObjectDataSource 提供的StartRowIndex是從0開(kāi)始的,而ROW_NUMBER()的值從1開(kāi)始.因此,WHERE從句返回會(huì)嚴(yán)格返回PriceRank大于StartRowIndex而小于StartRowIndex+MaximumRows的那些記錄.

  我們已經(jīng)知道如何根據(jù)給定的Start Row Index 和Maximum Rows 用ROW_NUMBER()返回特定頁(yè)的數(shù)據(jù).現(xiàn)在我們需要在DAL和BLL里實(shí)現(xiàn)它.

  我們首先要決定根據(jù)什么排序來(lái)分級(jí).我們這里用product名字的字母順序.這意味著我們還不能同時(shí)實(shí)現(xiàn)排序的功能.在后面的教程里,我們將學(xué)習(xí)如何實(shí)現(xiàn)這樣的功能.

  在前面我們使用SQL statement創(chuàng)建DAL方法.但是TableAdapter  wizard 使用的Visual Stuido里的T-SQL 解析器不能識(shí)別帶OVER語(yǔ)法的ROW_NUMBER()方法.因此我們要以存儲(chǔ)過(guò)程來(lái)創(chuàng)建這個(gè)DAL方法.從view menu里選擇server explorer(Ctrl+Alt+S),展開(kāi)NORTHWND.MDF 的節(jié)點(diǎn).右鍵點(diǎn)擊存儲(chǔ)過(guò)程,選擇增加一個(gè)新的存儲(chǔ)過(guò)程(見(jiàn)圖6).

圖 6: 為Products分頁(yè)增加一個(gè)存儲(chǔ)過(guò)程

  這個(gè)存儲(chǔ)過(guò)程帶兩個(gè)整型的輸入?yún)?shù)- @startRowIndex和@maximumRows- 并用ROW_NUMBER()以ProductName字段排序,返回那些大于@startRowIndex并小于等于@startRowIndex+@maximumRows的記錄.將以下代碼加到存儲(chǔ)過(guò)程里,然后保存.

CREATE PROCEDURE dbo.GetProductsPaged
(
 @startRowIndex int,
 @maximumRows int
)
AS
 SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit,
 UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued,
 CategoryName, SupplierName
FROM
 (
 SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit,
 UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued,
 (SELECT CategoryName
 FROM Categories
 WHERE Categories.CategoryID = Products.CategoryID) AS CategoryName,
 (SELECT CompanyName
 FROM Suppliers
 WHERE Suppliers.SupplierID = Products.SupplierID) AS SupplierName,
 ROW_NUMBER() OVER (ORDER BY ProductName) AS RowRank
 FROM Products
 ) AS ProductsWithRowNumbers
WHERE RowRank > @startRowIndex AND RowRank = (@startRowIndex + @maximumRows)

創(chuàng)建完存儲(chǔ)過(guò)程后,花點(diǎn)時(shí)間測(cè)試一下.右鍵在Server Explorer 點(diǎn)名為GetProductsPaged的存儲(chǔ)過(guò)程,選擇執(zhí)行.Visual Studio 會(huì)讓你輸入?yún)?shù), @startRowIndex和@maximumRows(見(jiàn)圖7).輸入不同的值查看一下結(jié)果是什么.

圖 7: 為 @startRowIndex 和@maximumRows Parameters輸入值

  輸入?yún)?shù)的值后,你會(huì)看到結(jié)果.圖8的結(jié)果為兩個(gè)參數(shù)的值都為10的結(jié)果.

圖 8: 將在第二頁(yè)里顯示的數(shù)據(jù)

  完成存儲(chǔ)過(guò)程后,我們可以創(chuàng)建ProductsTableAdapter 方法了.打開(kāi)Northwind.xsd ,右鍵點(diǎn)ProductsTableAdapter,選擇Add Query.選擇使用已經(jīng)存在的存儲(chǔ)過(guò)程.

圖 9: 使用已經(jīng)存在的存儲(chǔ)過(guò)程創(chuàng)建DAL Method

  下一步會(huì)要我們選擇要調(diào)用的存儲(chǔ)過(guò)程.從下拉列表里選擇GetProductsPaged .

圖10: 選擇GetProductsPaged

  下一步要選擇存儲(chǔ)過(guò)程返回的數(shù)據(jù)類型:表值,單一值,無(wú)值.由于GetProductsPaged 返回多條記錄,所以選擇表值.

圖 11: 為存儲(chǔ)過(guò)程指定返回表值

  最后給方法命名.象前面的方法一樣,選擇Fill a DataTable 和Return a DataTable,為第一個(gè)命名為FillPaged ,第二個(gè)為GetProductsPaged.

圖 12: 命名方法為FillPaged 和GetProductsPaged

  除了創(chuàng)建一個(gè)DAL方法返回特定頁(yè)的products外,我們需要在BLL里也這樣做.和DAL方法一樣,BLL的GetProductsPaged 方法帶兩個(gè)整型的輸入?yún)?shù),分別為Start Row Index 和Maximum Rows,并返回在指定范圍內(nèi)的記錄.在ProductsBLL 創(chuàng)建這個(gè)方法,僅僅調(diào)用DAL的GetProductsPaged 就可以了.

[System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Select, false)]
public Northwind.ProductsDataTable GetProductsPaged(int startRowIndex, int maximumRows)
{
 return Adapter.GetProductsPaged(startRowIndex, maximumRows);
}

  你可以為BLL方法的參數(shù)取任何名字.但是我們馬上會(huì)看到,選擇用startRowIndex 和maximumRows 會(huì)讓我們?cè)谂渲肙bjectDataSource 時(shí)方便很多.
第四步: 使用自定義分頁(yè)配置ObjectDataSource

  創(chuàng)建完BLL和DAL的方法后,我們可以準(zhǔn)備創(chuàng)建一個(gè)GridView 來(lái)使用自定義分頁(yè)了.打開(kāi)PagingAndSorting 文件夾里的EfficientPaging.aspx ,添加一個(gè)GridView ,然后用ObjectDataSource 來(lái)配置它.在我們以前的教程里,我們通常使用ProductsBLL 類的GetProducts 方法來(lái)配置ObjectDataSource .然而這一次,我們使用GetProductsPaged 方法.GetProducts 會(huì)返回所有的products而GetProductsPaged 只返回特定的記錄.

圖 13: 使用ProductsBLL Class類的 GetProductsPaged方法 來(lái)配置ObjectDataSource

  我們要?jiǎng)?chuàng)建一個(gè)只讀的GridView,因此在INSERT, UPDATE, 和DELETE 標(biāo)簽下拉列表里選擇(None).

  接下來(lái)ObjectDataSource 向?qū)?huì)讓我們選擇GetProductsPaged 方法的輸入?yún)?shù)startRowIndex 和maximumRows 的值.在source里選擇none.

圖 14: Sources 里選擇None

  完成ObjectDataSource 向?qū)Ш?GridView 會(huì)為每個(gè)product字段創(chuàng)建一個(gè)BoundField 或CheckBoxField .可以隨意裁減GridView 的外觀.我這里選擇的是只顯示ProductName, CategoryName, SupplierName, QuantityPerUnit, 和UnitPrice BoundFields.在智能標(biāo)簽里選擇支持分頁(yè),GridView 和ObjectDataSource 的標(biāo)記看起來(lái)應(yīng)該和下面差不多:

asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
 DataKeyNames="ProductID" DataSourceID="ObjectDataSource1" AllowPaging="True">
 Columns>
 asp:BoundField DataField="ProductName" HeaderText="Product"
 SortExpression="ProductName" />
 asp:BoundField DataField="CategoryName" HeaderText="Category"
 ReadOnly="True" SortExpression="CategoryName" />
 asp:BoundField DataField="SupplierName" HeaderText="Supplier"
 SortExpression="SupplierName" />
 asp:BoundField DataField="QuantityPerUnit" HeaderText="Qty/Unit"
 SortExpression="QuantityPerUnit" />
 asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}"
 HeaderText="Price" HtmlEncode="False" SortExpression="UnitPrice" />
 /Columns>
/asp:GridView>
asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
 OldValuesParameterFormatString="original_{0}" SelectMethod="GetProductsPaged"
 TypeName="ProductsBLL">
 SelectParameters>
 asp:Parameter Name="startRowIndex" Type="Int32" />
 asp:Parameter Name="maximumRows" Type="Int32" />
 /SelectParameters>
/asp:ObjectDataSource>

如果你通過(guò)瀏覽器瀏覽頁(yè)面,你會(huì)發(fā)現(xiàn)看不到GridView .

圖 15: GridView 沒(méi)有被顯示

  由于在ObjectDataSource 里的GetProductsPaged的startRowIndex和maximumRows的參數(shù)都為0,由SQL沒(méi)有返回任何的記錄因此GridView 看不到了.


我們需要將ObjectDataSource 配置成為自定義分頁(yè)來(lái)修補(bǔ)上面的問(wèn)題.下面的步驟可以完成這個(gè):

  1.將ObjectDataSource的 EnablePaging 屬性設(shè)為true – 這樣表示必須傳兩個(gè)參數(shù)給SelectMethod方法: 一個(gè)指定Start Row Index (StartRowIndexParameterName), 一個(gè)指定Maximum Rows (MaximumRowsParameterName).

 ?。?設(shè)置 ObjectDataSource的 StartRowIndexParameterName 和MaximumRowsParameterName 屬性– StartRowIndexParameterName 和MaximumRowsParameterName 屬性是傳給SelecMethod用來(lái)自定義分頁(yè)的輸入?yún)?shù). 默認(rèn)的參數(shù)名為startIndexRow and MaximumRows, 這就是在創(chuàng)建BLL里的GetProductsPaged方法時(shí)用這些給參數(shù)命名的原因 . 如果你使用了其它的參數(shù)名字–比如startIndex和maxRows–你將不得不相應(yīng)的設(shè)置ObjectDataSource的StartRowIndexParameterName和MaximumRowsParameterName(startIndex和maxRows).

 ?。?設(shè)置 ObjectDataSource的 SelectCountMethod Property 為返回分頁(yè)記錄總數(shù)的方法的名字(TotalNumberOfProducts)–調(diào)用ProductsBLL類的TotalNumberOfProducts方法返回總的記錄數(shù) . ObjectDataSource 需要這個(gè)信息來(lái)正確的顯示頁(yè)面.

  4.從ObjectDataSource的聲明里移除startRowIndex and maximumRows asp:Parameter> 元素的標(biāo)記– 當(dāng)通過(guò)向?qū)渲?ObjectDataSource 時(shí), Visual Studio 自動(dòng)為GetProductsPaged方法的參數(shù)增加了兩個(gè)asp:Parameter> 元素. 設(shè)置EnablePaging 為true后, 這些參數(shù)會(huì)被自動(dòng)傳遞;如果在聲明代碼里保留它們,那么ObjectDataSource會(huì)試圖傳遞4個(gè)參數(shù)給GetProductsPaged和2個(gè)參數(shù)給TotalNumberOfProducts .如果你沒(méi)有移除asp:Parameter> ,當(dāng)瀏覽頁(yè)面的時(shí)候你會(huì)獲得一個(gè)象這樣的錯(cuò)誤信息 : “ObjectDataSource 'ObjectDataSource1' could not find a non-generic method 'TotalNumberOfProducts' that has parameters: startRowIndex, maximumRows.”

做完這些改動(dòng)后,ObjectDataSource的聲明代碼看起來(lái)應(yīng)該和下面差不多:

asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
 OldValuesParameterFormatString="original_{0}" TypeName="ProductsBLL"
 SelectMethod="GetProductsPaged" EnablePaging="True"
 SelectCountMethod="TotalNumberOfProducts">
/asp:ObjectDataSource>

注意EnablePaging和SelectCountMethod屬性已經(jīng)被設(shè)置了,asp:Parameter>被移除了.圖16是屬性窗口.

圖16: 使用自定義分頁(yè)配置,ObjectDataSource

  完成這些后,瀏覽頁(yè)面.你會(huì)看到10條product按照字母排序被列出來(lái)了.每次翻一頁(yè)看看.對(duì)用戶來(lái)說(shuō)現(xiàn)在還看不出來(lái)什么差別,因?yàn)樽远x分頁(yè)在大數(shù)據(jù)量的情況下效率才能顯示出來(lái).

圖17: 根據(jù)Product的 Name排序的數(shù)據(jù)的自定義分頁(yè)

  注意:自定義分頁(yè)時(shí),ObjectDataSource的SelectCountMethod方法返回的page count值存在GridView的view state里.其它變量–PageIndex,EditIndex,SelectedIndex,DataKeys集合等–都存在control state里.control state和GridView的EnableViewState屬性無(wú)關(guān).由于PageCount的值在postback期間存在viewstate里,當(dāng)你的頁(yè)面上有鏈到上一頁(yè)的link時(shí),你需要開(kāi)啟GridView的view state(如果沒(méi)有這個(gè)link,你可以禁用view state).

  點(diǎn)上一頁(yè)link會(huì)引起postback,GridView會(huì)更新PageIndex屬性.GridView會(huì)給PageIndex賦一個(gè)小于PageCount的值.如果禁用了view state,PageCount的值在postback時(shí)會(huì)丟失,PageIndex會(huì)被賦一個(gè)最大的整型值.然后GridView在根據(jù)PageSize乘PageCount來(lái)計(jì)算starting row index時(shí)會(huì)發(fā)生OverflowException異常.

執(zhí)行自定義分頁(yè)和排序

  目前我們自定義分頁(yè)時(shí)使用的排序字段是在創(chuàng)建GetProductsPaged存儲(chǔ)過(guò)程時(shí)寫(xiě)死的.在GridView的智能標(biāo)簽里有一個(gè)Enable Sorting的checkbox,不幸的是,在前面的工作里加上排序功能僅僅只能將當(dāng)前頁(yè)的記錄排序.比如,按照降序查看第一頁(yè)的數(shù)據(jù),第一頁(yè)的product的順序回反轉(zhuǎn).見(jiàn)圖18,Carnarvon Tigers 成為第一條記錄,而在它之后的71條記錄被忽略了.排序時(shí)只排了顯示在第一頁(yè)的數(shù)據(jù).

圖18: 只有當(dāng)前頁(yè)的數(shù)據(jù)被排序了

  發(fā)生這種情況的原因是調(diào)用完BLL的GetProductsPaged方法返回?cái)?shù)據(jù)之后才排序.耳針個(gè)方法只返回特定頁(yè)的記錄.為了正確的排序,我們需要將排序表達(dá)式傳到GetProductsPaged方法里,在返回特定頁(yè)的數(shù)據(jù)前進(jìn)行排序.我們將在后面的教程里完成這個(gè)功能.

執(zhí)行自定義分頁(yè)和刪除

  如果你開(kāi)啟GridView的刪除功能,你會(huì)發(fā)現(xiàn)刪除最后一頁(yè)的最后一條記錄時(shí),GridView消失了,而不是正確的減掉PageIndex的值.在我們上面創(chuàng)建的GridView里開(kāi)啟刪除來(lái)查看這個(gè)bug.到最后一頁(yè)(第九頁(yè)),由于我們有81條記錄,每頁(yè)顯示10條,所以你會(huì)只看到一條記錄,刪除這條記錄.

  在默認(rèn)分頁(yè)時(shí),GridView會(huì)自動(dòng)跳到第八頁(yè),這也是我們想要的結(jié)果.然而在自定義分頁(yè)里, GridView卻顯示.發(fā)生這個(gè)的原因有點(diǎn)超出了本教程的范圍,可以看Deleting the Last Record on the Last Page from a GridView with Custom Paging.簡(jiǎn)單的說(shuō)是因?yàn)辄c(diǎn)Delete時(shí),GridView是按這樣的步驟工作的:   

刪除記錄.
   

  按照給定的PageIndex和PageSize獲取記錄.

  檢查PageIndex確保沒(méi)有超過(guò)數(shù)據(jù)源的頁(yè)的數(shù)量.如果是,GridView的PageIndex會(huì)自動(dòng)減.

  使用第二步獲取的記錄綁定到GridView適當(dāng)?shù)捻?yè).

  問(wèn)題的根源在于第二步,當(dāng)獲取顯示的記錄時(shí),使用的PageIndex仍然是最后一頁(yè)的PageIndex.因此沒(méi)有記錄被返回.在第三步里GridView判斷出PageIndex屬性大于數(shù)據(jù)源的總頁(yè)數(shù)(因?yàn)樽詈笠豁?yè)的最后一條數(shù)據(jù)被刪除了) 就對(duì)PageIndex減1.在第四步里GridView試圖將第二步獲取的數(shù)據(jù)作為數(shù)據(jù)源進(jìn)行綁定,但是沒(méi)有任何數(shù)據(jù),因此顯示的GridView不見(jiàn)了.在默認(rèn)分頁(yè)里沒(méi)有這個(gè)問(wèn)題是因?yàn)樵诘诙竭€是返回的所有數(shù)據(jù).

  我們可以用兩種方法來(lái)修改這個(gè).第一是為GridView的RowDeleted事件創(chuàng)建一個(gè)event handler
來(lái)判斷在刪除頁(yè)里有多少條記錄,如果只有一條,那么這條肯定是最后一條,我們需要為PageIndex減1.當(dāng)然我們希望只在刪除成功后來(lái)修改PageIndex的值.我們需要用e.Exception屬性是否為空來(lái)判斷.

  這個(gè)方法之所以起作用是因?yàn)樗诘谝徊胶偷诙街g修改了PageIndex的值.因此在第二步里正確的記錄會(huì)被返回.見(jiàn)如下代碼:

protected void GridView1_RowDeleted(object sender, GridViewDeletedEventArgs e)
{
 // If we just deleted the last row in the GridView, decrement the PageIndex
 if (e.Exception == null  GridView1.Rows.Count == 1)
 // we just deleted the last row
 GridView1.PageIndex = Math.Max(0, GridView1.PageIndex - 1);
}

  另外一種辦法是為ObjectDataSource的RowDeleted事件創(chuàng)建一個(gè)event handler,設(shè)置AffectedRows屬性為1.在第一步刪除記錄后(在第二步之前),如果一行或多行記錄被影響,GridView會(huì)更新PageIndex的值.然而ObjectDataSource 并沒(méi)有設(shè)置AffectedRows,因此這一步不會(huì)執(zhí)行.我們需要在刪除操作成功的情況下手動(dòng)設(shè)置AffectedRows.見(jiàn)下面的代碼:

protected void ObjectDataSource1_Deleted(
 object sender, ObjectDataSourceStatusEventArgs e)
{
 // If we get back a Boolean value from the DeleteProduct method and it's true,
 // then we successfully deleted the product. Set AffectedRows to 1
 if (e.ReturnValue is bool  ((bool)e.ReturnValue) == true)
 e.AffectedRows = 1;
}

  這些代碼都可以在EfficientPaging.aspx的code-behind class里找到

比較默認(rèn)和自定義分頁(yè)的性能

  由于自定義分頁(yè)返回需要的數(shù)據(jù),而默認(rèn)分頁(yè)返回全部數(shù)據(jù),因此自定義分頁(yè)比默認(rèn)分頁(yè)更有效率是非常清楚的.但是性能上的提升究竟有多少?從默認(rèn)分頁(yè)換成自定義分頁(yè)有什么性能上的優(yōu)勢(shì)?

  很不幸,沒(méi)有一個(gè)統(tǒng)一的答案.性能的優(yōu)勢(shì)取決于很多因素,其中最重要的是分頁(yè)記錄的數(shù)量,數(shù)據(jù)庫(kù)的負(fù)載和web server和數(shù)據(jù)庫(kù)的通信渠道.對(duì)一些小的表來(lái)說(shuō),性能的差異是可以忽略的.對(duì)成千上萬(wàn)行數(shù)據(jù)的表來(lái)說(shuō),差異是非常明顯的.

  我們的一篇Custom Paging in ASP.NET 2.0 with SQL Server 2005文章包含一些對(duì)比這兩種分頁(yè)技術(shù)的性能測(cè)試,用到的表有大概50,000 條記錄.在測(cè)試中我分別測(cè)試了在SQL Server里(使用SQL Profiler)和ASP.NET頁(yè)面里(使用ASP.NET's tracing features)執(zhí)行查詢的時(shí)間.注意這是在我的開(kāi)發(fā)環(huán)境下單個(gè)用戶的測(cè)試結(jié)果,因此沒(méi)有模仿典型的網(wǎng)站的負(fù)載情況,結(jié)果也并不科學(xué).

  Avg. Duration (sec) Reads
Default Paging – SQL Profiler 1.411 383
Custom Paging – SQL Profiler 0.002 29
Default Paging – ASP.NET Trace 2.379 N/A
Custom Paging – ASP.NET Trace 0.029 N/A

  如你所見(jiàn),獲取特定頁(yè)的數(shù)據(jù)平均少了354 reads,并在恩短的時(shí)間完成.而在頁(yè)面里,自定義分頁(yè)是默認(rèn)分頁(yè)所花費(fèi)時(shí)間的1/100.在my article 可以看到更多的測(cè)試信息和代碼,你可以下載測(cè)試數(shù)據(jù)庫(kù)在你的環(huán)境里重新測(cè)試.

總結(jié)

  默認(rèn)分頁(yè)是非常容易實(shí)現(xiàn)的–你僅僅只需要選擇控件上的智能標(biāo)簽里的Enable Paging checkbox –但是方便帶來(lái)的是性能的損失.在默認(rèn)分頁(yè)時(shí),用戶無(wú)論請(qǐng)求哪個(gè)頁(yè)面,所有的數(shù)據(jù)都會(huì)被返回,即使只有一小部分被顯示出來(lái).為了提升性能,ObjectDataSource 提供了一個(gè)可選擇的分頁(yè)功能–自定義分頁(yè).

  自定義分頁(yè)通過(guò)只獲取需要顯示的數(shù)據(jù)來(lái)解決默認(rèn)分頁(yè)的性能問(wèn)題,但是使用起來(lái)更麻煩.首先,請(qǐng)求特定數(shù)據(jù)的查詢語(yǔ)句必須正確而且有效.這個(gè)可以通過(guò)很多方法來(lái)實(shí)現(xiàn).在本教程里我們使用SQL Server 2005的ROW_NUMBER來(lái)實(shí)現(xiàn)給結(jié)果分級(jí),然后返回等級(jí)在特定范圍內(nèi)的數(shù)據(jù).其次我們需要增加一個(gè)方法來(lái)獲取需要分頁(yè)的總記錄數(shù).在創(chuàng)建完DAL和BLL方法后,我們還需要配置ObjectDataSource以使它可以獲取需要分頁(yè)的總記錄數(shù),并將正確的Row Index 和Maximum Rows 的值傳給BLL.

  雖然使用自定義分頁(yè)需要一系列的操作,而且遠(yuǎn)沒(méi)有默認(rèn)分頁(yè)那么簡(jiǎn)單.但是在大數(shù)據(jù)量的情況還是必須的.只顯示需要的數(shù)據(jù),自定義分頁(yè)可以節(jié)省很多時(shí)間,減輕數(shù)據(jù)庫(kù)的負(fù)擔(dān).

祝編程快樂(lè)!

作者簡(jiǎn)介

Scott Mitchell,著有六本ASP/ASP.NET方面的書(shū),是4GuysFromRolla.com的創(chuàng)始人,自1998年以來(lái)一直應(yīng)用 微軟Web技術(shù)。Scott是個(gè)獨(dú)立的技術(shù)咨詢顧問(wèn),培訓(xùn)師,作家,最近完成了將由Sams出版社出版的新作,24小時(shí)內(nèi)精通ASP.NET 2.0。他的聯(lián)系電郵為mitchell@4guysfromrolla.com,也可以通過(guò)他的博客http://scottonwriting.net/與他聯(lián)系。

您可能感興趣的文章:
  • asp.net下linkbutton的前后臺(tái)使用方法
  • asp.net button 綁定多個(gè)參數(shù)
  • 關(guān)于asp.net button按鈕的OnClick和OnClientClick事件
  • js觸發(fā)asp.net的Button的Onclick事件應(yīng)用
  • ASP.NET 中 Button、LinkButton和ImageButton 三種控件的使用詳解
  • 在ASP.NET 2.0中操作數(shù)據(jù)之二十三:基于用戶對(duì)修改數(shù)據(jù)進(jìn)行限制
  • 在ASP.NET 2.0中操作數(shù)據(jù)之二十四:分頁(yè)和排序報(bào)表數(shù)據(jù)
  • 在ASP.NET 2.0中操作數(shù)據(jù)之二十六:排序自定義分頁(yè)數(shù)據(jù)
  • 在ASP.NET 2.0中操作數(shù)據(jù)之二十七:創(chuàng)建自定義排序用戶界面
  • 在ASP.NET 2.0中操作數(shù)據(jù)之二十八:GridView里的Button

標(biāo)簽:中衛(wèi) 臨夏 海西 甘肅 聊城 慶陽(yáng) 清遠(yuǎn)

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《在ASP.NET 2.0中操作數(shù)據(jù)之二十五:大數(shù)據(jù)量時(shí)提高分頁(yè)的效率》,本文關(guān)鍵詞  在,ASP.NET,2.0,中,操作,數(shù)據(jù),;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《在ASP.NET 2.0中操作數(shù)據(jù)之二十五:大數(shù)據(jù)量時(shí)提高分頁(yè)的效率》相關(guān)的同類信息!
  • 本頁(yè)收集關(guān)于在ASP.NET 2.0中操作數(shù)據(jù)之二十五:大數(shù)據(jù)量時(shí)提高分頁(yè)的效率的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章