zoukankan      html  css  js  c++  java
  • CS前台分页功能实现

    前台:浏览一个Thread的内容

    http://192.168.18.153:81/forums/t/3.aspx?PageIndex=1

    image001.png
    其中的分页部分,是我们要研究的

    image003.png 

    根据网址,实际上是用了正则表达式。

    查看SiteUrls.config,找到

        <url name="thread_Paged" path="t/{0}.aspx?PageIndex={1}" pattern="t/(\d+).aspx?p=(\d+)" physicalPath="##themeDir##" vanity="{2}?ThreadID=$1^PageIndex=$2" page="thread.aspx" />

    <url name="thread" path="t/{0}.aspx" pattern ="t/(\d+).aspx" physicalPath="##themeDir##" vanity="{2}?ThreadID=$1" page="thread.aspx" />

    这里匹配第1个,再细节,我也不考虑了,最终应该定位到

    /Theme/Default/Forums/thread.aspx页面,这与你的Theme目录有关系。


    void
    Page_Init()

        {

            if (CurrentThread != null)

            {

                if (ChangeViewPopupMenu.GetPostViewType(CurrentThread) == PostViewType.Threaded)

                    ViewContainer.Controls.Add(Page.LoadControl("thread-threadedview.ascx"));

                else

                    ViewContainer.Controls.Add(Page.LoadControl("thread-flatview.ascx"));

            }

    Page_Init中会根据当前的浏览类型,决定是以 树型结构还是 平板结构 显示页面

    Flatview形式讨论:

    再查看thread-flatview.ascx页面,这是一个用户控件

            <div class="CommonFormArea">

                 <table cellpadding="0" cellspacing="0" border="0" width="100%">

                      <tr>

                           <td colspan="2" align="right" class="CommonFormField CommonPrintHidden">

                               <CSControl:Pager runat="server" id="TopPager" ShowTotalSummary="true" Tag="Div" CssClass="CommonPagingArea" align="right" />

                           </td>

                      </tr>

    注意,这里有个<CSControl:Pager>用户控件,idTopPager

    在页面的下端,

    <CSControl:PagerGroup runat="server" Id="Pager" PagerIds="TopPager,BottomPager" />

            <CSControl:Pager runat="server" id="BottomPager" ShowTotalSummary="true" Tag="Div" CssClass="CommonPagingArea" align="right" />

    这里定义了<CSControl:PagerGroup> <CSControl:Pager>,从字面意思,PageGroup控制所有的Pager

     

    从官方网站,有篇文章专门讲述了一个CS Pager

     

            <CSForum:ForumPostList runat="server" ID="PostList">

                <QueryOverrides SortBy="PostDate" PagerID="Pager" />

                <HeaderTemplate><ul class="ForumPostList"></HeaderTemplate>

                <ItemTemplate>

    从这里开始,主要就是显示Post列表了,用了一个Table,左边一列显示 ForumPostUserArea

    右边一列分成两行,分别显示ForumPostTitleArea ForumPostBodyArea

     

    如果查看CSForm:ForumPostList控件

    CommunityServerForums20项目下 Controls\ForumPost\ForumPostList.cs 源程序

    public override object DataSource 函数中有


                       
    query.PageIndex = 0;

                        query.PageSize = ForumConfiguration.Instance().PostsPerPage;

     

                        if (this.QueryOverrides != null)

                            this.QueryOverrides.ApplyQueryOverrides(query);

     

                        if (query.PostID > 0)

                        {

                            PostSet posts = Posts.GetPosts(query);

                            _forumPosts = new List<ForumPost>();

                           

                            foreach (ForumPost p in posts.Posts)

                                _forumPosts.Add(p);

     

                            if (this.QueryOverrides != null && this.QueryOverrides.Pager != null)

                            {

                                this.QueryOverrides.Pager.PageIndex = query.PageIndex;

                                this.QueryOverrides.Pager.PageSize = query.PageSize;

                                this.QueryOverrides.Pager.TotalRecords = posts.TotalRecords;

                                this.QueryOverrides.Pager.OnPageIndexChanged += new PagerEventHandler(this.PageIndexChanged);

                                this.QueryOverrides.Pager.DataBind();

                            }

                        }

                        else if (this.QueryOverrides != null && this.QueryOverrides.Pager != null)

                        {

                            this.QueryOverrides.Pager.TotalRecords = 0;

                            this.QueryOverrides.Pager.DataBind();

                        }

    当然,我们可以看到CS2007中用了泛型,这与2.1不同,2.1还是用了ArrayList,这样性能肯定能得到很大的提升,少了装箱拆箱环节。

     

    PostSet posts = Posts.GetPosts(query);这一句比较重要

    查看一下定义

     

            public static PostSet GetPosts(ForumPostQuery query)

            {

                return GetPosts(query.PostID, query.PageIndex, query.PageSize, query.SortBy, query.SortOrder);

            }

     

    最终定位到这样一个方法

            // *********************************************************************

            //  GetPosts

            //

            /// <summary>

            /// This method returns a listing of the messages in a given thread using paging.

            /// </summary>

            /// <param name="PostID">Specifies the PostID of a post that belongs to the thread that we are

            /// interested in grabbing the messages from.</param>

            /// <returns>A PostCollection containing the posts in the thread.</returns>

            ///

            // ********************************************************************/

             public static PostSet GetPosts(int postID, int pageIndex, int pageSize, int sortBy, int sortOrder, bool includeCategories)

            {

                PostSet postSet = null;

                string key = "Forum-Posts::P:{0}-PI:{1}-PS:{2}-SB:{3}-SO:{4}-C:{5}";

                string postCollectionKey = string.Format(key,postID,pageIndex,pageSize, sortBy, sortOrder, includeCategories);

     

                CSContext csContext = CSContext.Current;

                postSet = csContext.Items[postCollectionKey] as PostSet;

     

                if(postSet == null)

                    postSet = CSCache.Get(postCollectionKey) as PostSet;

               

     

                if (postSet == null) {

                    // Create Instance of the CommonDataProvider

                    ForumDataProvider dp = ForumDataProvider.Instance();

     

                    postSet = dp.GetPosts(postID, pageIndex, pageSize, sortBy, sortOrder, CSContext.Current.User.UserID, true, includeCategories);

     

                    csContext.Items[postCollectionKey] = postSet;

     

                    if(pageIndex == 0)

                        CSCache.Insert(postCollectionKey,postSet,6);

                }

     

                return postSet;

            }

     

    先从Content取,取不到再到Cache取,再取不到,才到数据库取。

    从这里可以看出,CS充分考虑了性能,运用缓存和分页

     

    ForumDataProvider dp = ForumDataProvider.Instance();

                    postSet = dp.GetPosts(postID, pageIndex, pageSize, sortBy, sortOrder, CSContext.Current.User.UserID, true, includeCategories);

    这一段用了一个Provider模式,具体的实现在SqlDataProvider20项目中,这里主要考虑跨数据库。CS也充分运用了设计模式,的确,老外写的程序真的不一样,不是我崇洋媚外,我是发自内心的。

     

    写到这里,谈谈国货,特别是手机,国产的就不要买了。要买就买Nokia,上次移动充话费,300送个Nokia 1116,唉,我第1个手机是Nokia 3110,到现在还很好用,还给我岳父用着。如果你没钱,就用移动送的,再不行,就买个最便宜的Nokia,比什么都好,有钱,就买Nokia价位好点的,它的质量你绝对不要怀疑。

    从高到低通吃,想不占领市场都难。

     

    再查看GetPosts的具体实现

    SqlDataProvider20项目下的 ForumsSqlDataProvider.cs类中

            #region GetPosts

            /// <summary>

            /// Returns a collection of Posts that make up a particular thread with paging

            /// </summary>

            /// <param name="postID">The ID of a Post in the thread that you are interested in retrieving.</param>

            /// <returns>A PostCollection object that contains the posts in the thread.</returns>

            ///

            public override PostSet GetPosts(int postID, int pageIndex, int pageSize, int sortBy, int sortOrder, int userID, bool returnRecordCount, bool includeCategories)

            {

     

                // Create Instance of Connection and Command Object

                //

                using( SqlConnection myConnection = GetSqlConnection() )

                {

                    SqlCommand myCommand = new SqlCommand(databaseOwner + ".cs_forums_Posts_PostSet", myConnection);

                    myCommand.CommandType = CommandType.StoredProcedure;

                    PostSet postSet = new PostSet();

     

                    // Set parameters

                    //

                    myCommand.Parameters.Add("@PostID", SqlDbType.Int).Value            = postID;

                    myCommand.Parameters.Add("@PageIndex", SqlDbType.Int).Value         = pageIndex;

                    myCommand.Parameters.Add("@PageSize", SqlDbType.Int).Value          = pageSize;

                    myCommand.Parameters.Add("@SortBy", SqlDbType.Int).Value            = sortBy;

                    myCommand.Parameters.Add("@SortOrder", SqlDbType.Int).Value         = sortOrder;

                    myCommand.Parameters.Add("@UserID", SqlDbType.Int).Value            = userID;

                    myCommand.Parameters.Add("@ReturnRecordCount", SqlDbType.Bit).Value = returnRecordCount;

                       myCommand.Parameters.Add("@IncludeCategories", SqlDbType.Bit).Value = includeCategories;

                    myCommand.Parameters.Add(this.SettingsIDParameter());

     

                    // Execute the command

                    //

                    myConnection.Open();

                    using(SqlDataReader reader = myCommand.ExecuteReader(CommandBehavior.CloseConnection))

                    {

     

                        // Get the results

                        //

                        IList<int> authors = new List<int>();

                        while (reader.Read())

                        {

                            authors.Add(Convert.ToInt32(reader["UserID"]));

                            postSet.Posts.Add(PopulatePostFromIDataReader(reader));

                        }

     

                        Users.AddUsersToCache(authors);

     

                           if (includeCategories)

                           {

                                reader.NextResult();

     

                                Hashtable categoryLookupTable = new Hashtable();

     

                                while(reader.Read())

                                {

                                     int cPostID = (int) reader["PostID"];

     

                                     if (categoryLookupTable[cPostID] == null)

                                     {

                                         categoryLookupTable[cPostID] = new ArrayList();

                                     }

     

                                     ((ArrayList) categoryLookupTable[cPostID]).Add(reader["Name"]);

                                }

     

                                // Map categories to the threads

                                //

                                foreach (ForumPost post in postSet.Posts)

                                {

                                     if (categoryLookupTable.ContainsKey(post.PostID))

                                         post.Categories = (string[]) ((ArrayList) categoryLookupTable[post.PostID]).ToArray(typeof(string));

                                }

                           }

     

                        // Are we expecting more results?

                        //

                        if ((returnRecordCount) && (reader.NextResult()) )

                        {

                            reader.Read();

     

                            // Read the value

                            //

                            postSet.TotalRecords = (int) reader[0];

                        }

                        reader.Close();

                    }

     

                    myConnection.Close();

     

                    return postSet;

                }

            }

     

    用了存储过程cs_forums_Posts_PostSet

    查看定义

    set ANSI_NULLS ON

    set QUOTED_IDENTIFIER ON

    GO

    ALTER PROCEDURE [dbo].[cs_forums_Posts_PostSet]

    (

        @PostID    int,

        @PageIndex int,

        @PageSize int,

        @SortBy int,

        @SortOrder bit,

        @UserID int,

        @ReturnRecordCount bit,

        @AllowUnapproved bit = 0,

        @SettingsID int,

        @IncludeCategories bit = 0

    )

    AS

    SET Transaction Isolation Level Read UNCOMMITTED

    BEGIN

     

    DECLARE @PageLowerBound int

    DECLARE @PageUpperBound int

    DECLARE @ThreadID int

    DECLARE @SectionID int

     

    -- First set the rowcount

    DECLARE @RowsToReturn int

    SET @RowsToReturn = @PageSize * (@PageIndex + 1)

    SET ROWCOUNT @RowsToReturn

     

    -- Set the page bounds

    SET @PageLowerBound = @PageSize * @PageIndex

    SET @PageUpperBound = @PageLowerBound + @PageSize + 1

     

    -- Get the ThreadID

    SELECT

        @ThreadID = ThreadID,

        @SectionID = SectionID

    FROM

        cs_Posts

    WHERE

        PostID = @PostID and SettingsID = @SettingsID

     

    -- Is the Forum 0 (If so this is a private message and we need to verify the user can view it

    IF @SectionID = 0

    BEGIN

        IF NOT EXISTS (SELECT UserID FROM cs_PrivateMessages WHERE UserID = @UserID AND ThreadID = @ThreadID AND SettingsID = @SettingsID)

           RETURN

    END

     

    -- Create a temp table to store the select results

    CREATE TABLE #PageIndex

    (

        IndexID int IDENTITY (1, 1) NOT NULL,

        PostID int

    )

     

    -- Sort by Post Date

    IF @SortBy = 0 AND @SortOrder = 0

        INSERT INTO #PageIndex (PostID)

        SELECT PostID FROM cs_Posts (nolock) WHERE (IsApproved = 1 OR 1 = @AllowUnapproved) AND ThreadID = @ThreadID and SettingsID = @SettingsID ORDER BY PostDate

     

    ELSE IF @SortBy = 0 AND @SortOrder = 1

        INSERT INTO #PageIndex (PostID)

        SELECT PostID FROM cs_Posts (nolock) WHERE (IsApproved = 1 OR 1 = @AllowUnapproved) AND ThreadID = @ThreadID and SettingsID = @SettingsID  ORDER BY PostDate DESC

     

    -- Sort by Author

    IF @SortBy = 1 AND @SortOrder = 0

        INSERT INTO #PageIndex (PostID)

        SELECT PostID FROM cs_Posts (nolock) WHERE (IsApproved = 1 OR 1 = @AllowUnapproved) AND ThreadID = @ThreadID and SettingsID = @SettingsID  ORDER BY UserID

     

    ELSE IF @SortBy = 1 AND @SortOrder = 1

        INSERT INTO #PageIndex (PostID)

        SELECT PostID FROM cs_Posts (nolock) WHERE (IsApproved = 1 OR 1 = @AllowUnapproved) AND ThreadID = @ThreadID and SettingsID = @SettingsID  ORDER BY UserID DESC

     

    -- Sort by SortOrder

    IF @SortBy = 2 AND @SortOrder = 0

        INSERT INTO #PageIndex (PostID)

        SELECT PostID FROM cs_Posts (nolock) WHERE (IsApproved = 1 OR 1 = @AllowUnapproved) AND ThreadID = @ThreadID and SettingsID = @SettingsID  ORDER BY SortOrder

     

    ELSE IF @SortBy = 2 AND @SortOrder = 1

        INSERT INTO #PageIndex (PostID)

        SELECT PostID FROM cs_Posts (nolock) WHERE (IsApproved = 1 OR 1 = @AllowUnapproved) AND ThreadID = @ThreadID and SettingsID = @SettingsID  ORDER BY SortOrder DESC

     

    -- Select the individual posts

    SELECT

        P.PostID, P.ThreadID, P.ParentID, P.PostAuthor, P.UserID, P.SectionID, P.PostLevel, P.SortOrder, P.Subject, P.PostDate, P.IsApproved,

        P.IsLocked, P.IsIndexed, P.TotalViews, P.Body, P.FormattedBody, P.IPAddress, P.PostType, P.PostMedia, P.EmoticonID, P.SettingsID, P.AggViews,

        P.PropertyNames as PostPropertyNames, P.PropertyValues as PostPropertyValues,

        P.PostConfiguration, P.UserTime, P.ApplicationPostType, P.PostName, P.PostStatus, P.SpamScore,

        P.Points as PostPoints, P.RatingSum as PostRatingSum, P.TotalRatings as PostTotalRatings,

        T.*, U.*, #PageIndex.*,

        T.IsLocked,

        T.IsSticky,

        Username = P.PostAuthor,

        ThreadStarterAuthor = T.PostAuthor,

        ThreadStartDate = T.PostDate,  

        EditNotes = (SELECT EditNotes FROM cs_PostEditNotes WHERE PostID = P.PostID),

        AttachmentFilename = ISNULL ( (SELECT [FileName] FROM cs_PostAttachments WHERE PostID = P.PostID), ''),

        Replies = 0, --not used(SELECT COUNT(P2.PostID) FROM cs_Posts P2 (nolock) WHERE P2.ParentID = P.PostID AND P2.PostLevel != 1),

        IsModerator = 0, -- not used

        HasRead = 0 -- not used

    FROM

        cs_Posts P (nolock),

        cs_Threads T,

        cs_vw_Users_FullUser U,

        #PageIndex

    WHERE

        P.PostID = #PageIndex.PostID AND

        P.UserID = U.UserID AND

        T.ThreadID = P.ThreadID AND

        #PageIndex.IndexID > @PageLowerBound AND

        #PageIndex.IndexID < @PageUpperBound and U.SettingsID = @SettingsID

    ORDER BY

        IndexID

    END

     

    IF @IncludeCategories = 1

    BEGIN

        SET ROWCOUNT 0

        SELECT

           Cats.[Name], jPI.PostID

        FROM

           #PageIndex jPI

           JOIN cs_Posts_InCategories PIC ON jPI.PostID = PIC.PostID

           JOIN cs_Post_Categories Cats ON PIC.CategoryID = Cats.CategoryID

        WHERE

           jPI.IndexID > @PageLowerBound

           AND jPI.IndexID < @PageUpperBound

           AND  PIC.SettingsID = @SettingsID

    End

     

    IF @ReturnRecordCount = 1

      SELECT count(PostID) FROM cs_Posts (nolock) WHERE (IsApproved = 1 OR 1 = @AllowUnapproved) AND ThreadID = @ThreadID  and SettingsID = @SettingsID

     

     

    可以看到,SET ROWCOUNT @RowsToReturn,临时表,连接查询,分页算法,在大数据量时,应该也不错,最后一句返回记录总数。但现在SQL 2005支持RowNum,我上网看了一下,赞扬和批评的声音都有。分页确实很重要,我现在优化海学网,最先想到的就是分页和缓存。先研究一下分页。

     

    上层程序通过SqlDataReader readerreader.NextResult(),封装返回的结果集和记录总数。

    底层实现差不多就这样,现在再看上层展示。

    public class ForumPostList : PreTemplatedListBase

    public abstract class PreTemplatedWrappedRepeaterBase : WrappedRepeater

    public class WrappedRepeater : Repeater, IAttributeAccessor

    最终我们可以看到ForumPostList扩展了Repeater控件

    PreTemplatedWrappedRepeaterBase类的

    protected override void OnLoad(EventArgs e)

            {

                base.OnLoad(e);

     

                if (!_isDataBound)

                    DataBind();

            }

    最终会调用DataBind()函数,实现页面数据绑定

    ForumPostList中改写DataSource函数。

     

    <CSForum:ForumPostList runat="server" ID="PostList">

                <QueryOverrides SortBy="PostDate" PagerID="Pager" />控件中

    加入了一个QueryOverrides 属性

    public virtual ForumPostQuery QueryOverrides

            {

                get { return _queryOverrides; }

                set

                {

                    if (_queryOverrides != null)

                        this.Controls.Remove(_queryOverrides);

     

                    _queryOverrides = value;

                    if (_queryOverrides != null)

                        this.Controls.Add(_queryOverrides);

                }

            }

    这个属性是ForumPostQuery控件

     public class ForumPostQuery : System.Web.UI.Control

    本质上这是一个传递属性字段的容器,这是我的理解

    if (this.QueryOverrides != null && this.QueryOverrides.Pager != null)

                            {

                                this.QueryOverrides.Pager.PageIndex = query.PageIndex;

                                this.QueryOverrides.Pager.PageSize = query.PageSize;

                                this.QueryOverrides.Pager.TotalRecords = posts.TotalRecords;

                                this.QueryOverrides.Pager.OnPageIndexChanged += new PagerEventHandler(this.PageIndexChanged);

                                this.QueryOverrides.Pager.DataBind();

                            }

     

    this.QueryOverrides.Pager.DataBind();这句话是在绑定分页控件

     

    查来查去

    public abstract class WrappedContentBase : Control, IAttributeAccessor, INamingContainer

    抽象类中有这样一个方法,调用CreateChildControls(),这个方法会一层一层调用,最终调用

        public abstract class PreTemplatedPagerBase : WrappedContentBase, IPager, IDataItemsContainer

    这个类的protected override void BindDefaultContent(Control control, IDataItemContainer dataItemContainer)方法,这样,我们的分页控件的内容就输出来了。

     

    protected override void OnDataBinding(EventArgs e)

            {

                if (RecreateChildControlsOnDataBind)

                {

                    this.ChildControlsCreated = true;

                    this.CreateChildControls();

                }

                else

                    EnsureChildControls();

     

                base.OnDataBinding(e);

            }

     

    public class PagerGroup : Control, IPager

    类中用了代理,

            protected override void OnInit(EventArgs e)

            {

                base.OnInit(e);

     

                this.Page.InitComplete += new EventHandler(Page_InitComplete);

            }

     

            void Page_InitComplete(object sender, EventArgs e)

            {

                string[] ids = this.PagerIds.Split(',');

                foreach (string id in ids)

                {

                    IPager pager = CSControlUtility.Instance().FindControl(this, id) as IPager;

                    if (pager != null)

                        pager.OnPageIndexChanged += new PagerEventHandler(PagerGroup_OnPageIndexChanged);

                }

            }

     

    当里面的任何一个分页控制,发生页数更改时,就会调用这个Group类,Group再修改所有分页控件的样式。因为分页是采用超链接,我想这个代理仅会发生在页面载入时。

     

    这里是“窥一斑而见全豹”,还有很多要学的,再看看吧。

     

    再来分析后台分页功能,后台用了

    <add tagPrefix="CA" namespace="ComponentArt.Web.UI" assembly="ComponentArt.Web.UI"/> Grid控件。

     image005.png

    其实这里没什么好说的,取数据还是存储过程取,表现用了ComponentArt控件,就方便多了,实现的原理和ComponentArt自带的例子里的一个Manual Page一样,就是手工分页。

     

  • 相关阅读:
    Spring boot 请求接口404
    Windows下安装Redis
    利用maven的profiles灵活的配置多环境
    各个JSON技术的比较
    常用工具软件
    Java方法 传值方式
    JVM内存模型
    spring-task解决定时问题
    quartz Cron表达式解读
    maven 常用命令
  • 原文地址:https://www.cnblogs.com/huang/p/989692.html
Copyright © 2011-2022 走看看