الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

Efficient Paging

بدأه Robatic في 15 يونيو 2010 · 5 رد · 2,236 مشاهدة · في ASP.NET
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكم ورجمة الله وبركاته

من منا لا يعرف خاصية PageSize أو AllowPaging الموجودة في الـ GridView، وكلنا نعرف أنها تقوم بتقسيم البيانات المستردة من قاعدة البيانات حيث يتم عرضها بطريقة سلسة تعطي مظهر جيد للصفحة الإلكترونية وخصوصا لو كانت هناك أزرار للتنقل مثل التالي والسابق والأخير والأول أو أرقام الصفحات المتوفرة. ولكن دعونا نفكر قليلا ونسأل هل الغرض من هذا التكتيك هو فقط المظهر أم زيادة الفعالية والآداء ؟ هل حقا عندما تقوم بتحديد حجم الصفحة PageSize سيتم إسترجاع فقط العدد المذكور من قاعدة البيانات أم أنه سيتم إسترجاع كافة البيانات ثم ستقوم الـ GridView بعملية التقسيم ؟

سأقوم بشرح طريقة بسيطة جدا تقوم بإسترجاع الصفوف المطلوبة من قاعدة البيانات مباشرة ولكن للأسف الشديد هذه الطريقة لا تصلح إلا لو كان خادم قاعدة البيانات هو MS SQL Server، والسر يكمن لوجود داله إسمها ROW_NUMBER تقوم بإسترجاع رقم الصف بناء على حقل يتم تحديده. لكي تتضح الفكرة سأقوم ببناء الجدول التالي والذي سأسميه people :

post-91978-005280100 1276609801_thumb.jp

بعد ذلك سأقوم بإنشاء الــ Stored Procedure التالي :

create proc get_paged_data

@page_index int,
@number_of_rows int,
@row_count int output

as

begin

select @row_count = (select count(id) from people)

declare @starting_index int;

set @starting_index = (@page_index * @number_of_rows) + 1;

with entries as (
	select ROW_NUMBER() over (order by id asc) as row, id, [name], entry_date
	from people
)

select id, [name], entry_date
from entries
where row between @starting_index and @starting_index + @number_of_rows - 1


end

حيث أنه يستقبل 3 Parameters الأول يمثل رقم الصفحة والثاني يمثل عدد السجلات في الصفحة والثالث يتم تخزين عدد السجلات الكلي والذي سوف أستخدمه لعرض عدد السجلات حتى أتمكن من إظهار معلومات أكثر مثل "يتم عرض عدد كذا من مجموع كذا". كذلك سأنشيء صفحة الغرض منها إدخال عدد سجلات عشوائية حسب ما يتم إدخاله في TextBox ستكون الصفحة بإسم insert.aspx وهذا هو الكود :

<!-- Design Code -->

<h4 style="text-align: center">Inserting new data</h4>
    <table cellpadding="5" cellspacing="0" border="0" style="border: 1px solid #000; width: 330px; margin: 10px auto;">
        <tr>
            <td>Number of Rows: </td>
            <td><asp:TextBox ID="numberOfRowsTextBox" runat="server" /></td>
        </tr>
        <tr>
            <td> </td>
            <td style="text-align: right;"><asp:Button ID="insertButton" runat="server" Text="Submit" OnClick="insertButton_Click" /></td>
        </tr>
        <tr>
            <td colspan="2" style="font-size: 10px;">
                <asp:Label ID="statusLabel" runat="server" />
            </td>
        </tr>
    </table>


//  Code Behind

protected void insertButton_Click(object source, EventArgs e)
    {
        using (SqlConnection con = new SqlConnection(
            ConfigurationManager.ConnectionStrings["DbConnection"].ConnectionString))
        {
            SqlTransaction trans = null;

            try
            {
                con.Open();

                trans = con.BeginTransaction();

                int rowCount = int.Parse(numberOfRowsTextBox.Text);

                for (int i = 0; i < rowCount; i++)
                {
                    SqlCommand cmd = new SqlCommand("insert into people([name]) values('Auto Entry!')", con);
                    cmd.Transaction = trans;
                    cmd.ExecuteNonQuery();
                }

                trans.Commit();

                statusLabel.Text = string.Format("{0} row have/hase been inserted", rowCount);
            }
            catch (Exception ex)
            {
                if (trans != null)
                    if (trans.Connection.State == ConnectionState.Open)
                        trans.Rollback();

                statusLabel.Text = ex.Message;
            }
        }
    }

أيضا سأضيف صفحة data.aspx والتي فيها سيتم عرض البيانات، وهذا الكود المتعلق بها :

<!-- Design Code -->

<h4 style="text-align: center;">
        Display data</h4>
    <asp:SqlDataSource ID="peopleDataSource" runat="server" ConnectionString="<%$ ConnectionStrings:DbConnection %>"
        SelectCommand="get_paged_data" SelectCommandType="StoredProcedure" 
        onselected="peopleDataSource_Selected">
        <SelectParameters>
            <asp:QueryStringParameter Name="page_index" DefaultValue="0" Type="Int32" QueryStringField="i" />
            <asp:Parameter Name="number_of_rows" DefaultValue="15" Type="Int32" />
            <asp:Parameter Name="row_count" Direction="Output" Type="Int32" />
        </SelectParameters>
    </asp:SqlDataSource>

    <div style="margin: 0 auto; width: 500px; border: 1px solid #07f">
        <asp:ListView ID="peopleListView" runat="server" DataSourceID="peopleDataSource" ItemPlaceholderID="itemPlaceHolder">
            <LayoutTemplate>                
                <ul>
                    <asp:PlaceHolder ID="itemPlaceHolder" runat="server" />
                </ul>
                <p id="pager" runat="server" style="padding: 3px; word-wrap: break-word;" />
                <p style="text-align: right; padding: 3px;" id="rowCount" runat="server"></p>
            </LayoutTemplate>
            <ItemTemplate>
                <li>
                    <%# Eval("id") %> : <%# Eval("name") %> <p style="display: inline; font-size: 8px; color: #77f;">(<%# Eval("entry_date") %>)</p>
                </li>
            </ItemTemplate>
        </asp:ListView>
    </div>

// Code Behind

protected void peopleDataSource_Selected(object sender, SqlDataSourceStatusEventArgs e)
    {
        HtmlContainerControl htmlControl =
            (HtmlContainerControl)peopleListView.FindControl("rowCount");

        int rowCount = int.Parse(e.Command.Parameters["@row_count"].Value.ToString());

        if (htmlControl != null)
            htmlControl.InnerHtml = 
                string.Format("Record(s) Count: {0}", rowCount);

        RenderPager(rowCount);
    }

    protected void RenderPager(int rowCount)
    {
        int pages = rowCount / 15;

        int currentPage = Request["i"] != null ? int.Parse(Request["i"]) : 0;

        string pattern = "<a href='data.aspx?i={0}'>{1}</a>";

        string html = null;

        for (int i = 0; i < pages; i++)
            html += (i != currentPage ? string.Format(pattern, i, i + 1) : (i + 1).ToString()) + " ";

        HtmlContainerControl htmlControl = (HtmlContainerControl)peopleListView.FindControl("pager");

        if (htmlControl != null)
            htmlControl.InnerHtml = html;
    }

في المفرقات ستجدون المشروع كامل

أتمنى للجميع التوفيق والنجاح

EfficientPaging.zip

المرفقات
table.JPG
3
#2

جميل جدا وربنا يجزيك الخير

سرعة جيدة جدا بالمقارنة مع الgridview المنشأ بشكل عادي

لازم تكون عامل زمن انشاء الصفحة :wink: عما امزح

على فكرة من قبل كنت اسمع (طبعا لم ابحث) انه يتم جلب بيانات ال gridview دفعة واحدة ومن تقسيمها على جهاز الزبون

هل هذا الكلام صحيح ام لا

بالتوفيق وللامام ...

تم تعديل هذه المشاركة بواسطة X-File في 15 يونيو 2010 في 20:25

In bad state

#3

السلام عليكم ورحمة الله وبركاته

الأخ X-File شكرا جزيلا على مرورك الكريم

ما سمعته صحيح بناء على المقتبس التالي

اقتباس
The ASP.NET 1.x DataGrid had two modes for paging through data: default paging and custom paging. With both paging models you, the page developer, needed to specify the number of records to display per page and the zero-based index of the page whose records were to be displayed. Default paging worked by having the entire contents of the data to be paged through assigned to the DataGrid's DataSource property. The DataGrid then determined the correct subset of records to display, discarding the rest. While default paging was easy to implement, it carried with it a performance cost since all records to be paged through were being returned from the database. That is, if the DataGrid was paging through a total of 1,000 records, showing 10 records per page, on each and every page request all 1,000 records would be returned from the database, but only the 10 appropriate ones would be displayed.

Custom paging solved this performance issue by requiring the page developer to tell the DataGrid exactly how many total records were being paged through as well as returning the precise subset of records to display on the page. With custom paging, even if there were a total of 1,000 records to page through, if only 10 records were being displayed per page, then only 10 records would be retrieved from the database per page visit. The custom paging model offered better performance but required more code and effort on the page developer's end.

The ASP.NET 2.0 GridView can support both models of paging, but does not have an AllowCustomPaging property like the ASP.NET 1.x DataGrid. In the following two sections we'll look at how to specify the paging model used by the GridView with the SqlDataSource and ObjectDataSource.

ستجد الموضوع كاملا هنا

ولا تنسى أيضا أنه قبل وجود الـ DataPager ASP Control كان من الصعب عمل تقسيم البيانات لكل من الـ Repeater والـ DataList والذي من شأنهم إعطاء المبرمج أريحية أكبر في عرض البيانات بأي شكل كان، الطريقة المشروحه هنا تصلح لكل من GridView, DataList, ListView, Repeater ..... etc

ولكن قد قرأت أنه عندما تستخدم الـ LINQ سيتم فقط إسترجاع السجلات المطلوبة فقط ولكني لم أتأكد من ذلك بعد، بإذن الله سوف أقوم بتعديل المثال المرفق مسبقا بحيث يتم فيه إستخدام LINQ

أتنمى للجميع التوفيق

#4

بالتوفيق وربنا يجزيك الخير وبانتظار الشروحات التالية لك

:)

In bad state

#5
اقتباس
على فكرة من قبل كنت اسمع (طبعا لم ابحث) انه يتم جلب بيانات ال gridview دفعة واحدة ومن تقسيمها على جهاز الزبون

هل هذا الكلام صحيح ام لا

ليس على جهاز الزبون بل على السيرفر اي يتم سحب جميع البيانات المطابقة من قاعدة البيانات وتتم الفلترة على السيرفر الموجود عليه الموقع .

اقتباس
ولا تنسى أيضا أنه قبل وجود الـ DataPager ASP Control كان من الصعب عمل تقسيم البيانات لكل من الـ Repeater والـ DataList

DataPager تستخدم فقط مع Listview لذا مازال من الصعب التعامل مع Repeater و Datalist الا بطريقة مخصصة مثل التي تكلمت عنها .

اقتباس
ولكن قد قرأت أنه عندما تستخدم الـ LINQ سيتم فقط إسترجاع السجلات المطلوبة فقط ولكني لم أتأكد من ذلك بعد، بإذن الله سوف أقوم بتعديل المثال المرفق مسبقا بحيث يتم فيه إستخدام LINQ

الكلام صحيح حيث يتم عمل استعلامات معينة للحصول على النتيجة المطلوبة .

وملاحظة اخيرة وهي ROW_NUMBER() جاءت مع SQL 2005+ .

والله يعطيك العافية

1
#6
اقتباس
DataPager تستخدم فقط مع Listview لذا مازال من الصعب التعامل مع Repeater و Datalist الا بطريقة مخصصة مثل التي تكلمت عنها .

حتى نكون أكثر دقه فإن الـ DataPager يستخدم مع أي Control يقوم بعمل implement للـ IPageableItemContainer فيمكننا أن نقوم بصنع Server Control ومن ثم نقوم بتوريثه للـ Repeater و IPageableItemContainer كما هو في المثال التالي

سوف نقوم ببناء Server Control بإسم PageableRepeater

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace ServerControl1
{
    [DefaultProperty("Text")]
    [ToolboxData("<{0}:PageableRepeater runat=server PresistentDataSource=true></{0}:PageableRepeater>")]
    public class PageableRepeater : Repeater, IPageableItemContainer, INamingContainer
    {
        public int MaximumRows
        {
            get
            {
                return ViewState["_maximumRows"] != null ?
                    (int)ViewState["_maximumRows"] : -1;
            }
        }
        public int StartRowIndex
        {
            get
            {
                return ViewState["_startRowIndex"] != null ?
                    (int)ViewState["_startRowIndex"] : -1;
            }
        }
        public int TotalRows
        {
            get
            {
                return ViewState["_totalRows"] != null ?
                    (int)ViewState["_totalRows"] : -1;
            }
        }

        public bool PersistentDataSource
        {
            get
            {
                return ViewState["PersistentDataSource"] != null ?
                (bool)ViewState["PersistentDataSource"] : true;
            }
            set { ViewState["PersistentDataSource"] = value; }
        }

        protected override void LoadViewState(object savedState)
        {
            base.LoadViewState(savedState);

            if (Page.IsPostBack)
            {
                if (PersistentDataSource && ViewState["DataSource"] != null)
                {
                    this.DataSource = ViewState["DataSource"];
                    this.DataBind();
                }
            }
        }

        public void SetPageProperties(int startRowIndex, int maximumRows, bool databind)
        {
            ViewState["_startRowIndex"] = startRowIndex;
            ViewState["_maximumRows"] = maximumRows;

            if (TotalRows > -1)
            {
                if (TotalRowCountAvailable != null)
                {
                    TotalRowCountAvailable(this,
                       new PageEventArgs((int)ViewState["_startRowIndex"],
                       (int)ViewState["_maximumRows"], TotalRows));
                }
            }
        }

        protected override void OnDataPropertyChanged()
        {
            if (MaximumRows != -1)
            {
                this.RequiresDataBinding = true;
            }
            else
                base.OnDataPropertyChanged();
        }

        protected override void RenderChildren(HtmlTextWriter writer)
        {
            if (MaximumRows != -1)
            {
                foreach (RepeaterItem item in this.Items)
                {
                    if (item.ItemType == ListItemType.Item ||
                        item.ItemType == ListItemType.AlternatingItem)
                    {
                        item.Visible = false;

                        if (item.ItemIndex >= (int)ViewState["_startRowIndex"] &&
                            item.ItemIndex <= ((int)ViewState["_startRowIndex"] +
                              (int)ViewState["_maximumRows"]))
                        {
                            item.Visible = true;
                        }
                    }
                    else
                    {
                        item.Visible = true;
                    }
                }
            }
            base.RenderChildren(writer);
        }

        public override void DataBind()
        {
            base.DataBind();

            if (MaximumRows != -1)
            {
                int i = 0;
                foreach (object o in GetData())
                {
                    i++;
                }
                ViewState["_totalRows"] = i;

                if (PersistentDataSource)
                    ViewState["DataSource"] = this.DataSource;
                SetPageProperties(StartRowIndex, MaximumRows, true);
            }
        }

        protected override System.Collections.IEnumerable GetData()
        {
            return base.GetData();
        }

        public event System.EventHandler<PageEventArgs> TotalRowCountAvailable;

    }
}

بعد أن تقوم بعمل Build للمشروع سيتم إنشاء ملف DLL قم بإنشاء مشرع Web Site or Web Application ومن ثم قم بإضافة ملف الـ DLL الناتج من الكود في الأعلى إلى المشروع وبعد ذلك في الصفحة الإفتراضية Default.aspx قم بتسجيل الـ Server Control الذي تم إنشاءه في الكود في الأعلى

كما هو موضح أدناه

<!-- Design Code -->

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>

<%@ Register Assembly="ServerControl1" Namespace="ServerControl1" TagPrefix="cc1" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

        <cc1:PageableRepeater ID="myRepeater" runat="server">
            <ItemTemplate>
                <p><%# Eval("ID") %> <%# Eval("Name") %></p>
            </ItemTemplate>
        </cc1:PageableRepeater>
        <asp:DataPager ID="myPager" runat="server" PagedControlID="myRepeater" PageSize="10" >
            <Fields>
                <asp:NumericPagerField ButtonCount="10" ButtonType="Link" />
            </Fields>
        </asp:DataPager>
    </div>
    </form>
</body>
</html>


// Code Behind

using System;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            DataTable dt = new DataTable();
            dt.Columns.Add("ID", typeof(string));
            dt.Columns.Add("Name", typeof(string));


            for (int i = 1; i <= 100; i++)
                dt.Rows.Add(i, string.Format("Name #{0}", i));

            myRepeater.DataSource = dt;
            myRepeater.DataBind();
        }
    }
}

بعد ذلك قم بتشغيل المشروع ستلاحظ أنه تم تقسيم الـ Repeater إلى صفحات

أتمنى للجميع التوفيق

1

مواضيع مشابهة