玄铁剑

成功的途径:抄,创造,研究,发明...
posts - 128, comments - 42, trackbacks - 0, articles - 174

Failed to load viewstate

Posted on 2007-09-12 21:10 玄铁剑 阅读(2231) 评论(0)  编辑 收藏 引用 所属分类: asp.net

<Part one>
I need some help with this one. If I add this LinkButton Command event I get
an 'System.Web.HttpException: Failed to load viewstate' on the second
postback. It only occurs when FormView1.UpdateItem(true) is called. The page
posts back OK. When I do a second PostBack I get the error.

protected void LinkButtonPrevNext_Command(object sender,
CommandEventArgs e)
{
if (FormView1.CurrentMode == FormViewMode.Edit)
{
FormView1.UpdateItem(true);
}
//...other bits
}

As far as I know I am not loading any Controls dynamically. I'm finding it
almost impossible to debug and find out why the controls are different.

What's the best way to isolate the problem?
/////////////////////////////////////////////////////////////////////
From your description, you're getting "System.Web.HttpException: Failed to
load viewstate...." exception when manually use a button to trigger the
FormView's UpdateItem command, correct?

According to the code logic you mentioned, I have performed some simple
test on my local side and it seems a very simple and typical FormView
editing/updating webform can not quite repro the exact error behavior. My
test result is as below:

I use a FormView to edit and update a very simple table in SQL Server, the
autogenerated edit/update interface(inside formview works well).

And then I put a button on the webform(out side of the formview) and add
code to manually update the FormView item:

protected void btnOutUpdate_Click(object sender, EventArgs e)
{
if (FormView1.CurrentMode == FormViewMode.Edit)
{
FormView1.UpdateItem(true);
}
}

如果是有自己的加載控件,在進行類似操作時進行如下操作:
FormView1.DataBind()就不會出現這樣的錯誤。

it also works without any problem. Therefore, I think the problem is
likely specific to the data table or anything else on the webform. I
suggest you try simplify the page step by step to isolate the cause.

If you have any new finding or anything we can help, please feel free to
post here.
///////////////////////////////////////////////////////////////////////////
I solved the problem by adding FormView1.DataBind below the UpdateItem
method. My code creates and event when the UpdateItem occurs to tell the
Parent page to switch to another View in a MultiView control. If I don't
change the view then the error doesn't occur.

The MultiView control contains 4 views with their own user control. I'm
still not sure why DataBinding would solve the problem and why the error
occurs when the MultiView.ActiveViewIndex is changed.

<Part Two>
        View state is a feature in ASP.NET that allows pages to automatically preserve state without relying on server state (for example, session state). However, issues relating to view state can be difficult to debug. In most cases, when problems with view state occur, you receive the following error message in the Web browser, with little indication of what might be causing the issue:
"The viewstate is invalid for this page and might be corrupted"
This article describes some techniques that can be used for debugging and for resolving problems with view state.


MORE INFORMATION
Verify that you are not running into issues that have been fixed
A number of view state issues were fixed with ASP.NET 1.0 hotfixes and service packs, and those fixes are also part of ASP.NET 1.1. Make sure that you have applied the latest fixes before tracking issues that have already been resolved. You can obtain the latest Microsoft .NET Framework updates from the following Microsoft Developer Network (MSDN) Web site:
http://msdn2.microsoft.com/en-us/netframework/aa569276.aspx (http://msdn2.microsoft.com/en-us/netframework/aa569276.aspx)


Set the validationKey attribute if you are running in a Web farm
In a Web farm, each client request can go to a different machine on every postback. Because of this behavior, you cannot leave the validationKey attribute set to AutoGenerate in the Machine.config file. Instead, you must set the value of the validationKey attribute to a fixed string that is shared by all the machines on the Web farm.

For more information about this issue, click the following article number to view the article in the Microsoft Knowledge Base:
323744 (http://support.microsoft.com/kb/323744/) FIX: "The view state is invalid for this page and might be corrupted" error message in ASP.NET


Do not store dynamically generated types in view state in a Web farm
When ASP.NET compiles files dynamically, the files are built into assemblies with essentially random names (for example, a file name might be jp395dun.dll). If you are running a Web farm, the same files will be compiled into assemblies with different random names. Normally, this is not a problem because no one makes assumptions on those assembly names. But if you ever put a dynamically compiled type into view state by using binary serialization, the name of the assembly will be included as part of the view state data. When that view state is later sent to a different server in the Web farm, the view state cannot be deserialized because it uses different assembly names.

The best fix to this problem is to avoid using binary serialization. Binary serialization uses many resources even when you do not run into this problem. Instead, limit what you put in view state to a combination of Arrays, Pairs, Triplets, and simple types (for example, strings, int, and other types). System.Web.UI.Pair and System.Web.UI.Triplet are simple wrapper types that the view state engine can efficiently process.

An alternative fix to avoid this problem is to move the types that you are storing in view state into a precompiled assembly, either in your Bin folder or in the Global Assembly Cache. This fix does not address performance, but it guarantees that the assembly has the same name on all computers.

Note If you store complex data types in view state and experience this issue, the call stack information will contain stacks that are similar to the following:
[FileNotFoundException: Could not load file or assembly 'App_Web_fx--sar9, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.]
System.RuntimeTypeHandle._GetTypeByName(String name, Boolean throwOnError, Boolean ignoreCase, Boolean reflectionOnly, StackCrawlMark& stackMark, Boolean loadTypeFromPartialName) +0
System.RuntimeTypeHandle.GetTypeByName(String name, Boolean throwOnError, Boolean ignoreCase, Boolean reflectionOnly, StackCrawlMark& stackMark) +72
System.RuntimeType.PrivateGetType(String typeName, Boolean throwOnError, Boolean ignoreCase, Boolean reflectionOnly, StackCrawlMark& stackMark) +58
System.Type.GetType(String typeName, Boolean throwOnError) +57
System.Web.UI.ObjectStateFormatter.DeserializeType(SerializerBinaryReader reader) +192
System.Web.UI.ObjectStateFormatter.DeserializeValue(SerializerBinaryReader reader) +943
System.Web.UI.ObjectStateFormatter.DeserializeValue(SerializerBinaryReader reader) +384
System.Web.UI.ObjectStateFormatter.DeserializeValue(SerializerBinaryReader reader) +198
System.Web.UI.ObjectStateFormatter.DeserializeValue(SerializerBinaryReader reader) +210
System.Web.UI.ObjectStateFormatter.DeserializeValue(SerializerBinaryReader reader) +198
System.Web.UI.ObjectStateFormatter.Deserialize(Stream inputStream) +142


Determine whether the problem is related to the view state MAC feature
The purpose of the view state machine authentication code (MAC) feature is to make it impossible for clients to send a request that contains a malicious view state. By default, this feature is enabled in the following flag in the Machine.config file.
enableViewStateMac="true"
The simplest way to determine whether the issue you are dealing with is related to the MAC feature is to turn off the feature. To do this, change the flag in the Machine.config file to the following code.
enableViewStateMac="false"
If you no longer get view state errors, the problem is related to the MAC feature.

Important Only turn off the view state MAC feature to help diagnose the problem. You should not keep the view state MAC turned off to work around the issue. If so, you could introduce security holes. For more information, visit the following MSDN Web site:
http://msdn2.microsoft.com/en-us/library/aa302388.aspx (http://msdn2.microsoft.com/en-us/library/aa302388.aspx)
If you turn the view state MAC feature off, and then you use view state for controls that do not HTML encode (for example, a Label control), attackers can tamper with the view state data and can put arbitrary data in view state. This arbitrary data is decoded and then used by controls when they render the posted page. As a result, attackers can inject script into the application unless you work to prevent the attack. For example, an attacker could decode the data, inject script into the data where a Label control is, and then link to it from a Web site. Anyone who clicks on the link would be the victim of a script injection attack that could potentially steal their authentication cookies or session id. The script could also let an attacker alter state data for controls that use view state and application specific attacks could occur as a result.

In general, Microsoft recommends that you not turn off the view state MAC feature unless you are completely confident that you have either disabled view state for all controls that do not HTML encode their output (for example, DataGrid controls, DataList controls, Label controls, and other controls) or that you are always explicitly setting their values on each request to something known to be safe.

For more information, click the following article numbers to view the articles in the Microsoft Knowledge Base:
316920 (http://support.microsoft.com/kb/316920/) You receive a "View state is invalid" error message when you use the Server.Transfer method
324488 (http://support.microsoft.com/kb/324488/) Forms authentication and view state fail intermittently under heavy load


Determine exactly what exception occurs when you receive the error message
Unfortunately, the invalid view state error message that is mentioned in the "INTRODUCTION" section of this article is not very informative. The error message is generally caused by some exception being thrown when the view state is being processed. The problem is that the exception is being consumed, and its details are lost in the error message.

By using a debugger, you can determine the original exception. To do this, you must attach a debugger to the ASP.NET process (Aspnet_wp.exe or W3wp.exe), and then set it to catch all exceptions. The debugger will probably stop at a few exceptions that are not relevant, but eventually it will hit the view state exception and provide useful information for troubleshooting.

The following steps are an example that uses the Runtime Debugger (Cordbg.exe).
1. At a command prompt, run the iisreset command to make sure you are provided with an good starting point, and then browse to a page on your site.
2. At a command prompt, run cordbg.exe.
3. At the command prompt, type pro, and then press ENTER. A list of managed processes will appear. You should see either the Aspnet_wp.exe process or the W3wp.exe process. Note its PID.
4. At the prompt, type a PID to attach to the process.

Note Replace PID with the PID that was noted in step 3.
5. At the prompt, type ca e to tell Cordbg.exe to break on all exceptions, and then type g to let the process run.
6. Whenever you get an exception, type w to see the stack. If the stack is a view state exception (look for LoadPageStateFromPersistenceMedium on the stack), copy all the exception and the stack information from the command window, and then save the information. This information can help you understand the problem. If the exception is unrelated, type g.

For more information, click the following article number to view the article in the Microsoft Knowledge Base:
831150 (http://support.microsoft.com/kb/831150/) The "Viewstate is invalid for this page" error message does not provide sufficient information to troubleshoot the issue


Try storing the view state in the session
By default, the view state is round-tripped by means of an <input type=hidden> field that is sent to the browser. The browser then sends the field back to the server on the next request. In some cases, this view state can get quite large and be a potential source of problems. Some browsers cannot handle such a large hidden field (and the resulting large request), and the browsers may truncate the view state. Truncating the view state causes a "view state corrupted" error message. This behavior is most likely to occur in simpler browsers. For example, this behavior may occur in a browser on a PDA.

To determine whether you may be running into such an issue, try storing the view state in the session. The following example demonstrates how to do this.
<%@ language=c# debug=true %>
<script runat=server>
protected override object LoadPageStateFromPersistenceMedium()
{
      return Session["_ViewState"];
}
protected override void SavePageStateToPersistenceMedium(object viewState)
{
      Session["_ViewState"] = viewState;
}
void TextChanged(object o, EventArgs e)
{
      Response.Write("TextChanged");
}
</script>
<form runat=server>
<asp:button text=Test runat=server/>
<asp:textbox ontextchanged=TextChanged runat=server/>
<input type=hidden name=__VIEWSTATE>
</form>


The following line of code is only needed in ASP.NET 1.0, to work around a bug. In ASP.NET 1.1, it is not necessary.
<input type=hidden name=__VIEWSTATE>


Determine whether the problem is caused by worker process recycling
Consider the following scenario.
• You are running ASP.NET under Microsoft Internet Information Services (IIS) 6.0.
• The application pool is running under an identity other than the Local System account, the Network Service account, or an administrative-level account.
• The validationKey attribute of the <machineKey> element is set to AutoGenerate in the configuration file.

In this scenario, the following procedure will cause a view state error to occur:
1. A user browses a page.
2. The worker process that hosts the ASP.NET application recycles.
3. The user posts back the page.

 


<Part Three>

After implementing Page Controller pattern and after we dynamically loaded user controls in child page we got the above exception. The exception was raised when we clicked on IE back button and then presses submit on the content user control entry form. The content user control entry form was loaded dynamically to the content placeHolder in the child page. 

 

[For more details on this pattern check these links:

Page Controller

Implementing Page Controller in ASP.NET 

 

The cause

The exception occurs because the viewstate of the page must be restored to its previous state. This mean we need to re-load any control we loaded dynamically before the page load had being triggered.

 

Solution:

 

step1: In base page page_load event u set a session variable with the content user control name
Session[Consts.SESSION_MENU_CONTENT] = PageContent;

base page always occured before child page load

step2: In child page DoPageLoad (which being called from base) u do the following

if (!Page.IsPostBack &&
            Session[Consts.SESSION_MENU_CONTENT] != null &&
            Session[Consts.SESSION_MENU_CONTENT].ToString() != string.Empty)
         {
            if (LoadedControlName != Session[Consts.SESSION_MENU_CONTENT].ToString())
            {
               contentHolder.Visible = true;
               holderTitles.Visible = false;

               contentHolder.Visible = true;
               Session[Consts.SESSION_CURR_TAB_MENU_TITLE] = string.Empty;

               contentHolder.Controls.Clear(); 

               UserControl content = (UserControl)Page.LoadControl(Request.ApplicationPath + "/WebControls/" + Session[Consts.SESSION_MENU_CONTENT].ToString());
               content.ID = "ctrlContent1";

               contentHolder.Controls.Add(content);
            }
         }

step3: in the child page, OnInit event - before onload event on base page being called u should do the following

contentHolder.Visible=false;
         holderTitles.Visible=true;

         if (Page.IsPostBack)
         {
            if (Session["CurrentPageContent"] != null &&
               Session["CurrentPageContent"].ToString() != string.Empty)
            {
               contentHolder.Visible = true;
               holderTitles.Visible = false;

               LoadedControlName = Session[Consts.SESSION_MENU_CONTENT].ToString();

               contentHolder.Controls.Clear(); 

               UserControl content = (UserControl)Page.LoadControl(Request.ApplicationPath + "/WebControls/" + Session["CurrentPageContent"].ToString());
               content.ID = "ctrlContent1";

               contentHolder.Controls.Add(content);             
            }   
         }

**LoadedControlName is a local field member that being set in OnInit when postback occured and we r re-load the existing current content user control which we keep its name in the Session

////////////////////////////////////////////////////////////////////////////////
出現Failed to load viewstate的根本原因還是Postback之後出現部分控制加載順序跟先前不一致出現的錯誤,解決方式:
1.使用FormView時操作之後(update/insert等)進行databind
2.或者採用頁面的PreRender事件對相關控件進行databind
另外:Prerender/PreRenderComplete/PreLoad/PreInit等事件在開發中還是值得關注,會解決不少難題。

只有注册用户登录后才能发表评论。