2009年11月22日 星期日

ObjectDataSource配合Gridview來達成手動分頁完整範例

    網站的程式,常常需要將資料自資料庫中抓取出來後,顯示在user端並且提供分頁的功能,讓user可以方便的到想要看到資料的那一頁。但是若是以SqlDataSource的方式,抓取資料後,拋給前端顯示的aspx程式,通常只能依據使用者提供的條件查詢到資料後,全部拋給aspx程式,然後根據Gridview的[AllowPaging]設定,提供分頁的功能,但是假設查到1000筆資料,若是一頁顯示10筆資料,使用SqkDataSource這種方式,若沒有特別處理自資料庫抓出來的筆數,仍然會將這1000筆資料,一次拋給apsx程式,然後再顯示一頁10筆的方式給user瀏覽。一次抓出大量且使用者暫時不會看到的資料,對於網站伺服器而言是很大的負荷,瀏覽人數少的時候,可能這種負荷看不出來,但是當瀏覽人數越來越多時,網站存取資料的回應速度就會明顯的下降。

    當然要處理這種問題,或許加上Cache是一種稍微提升回應速度的方式,但是我們這邊說明以ObjectDataSource的方式是配合SQL Server 2005資料庫ROW_NUMBER()的指令,並且將存取資料庫模型化成一個獨立的元件,每次只從資料庫中抓取使用者一次要看到的筆數拋給前端的Gridview。以上一段所描述的例子而言,使用者查到1000筆,若此時每個分頁顯示10筆資料,我們每次會只自資料庫中抓取出10筆資料,回應給user端顯示的aspx程式,大大的減少了資料庫與網路的負荷,並配合使用Stored Procedure以提升整體網站存取速度。

步驟一:

    首先我們先建立一個網站專案與一個類別庫專案,名稱分別為Northwind與NorthwindSQL,在專案Northwind中需將專案NorthwindSQL加入参考;然後建立css目錄以存放style.css檔案;建立ShowCustomer.aspx,純粹負責顯示介面資料給user瀏覽;並且在NorthwindSQL專案下建立GetCustomer.cs檔案,用來處理與存取資料的邏輯之用。在.NET 2008的開發環境界面看來如下:



步驟二:

    接下來我們開啟ShowCustomer.aspx,分別加上
1.兩個TextBox,分別讓使用者可輸入每頁顯示筆數(txtPagesize)與欲查詢的國別(txtCountry),並加上RangeValidator以規範每頁顯示筆數在1到100筆間。
2.一個確認查詢的按鈕(btn_query)。
3.一個顯示資料的Gridview控制項,並設定可准許分頁(AllowPaging="True")與自動產生資料欄位(AutoGenerateColumns="true"),然後加上CSS檔案的描述。
4.一個ObjectDataSource控制項,控制項設定稍後步驟五會說明。
上述步驟完成後,網頁設計如下所述。



步驟三:

    接下來我們打開SQL Server 2005,利用Northwind資料庫現成資料來作範例展示;我們新增2個Stored Procedure名為sp_customerssp_customers;2

sp_customers:只抓取使用者每頁顯示的資料。
sp_customers;2:計算並Output出每次查詢的總筆數,此為Gridview分頁時所需要。

我們傳入4個參數給這兩個Stored Procedure:

startRowInde:每頁開始筆數的index。
maximumRows:每頁最後一列筆的筆數。
Country:使用者查詢的國家別條件字串。
total_count每次查詢的總筆數。

這兩段Stored Procedure全部內容如下:


--只抓取使用者每頁顯示的資料

CREATE PROCEDURE [dbo].[sp_customers]
@startRowIndex  int,
@maximumRows  int,
@Country  nvarchar(15)=''
AS
BEGIN
if len(@Country)=0
    begin
      SELECT * FROM
      (SELECT ROW_NUMBER() OVER(ORDER BY CustomerID) As RowNumber,* FROM Customers ) Customers
     where RowNumber between @startRowIndex+1 AND (@startRowIndex+@maximumRows)
    end
else
    begin
      SELECT * FROM
      (SELECT ROW_NUMBER() OVER(ORDER BY CustomerID) As RowNumber,* FROM Customers where Country=@Country) Customers
      where RowNumber between @startRowIndex+1 AND (@startRowIndex+@maximumRows)
    end
END

--計算並Output出每次查詢的總筆數,此為Gridview分頁時所需要。
CREATE PROCEDURE [dbo].[sp_customers];2
@Country  nvarchar(15)='',
@total_count  int=0 output
AS
BEGIN
if len(@Country)=0
    begin
      select @total_count=(SELECT COUNT(*) AS TOTAL_COUNT FROM Customers)
    end
else
    begin
      select @total_count=(SELECT COUNT(*) AS TOTAL_COUNT FROM Customers where Country=@Country)

    end
END



有關ROW_NUMBER()的用法可叁考微軟ROW_NUMBER(),主要是可在SQL描述中,不使用Cursor的方式,可以很方便的去讀取特定筆數範圍的資料。

步驟四:

    我們將如何存取資料的邏輯撰寫在NorthwindSQL元件中的GetCustomer.cs內,程式碼如下:


public class GetCustomer
{
    public DataTable GetData(int startRowIndex, int maximumRows, string Country)
    {
        DataTable dt = new DataTable();
        SqlConnection objConn = new SqlConnection();
        objConn.ConnectionString =ConfigurationManager.ConnectionStrings
        ["NorthwindConn"].ConnectionString;
        SqlCommand cmd = objConn.CreateCommand();
        cmd.CommandTimeout = objConn.ConnectionTimeout;
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.CommandText = "sp_customers;1";
        cmd.Parameters.Add("@startRowIndex", SqlDbType.Int).Value = startRowIndex;
        cmd.Parameters.Add("@maximumRows", SqlDbType.Int).Value = maximumRows;
        cmd.Parameters.Add("@Country", SqlDbType.NVarChar, 15).Value = Country;
        using (objConn)
        {
            objConn.Open();
            using (cmd)
            {
                SqlDataReader sdr = cmd.ExecuteReader();
                using (sdr)
                {
                    dt.Load(sdr);
                    return dt;
                }
            }
        }
    }

    public int GetCount(int startRowIndex, int maximumRows, string Country)
    {
        SqlConnection objConn = new SqlConnection();
        objConn.ConnectionString = ConfigurationManager.ConnectionStrings
        ["NorthwindConn"].ConnectionString;
        SqlCommand cmd = objConn.CreateCommand();
        cmd.CommandTimeout = objConn.ConnectionTimeout;
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.CommandText = "sp_customers;2";
        cmd.Parameters.Add("@Country", SqlDbType.NVarChar, 15).Value = Country;
        cmd.Parameters.Add("@total_count", SqlDbType.Int).Direction =
        ParameterDirection.Output;
        using (objConn)
        {
            objConn.Open();
            using (cmd)
            {
                SqlDataReader sdr = cmd.ExecuteReader();
                using (sdr)
                {
                    int t = (int)cmd.Parameters["@total_count"].Value;
                    return t;
                }
            }
        }
    }
}



步驟五:

上個步驟我們已經將存取資料邏輯的元件寫好了,接下來就是本文最重要的地方,須在ObjectDataSource中指名要取用的元件為何,如下:

(1)點選ShowCustomer.aspx上的控制項ObjectDataSource,並點開[設定資料來源]。



(2)選擇我們步驟四中製作好的元件NorthwindSQL,記得先對此元件執行Compile的動作。



(3)接下來選擇SELECT頁籤中的GetData()這個方法,作為我們要回傳給ShowCustomer.aspx的資料取用Method。



(4)因為我們可接受user輸入與查詢的國別,來找尋Northwind資料庫中的Customer。故接下來我們需將傳入GetData()著参數對應到ShowCustomer.aspx中的txtCountry這個控制項。



(5)以上設定完成後,在ShowCustomer.aspx網頁上的ObjectDataSource各項設定如下:





步驟六:

    最後我們在ShowCustomer.aspx.cs中將ObjectDataSource所抓取的資料交給網頁上的Gridview控制項,以呈現可讓使用者選擇分頁的效果



執行網頁的成果如下:




結語:

    使用ObjectDataSource的分頁設定功能,再配合資料庫中ROW_NUMBER()的語法,可讓我們很快的只將必要的資料拋給前端的使用者,這對提升網站效能,有相當大的幫助。此外,ObjectDataSource可將網站架構中,前端網頁程式儘量只負責顯示(View)的工作,與業務資料處理邏輯元件獨立開,網頁設計人員與程式開發人員可彼此獨立作業,相同的前端網頁呈現設計,也可很快的套用在不同的資料處理邏輯元件,而不須做很多修改,大大的降低了前端網頁程式(aspx)中還要去處理業務與資料存取的複雜性。

沒有留言:

張貼留言