基於存儲過程的MVC開源分頁控件--LYB.NET.SPPager


摘要

    現在基於ASP.NET MVC的分頁控件我想大家都不陌生了,百度一下一大籮筐。其中有不少精品,陝北吳旗娃楊濤大哥做的分頁控件MVCPager(http://www.webdiyer.com/)算作當下開源里面的佼佼者,曾經在使用過程中感覺效果非常棒,拜讀其源碼也受益非淺。但我在使用其中的linq進行分頁操作時,如下:

                var log = from m in db.TESTA
                          join n in db.TESTB on m.id equals n.id
                          select new ResultModel
                          {
                              ....
                          };  //linq聯合查詢
                return log.ToPagedList(pageindex ?? 1, 20);

    發現當數據量在百萬級的時候,性能下降非常厲害,追蹤了一下代碼發現linq的底層分頁用的是Row_Number分頁,當數據量變大時,性能會下降的厲害。(具體分頁性能可以參考我文章后面那位兄弟的總結,不過貌視不怎么准,這不是我篇文章的重點,有興趣的可以自己測試一下,反正我在用的時候百萬數據,點任意一頁都要等5S左右⊙﹏⊙‖∣)。

    於是寫下LYB.NET.SPPager控件,並提交到CodePlex,希望大家多提意見。

    項目源地址:https://lybpager.codeplex.com/

    項目下載地址:https://lybpager.codeplex.com/releases 其中包括分頁控件源碼,演示工程,分頁控件DLL

需求分析

    在一般的網頁開發中,在數據庫中寫通用分頁存儲過程,然后在WEB中調用是一種最常見的方法。於是基本這個大前提下,規划出以下需求:

  •     1、控件可以實現有一個默認存儲過程,無需客戶端用戶關心存儲過程;
  •     2、控件可以擴展支持用戶自定義存儲過程;
  •     3、控件獲取的數據列表以List<T>的形式傳遞給客戶端,使用戶不用去操作諸如SqlDataReader之類與數據庫相關的東西;
  •     4、完成前端頁碼導航;

客戶端使用

最終調用效果(無樣式表,純天然的)

多頁效果

客戶調用代碼說明

使用到的默認分頁存儲過程.LYBPager可以在演示項目根目錄中找到。測試表數據結構(數據庫名:Shop)如下:

1、建一個基於MVC3.0的網站。文件結構如下圖所示:

 

2、控制器HomeController代碼如下:

using LYB.NET.SPPager;
using LYB.NET.WEBTEST.Models;

namespace LYB.NET.WEBTEST.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index(int? id)
        {
            string connection = "Data Source=玉寶;Initial Catalog=Shop;Integrated Security=True;Pooling=False";
            //采用默認LYBSPPager分頁存儲過程
            StoredProcedureBase sql = new LYBSPPager(connection)
            {
                TableName = "Products",
                PrimaryKey = "ProductId",
                SortField = "ProductId desc",
                OrderBy = 1
            };
            IRepository target = new SPRepository(sql); 

            int pageindex = id ?? 1;
            var actual = target.GetList<Product>(pageindex, 5);

            if (Request.IsAjaxRequest())
                return PartialView("PageList", actual);

            return View(actual);
        }

    }
}

3、查詢的數據模型Product如下:

using LYB.NET.SPPager;

namespace LYB.NET.WEBTEST.Models
{
    public class Product
    {
        [BindingFieldAttribute("ProductId")]
        public int ProductId { get; set; }
        [BindingFieldAttribute("ProductName")]
        public string ProductName { get; set; }
    }
}

采用C#自定義的特性BindingFieldAttribute實現數據庫字段與對象屬性進行綁定,從而實現為具體的數據庫操作分離;

4、主界面Index.cshtml如下:

@using LYB.NET.SPPager
@using LYB.NET.WEBTEST.Models
@model PageList<Product><h2>Index</h2>
@Html.Partial("PageList", Model)

5、分頁列表PageList.cshtml(部分視圖)代碼如下:

@using LYB.NET.SPPager
@using LYB.NET.WEBTEST.Models
@model PageList<Product>

<div id = "mainpage">
<table>
    <tr>
        <th>
            Product ID
        </th>
        <th>Product Name</th>
    </tr>

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.ProductId)
        </td>
        <td>
           @Html.DisplayFor(modelItem => item.ProductName)
        </td>
    </tr>
}

</table>
@Html.AjaxPager(Model, new PageOption { }, new AjaxOptions { UpdateTargetId="mainpage" })
</div>

 

到這里,完成全部的調用過程,點一下你的VS運行試試吧。

 

分頁源碼分析

源代碼文件結構如下:

類圖結構如下:

部分核心代碼解釋:

1、IPageList接口是參考楊濤大哥的MVCPager中的實現,能夠為前端頁碼導航提供當前頁碼、總記錄數、每頁記錄數待信息;

2、IRepository接口可以為將來控件擴展或用戶自己擴展時,提供獲取數據的接口,而不用改動WEB端調用代碼;

3、StoredProcedureBase是個抽象基類,如果用戶想使用自己的存儲過程,只要繼承這個類,並重寫以下兩個屬性,然后添加自己想要的屬性(即自定義存儲過程的參數)即可:

    public abstract class StoredProcedureBase
    {
        public abstract SqlParameter[] Parament { get; }
        public abstract int RecordCount { get; }
...//其它略 }

這里特別注意的是自定義存儲過程時,必須要有RecordCount這個參數,並為輸出類型,可以參考我的默認LYBSPPager的寫法

namespace LYB.NET.SPPager
{
    ///<summary>
    ///<para>默認LYBPager存儲過程</para> 
    ///<para>[帶*號為初始化時必須指定字段]</para>
    ///<para>*TableName --要顯示的表或多個表的連接 </para> 
    ///<para>*PrimaryKey --用於排序的主鍵  </para>
    ///<para>*SortField --用於排序,如:id desc (多個id desc,dt asc)</para>
    ///<para>QueryField --要查詢出的字段列表,*表示全部字段  </para>
    ///<para>Where --查詢條件,不需where  </para>
    ///<para>OrderBy --排序,0-順序,1-倒序,與SortField保持一致  </para>
    ///<para>RecordCount --查詢到的總記錄數[readonly] </para> 
    /// </summary>
    public class LYBSPPager : StoredProcedureBase
    {
        public string TableName { get; set; }
        public string QueryField { get; set; }
        public string PrimaryKey { get; set; }
        public string SortField { get; set; }
        public string Where { get; set; }
        public int OrderBy { get; set; }

        public LYBSPPager(string strconn)
            : base(strconn, ".LYBPager") { }

        /// <param name="strconn">連接字符串名稱</param>
        /// <param name="tablename">要顯示的表或多個表的連接 </param>
        /// <param name="primarykey">主鍵</param>
        /// <param name="sortfield">用於排序,如:id desc (多個id desc,dt asc)</param>
        public LYBSPPager(string strconn, string tablename, string primarykey, string sortfield)
            : base(strconn, ".LYBPager")
        {
            TableName = tablename;
            PrimaryKey = primarykey;
            SortField = sortfield;
        }

        public override SqlParameter[] Parament
        {
            get
            {
                if (string.IsNullOrEmpty(TableName))
                    throw new ApplicationException("LYBSPPager對象TableName屬性不能為空");

                if (string.IsNullOrEmpty(PrimaryKey))
                    throw new ApplicationException("LYBSPPager對象PrimaryKey屬性不能為空");

                if (string.IsNullOrEmpty(SortField))
                    throw new ApplicationException("LYBSPPager對象SortField屬性不能為空");

                SqlParameter[] para = new SqlParameter[]{  
                new SqlParameter("@strTable",SqlDbType.VarChar,-1),  
                new SqlParameter("@strField",SqlDbType.VarChar,-1),  
                new SqlParameter("@pageSize",SqlDbType.Int),  
                new SqlParameter("@pageIndex",SqlDbType.Int),  
                new SqlParameter("@strSortKey",SqlDbType.VarChar,-1),  
                new SqlParameter("@strSortField",SqlDbType.VarChar,-1), 
                new SqlParameter("@strOrderBy",SqlDbType.Bit),  
                new SqlParameter("@RecordCount",SqlDbType.Int),  
                new SqlParameter("@strWhere",SqlDbType.VarChar,-1)
                };

                para[0].Value = TableName;
                para[1].Value = QueryField;
                para[2].Value = PageSize;
                para[3].Value = CurrentPageIndex;
                para[4].Value = PrimaryKey;
                para[5].Value = SortField;
                para[6].Value = OrderBy;
                para[7].Direction = ParameterDirection.Output;
                para[8].Value = Where;

                return para; 
            }
        }
        public override int RecordCount { get { return int.Parse(_command.Parameters["@RecordCount"].Value.ToString()); } }
    }
}

總結

    第一次寫C#的DLL,代碼中存在很多不完善的地方,還請志同道合的同仁一起幫忙。謝謝大家。

    項目中存儲過程用的是MAX的分頁,在100W級數據測試下,速度還是非常快的。關於存儲過程分頁速度方面,有位兄弟已經做過類似總結:http://www.cnblogs.com/yangyy753/archive/2013/01/23/2872753.html

    最后,說一下關於分頁這個東西,在很多人看來,這是個很小的東西。看過有些弟兄的評論說:一個分頁至於整的這么復雜嗎?其實不怕各位高手笑話,我以前分頁確實都是直接寫的,方便,快速,后來發現每次都要自己重新寫。做了很多重復的工作。直到用了楊濤大哥的分頁,才告別這種COPY代碼的生活。

   

 

---工作也有好幾年了,做過ASP,弄過嵌入式開發,寫過單片機,整過無線通信協議,該靜下心來了,為了自己的事業,加油!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM