解读System.Web.UI.Page中关键方法ProcessRequestMain()

     为了更好地优化博客园程序的性能,最近我在优化代码的同时,更深入地去研究asp.net的源代码。asp.net的源代码通过Reflector工具一鉴无遗, 虽然不是原版的代码,但已经足够了,其中的原理与思想已经清楚地摆在我们面前。这是.NET开发人员的幸运! 
    在我们开发asp.net应用程序时, System.Web.UI.Page是我们最熟悉并用的最多的一个类。但有多少人真正对这个类的源代码仔细研究过? 从相关搜索中可以看出并不是很多,比如,用Google搜索ProcessRequestMain方法中开始的“OnPageStartSessionObjects”,结果只有四个。
     今天我花了半天时间,研究了Page中处理请求的最关键的方法:ProcessRequestMain(),在这里我将自己的理解写出来与大家共享,欢迎大家批评并指正。一切尽在代码注释中:

注:为了方便阅读与理解,已去掉源代码中输出Trace信息的部分。

public class Page
{
    
private void ProcessRequestMain()
    
{
        
try
        
{
            
if (this.IsInAspCompatMode)
            
{
                AspCompatApplicationStep.OnPageStartSessionObjects();
            }

            
//将当前Context的Session中的对象传递给asp的OnStartPage
            
//通过<%@ ASPCOMPAT="true" %>进行设置
            
//参考文章: http://samples.gotdotnet.com/quickstart/aspplus/doc/cominterop.aspx

            
this._requestValueCollection = this.DeterminePostBackMode();
            
//检查PostBackMode, 如果启用了PostBack, 获取VIEWSTATE数据并赋值给_requestValueCollection

            
base.InitRecursive(null);
            
//调用基类的InitRecursive方法通过递归对子控件进行初始化, 比如: 生成控件ID,设置控件的Page属性。
            
//OnInit()方法将会在此时被调用
            
            
if (this.IsPostBack)
            
{
                
this.LoadPageViewState();
                
//从_requestValueCollection通过反序列化载入视图状态数据,如果页面的Layout发生了改变,子控件重新递归载入(LoadViewStateRecursive)视图状态
                
//载入后,从视图状态数据从得到所有要处理PostBack的控件并注册到_controlsRequiringPostBack. 

                
this.ProcessPostData(this._requestValueCollection, true);
                
//处理PostBack数据, 从PostBack数据中得到所有控件ID并检查每个控件,如果不能在当前页面中找到该控件(FindControl), 将其存入_leftoverPostData.
                
//如果存在该控件,继续检查,若该控件没有实现IPostBackDataHandler, 但实现了 IPostBackEventHandler,注册该控件进行事件处理。
                
//若该控件实现了System.Web.UI.IPostBackDataHandler,该控件的LoadPostData()方法在此时被调用,并将其加入到_changedPostDataConsumers,
                
//并从._controlsRequiringPostBack(LoadPageViewState时对它进行了赋值)中移除该控件.
                
//这样就从_controlsRequiringPostBack中移除了所有实现IPostBackDataHandler接口的控件.
                
//接着继续对 _controlsRequiringPostBack中余下的控件进行处理,但奇怪的是又对这些余下的控件检查是否存在并实现了IPostBackDataHandler, 如果实现,
                
//调用该控件的LoadPostData()(有点多此一举了, 可能是Relector生成的代码有误)并放入_changedPostDataConsumers,如果没实现,放入一个新ArrayList变量,
                    //检查结束后,将其赋值给
_controlsRequiringPostBack.那现在_controlsRequiringPostBack中剩下什么呢?没有实现IPostBackDataHandler, 
                    //但实现了 IPostBackEventHandler的控件以及
没有被Load的控件, 也就是在PostBack数据中存在但FindControl没有找到的控件。
                    //为什么会有找不到的控件呢?我们这里需要注意的是OnLoad()事件还没执行,
                
//有些控件还没有被加载.下面的base.LoadRecursive()就是触发OnLoad()事件的。

            }


            
base.LoadRecursive();
            
//触发页面的OnLoad()事件->递归触发子控件的OnLoad()事件->将页面的_controlState状态设置为ControlState.Loaded

            
if (this.IsPostBack)
            
{
                
this.ProcessPostData(this._leftoverPostData, false);
                
//理解了ProcessPostData(this._requestValueCollection, true)之后, 这个就很好理解了,就是检查_controlsRequiringPostBack中的控件是否存在并实现了
                
//IPostBackDataHandler, 如果实现,调用该控件的LoadPostData()方法并将其加入到_changedPostDataConsumers.ProcessPostData(this._leftoverPostData, false)
                
//这个方法就是为OnLoad()之后加载的控件服务的。
                     }
                
this.RaiseChangedEvents();
                
//在_changedPostDataConsumers(在两个ProcessPostData方法中向它添加了数据)中没有实现IPostBackDataHandler接口的控件触发RaisePostDataChangedEvent.

                
this.RaisePostBackEvent(this._requestValueCollection);
                
//触发_registeredControlThatRequireRaiseEvent及PostBack数据中实现IPostBackEventHandler接口的控件的RaisePostBackEvent事件。
        
        
            
base.PreRenderRecursiveInternal();
            
//首先调用EnsureChildControls,检查子控件是否创建, 如果没有, 调用进行创建CreateChildControls.
            
//触发OnPreRende事件.
            
//递归调用子控件的PreRenderRecursiveInternal方法
            
//设置._controlState为 ControlState.PreRendered

            
this.SavePageViewState();
            
//保存视图状态数据至._viewStateToPersist 

            
base.RenderControl(this.CreateHtmlTextWriter(this.Response.Output));
            
//输出当前及所有子控件的内容

        }

        
catch (ThreadAbortException)
        
{
            
base.UnloadRecursive(true);
            
return;
        }

        
catch (ConfigurationException)
        
{
            
throw;
        }

        
catch (Exception exception1)
        
{
            PerfCounters.IncrementCounter(AppPerfCounter.ERRORS_DURING_REQUEST);
            PerfCounters.IncrementCounter(AppPerfCounter.ERRORS_TOTAL);
            
if (!this.HandleError(exception1))
            
{
                
throw;
            }

            
return;
        }

    }

}

posted on 2005-10-21 17:02 dudu 阅读(4699) 评论(8)  编辑 收藏 网摘 所属分类: ASP.NET 1.1

评论

#1楼  2005-10-21 18:44 sm160.com      

分析得不错啊!   回复  引用  查看    

#2楼  2005-10-22 12:11 匿名 [未注册用户]

有什么用?能说明什么?

虽然看了你的分析.可还是不清楚~
  回复  引用    

#3楼 [楼主] 2005-10-22 15:15 dudu      

@匿名
可能是我分析得不够清楚。
我觉得理解这个对开发高性能asp.net应用程序是有帮助的。
  回复  引用  查看    

#4楼  2005-10-23 00:05 匿名 [未注册用户]

@ dudu

我不是针对你的分析不清楚.我只是搞不明白.这跟开发ASP.NET的性能有什么关系
我就是这点不明白.比如.利用这个类.应该怎么做才能提高ASP.NET性能:)
  回复  引用    

#5楼  2005-11-15 15:43 野草      

如果设计一个Customs Control,并通过Render方法输出<input>控件,并希望参与数据回传处理,即实现IPostBackDataHandler,从上面的分析就可看出,这些控件中必须有一个的id为当前Customs Control的id,否则,Page对象不会调用控件的LoadPostData方法!!!
如果不知道这点,调试程序时可能就会非常苦恼:明明已经实现了IPostBackDataHandler,为什么系统就不调用自己的LoadPostData()?不知道这是不是Page的一个bug!
  回复  引用  查看    

#6楼  2006-09-29 18:58 孤叶(学习.net框架)      

@匿名
原来10行代码完成的一件事,你可以用1行代码完成,你说性能会提高吗?
  回复  引用  查看    

#7楼  2007-06-28 11:16 DoNet鸟      

@dudu
将页面设置了 ASPCOMPAT="true" 后,再在这个页面中使用Session的话会出错,这个情况不知道你是否研究过,代码如下:
DataSet ds = (DataSet)Session["SupplierSaleStat"];
Session["SupplierSaleStat"]是在前一个页面设置的,在这个页面来访问,结果出现错误:"以 ASP 兼容模式调用 OnStartPage 时遇到错误。"
但代码在vs2003(.net 1.1)下是能执行成功的,转成vs2005(.net 2.0)就出错了
---------------------------------------------------------------
[HttpException (0x80004005): 以 ASP 兼容模式调用 OnStartPage 时遇到错误。]
System.Web.Util.AspCompatApplicationStep.OnPageStartSessionObjects() +175
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +102
----------------------------------------------------------------
  回复  引用  查看    

#8楼 [楼主] 2007-06-28 17:10 dudu      

@DoNet鸟
没有研究过。
  回复  引用  查看    


发表评论



姓名 [登录] [注册] 
主页
Email (仅博主可见) 
验证码 *  验证码看不清,换一张
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论   新用户注册   返回页首      

导航: 网站首页 社区 新闻 博问 闪存 网摘 招聘 .NET频道 知识库 找找看 Google站内搜索



China-pub 计算机图书网上专卖店!6.5万品种 2-8折!
China-Pub 计算机绝版图书按需印刷服务

相关文章:

相关链接:
 

导航

公告

人生的真正价值在于从何种程度与何种意义上摆脱自我!
明天继续更新评论功能
<2005年10月>
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345

统计

与我联系

搜索

 

常用链接

留言簿

随笔分类

随笔档案

新闻分类

相册

HJ

朋友的博客

网站收藏

小组

友情链接

最新随笔

最新评论

阅读排行榜

评论排行榜

60天内阅读排行