【问题标题】:Persist ViewState on CallBack request在回调请求上保持 ViewState
【发布时间】:2012-10-24 11:24:15
【问题描述】:

问题是,我需要在 CallBack 请求期间坚持 ViewState

我知道当请求是CallBack 时,默认情况下 ASP.NET 不会保存页面状态。 所以,我需要一个替代方案来使它工作。

ASP.NET 应用程序有一个自定义PageStatePersister,它将所有页面状态(ViewState)存储到Session。这样ViewState 就不会在任何时候发送/检索到客户端(尽管隐藏字段仍然存在于页面上,但没有价值)。 ViewState 不必在 CallBack 响应中返回,我只需要以某种方式调用在正常的 PostBack 请求中保存 ViewState 的机制。

应该是这样的:

protected void Page_LoadComplete(object sender, EventArgs e)
{
    //Set anything to the viewstate for accessing on a later postback
    ViewState["Anything"] = 1;

    if (Page.IsCallback)
    {
        //Persist the page state manualy (not needed on normal postback)
        InvokeAspNetStateSaver();
    }
    ...
}

感谢您的帮助。

【问题讨论】:

    标签: c# asp.net session callback viewstate


    【解决方案1】:

    终于明白了。

    事情是这样的。

    在对Page 类(System.Web.UI)进行了一些反编译之后,我发现 ASP.NET 在包括状态加载和保存在内的请求处理中基本上做了什么。以下代码是 ASP.NET 对方法 ProcessRequestMain 所做的:

    this.Trace.Write("aspx.page", "Begin PreInit");
    this.PerformPreInit();
    this.Trace.Write("aspx.page", "End PreInit");
    this.Trace.Write("aspx.page", "Begin Init");
    this.InitRecursive((Control) null);
    this.Trace.Write("aspx.page", "End Init");
    this.Trace.Write("aspx.page", "Begin InitComplete");
    this.OnInitComplete(EventArgs.Empty);
    this.Trace.Write("aspx.page", "End InitComplete");
    
    if (this.IsPostBack)
    {
        this.Trace.Write("aspx.page", "Begin LoadState");
        this.LoadAllState();
        this.Trace.Write("aspx.page", "End LoadState");
        this.Trace.Write("aspx.page", "Begin ProcessPostData");
        this.ProcessPostData(this._requestValueCollection, true);
        this.Trace.Write("aspx.page", "End ProcessPostData");  
    }
    
    this.Trace.Write("aspx.page", "Begin PreLoad");
    this.OnPreLoad(EventArgs.Empty);
    this.Trace.Write("aspx.page", "End PreLoad");
    this.Trace.Write("aspx.page", "Begin Load");
    this.LoadRecursive();
    this.Trace.Write("aspx.page", "End Load");
    
    if (this.IsPostBack)
    {
        this.Trace.Write("aspx.page", "Begin ProcessPostData Second Try");
        this.ProcessPostData(this._leftoverPostData, false);
        this.Trace.Write("aspx.page", "End ProcessPostData Second Try");
        this.Trace.Write("aspx.page", "Begin Raise ChangedEvents");
        this.RaiseChangedEvents();
        this.Trace.Write("aspx.page", "End Raise ChangedEvents");
        this.Trace.Write("aspx.page", "Begin Raise PostBackEvent");
        this.RaisePostBackEvent(this._requestValueCollection);
        this.Trace.Write("aspx.page", "End Raise PostBackEvent");
    }
    
    this.Trace.Write("aspx.page", "Begin LoadComplete");
    this.OnLoadComplete(EventArgs.Empty);
    this.Trace.Write("aspx.page", "End LoadComplete");
    
    if (this.IsPostBack && this.IsCallback)
        this.PrepareCallback(callbackControlID);
    else if (!this.IsCrossPagePostBack)
    {
        this.Trace.Write("aspx.page", "Begin PreRender");
        this.PreRenderRecursiveInternal();
        this.Trace.Write("aspx.page", "End PreRender");
    }
    
    ...
    
    if (this.IsCallback)
    {
        this.RenderCallback();
    }
    else
    {
        if (this.IsCrossPagePostBack)
            return;
    
        this.Trace.Write("aspx.page", "Begin PreRenderComplete");
        this.PerformPreRenderComplete();
        this.Trace.Write("aspx.page", "End PreRenderComplete");
        this.BuildPageProfileTree(this.EnableViewState);
        this.Trace.Write("aspx.page", "Begin SaveState");
        this.SaveAllState();
        this.Trace.Write("aspx.page", "End SaveState");
        this.Trace.Write("aspx.page", "Begin SaveStateComplete");
        this.OnSaveStateComplete(EventArgs.Empty);
        this.Trace.Write("aspx.page", "End SaveStateComplete");
        this.Trace.Write("aspx.page", "Begin Render");
    
        if (exportedWebPartID != null)
            this.ExportWebPart(exportedWebPartID);
        else
            this.RenderControl(this.CreateHtmlTextWriter(this.Response.Output));
    
        this.Trace.Write("aspx.page", "End Render");
        this.CheckRemainingAsyncTasks(false);
    }
    

    方法LoadAllState()负责加载页面状态,它执行每个PostBack请求。

    方法SaveAllState()负责保存页面状态。 ASP.NET 执行它的代码如下:

    if (this.IsCallback)
    {
        this.RenderCallback();
    }
    else
    {
        ...
        this.SaveAllState();
        ...
    }
    

    这就是我的ViewState 没有在CallBack 请求中保存的原因!

    所以,最后,解决方案!使用反射手动调用SaveAllState()方法!

    if (Page.IsCallback)
    {
        System.Reflection.MethodInfo saveAllStateMethod = typeof(System.Web.UI.Page).GetMethod("SaveAllState", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
        saveAllStateMethod.Invoke(Page, null);
    }
    

    【讨论】:

    • 这有点骇人听闻。最好使用 Session 或完整的 PostBack。以后需要更改代码时,此类黑客行为可能代价高昂。
    【解决方案2】:

    可以在页面上的 SaveState 和 LoadState 事件中保存和加载视图状态。由于您有页面的通用基类,因此您希望在其中保存状态,您可以执行以下操作:

        protected override object SaveViewState()
        {
            object state = base.SaveViewState();
    
            // your custom save logic
        }
    
        protected override void LoadViewState(object savedState)
        {
            object state = // load your view state here
            base.LoadViewState(savedState);
        }
    

    不同的问题是,将状态存储在某处是否是好的设计。

    【讨论】:

    • 感谢您的回答。当它是回调请求时,.NET Framework 不会保存状态。因此,不会调用 SaveViewState 方法(仅在正常回发时)。它应该是调用保存它的 .NET Framework 方法的某种方式。我只是不知道它到底在哪里。
    • 也许您可以手动调用 SaveViewState 方法,在您的回调请求的页面生命周期的某个时刻。
    猜你喜欢
    • 1970-01-01
    • 2017-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多