C# Tips

C# Tip Article

Bug Fix: Invalid viewstate / Unable to find assembly

Bug Fix: Invalid viewstate / Unable to find assembly

An exception was thrown from a ASP.NET WebForms page. It complained about something wrong in ViewState and unable to deserialize the view state information.

[SerializationException: Unable to find assembly 'App_Web_test.aspx.88cc05ae.oewwgbdr, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.]
   System.Runtime.Serialization.Formatters.Binary.BinaryAssemblyInfo.GetAssembly() +2513650
   System.Runtime.Serialization.Formatters.Binary.ObjectReader.GetType(BinaryAssemblyInfo assemblyInfo, String name) +133
   System.Runtime.Serialization.Formatters.Binary.BinaryConverter.TypeFromInfo(BinaryTypeEnum binaryTypeEnum, Object typeInformation, ObjectReader objectReader, BinaryAssemblyInfo assemblyInfo, InternalPrimitiveTypeE& primitiveTypeEnum, String& typeString, Type& type, Boolean& isVariant) +267
   System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadArray(BinaryHeaderEnum binaryHeaderEnum) +355
   System.Runtime.Serialization.Formatters.Binary.__BinaryParser.Run() +714
   System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage) +206
   System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage) +206
   System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream) +15
   System.Web.UI.ObjectStateFormatter.DeserializeValue(SerializerBinaryReader reader) +1507

[ArgumentException: The serialized data is invalid.]
   System.Web.UI.ObjectStateFormatter.Deserialize(Stream inputStream) +230

[ViewStateException: Invalid viewstate. 
	Referer: https://test.com/test.aspx
	ViewState: /wEPDwUKLTI5NDI1MjI3Mw8WAh4NRmVhdHVyZWRQYWdlczL3GQABAAAA/////wEAAAAAAAAAD....(elided)...]

[HttpException (0x80004005): The state information is invalid for this page and might be corrupted.]
   System.Web.UI.ViewStateException.ThrowError(Exception inner, String persistedState, String errorPageMessage, Boolean macValidationError) +169
   System.Web.UI.ViewStateException.ThrowViewStateError(Exception inner, String persistedState) +14
   System.Web.UI.HiddenFieldPageStatePersister.Load() +251
   System.Web.UI.Page.LoadPageStateFromPersistenceMedium() +219
   System.Web.UI.Page.LoadAllState() +43
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +8431
   System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +253
   System.Web.UI.Page.ProcessRequest() +78
   System.Web.UI.Page.ProcessRequestWithNoAssert(HttpContext context) +21
   System.Web.UI.Page.ProcessRequest(HttpContext context) +49

One interesting error message is "Unable to find assembly App_Web_test.aspx.88cc05ae.oewwgbdr". This assembly is a dynamically generated assembly and stays on the web server. That is, if there are multiple web servers using load balancer (Web Farm), round trip request can go to another web server. And that was the case. Most of time consecutive requests from the same client went to the same server where the dynamic assembly resided, but it was not always guaranteed. Hence the error.

So does this error alway occur whenever we use ViewState in Web Farm environment? No, it does not occur if we do not use custom user type. If there is custom user type and the user type object is saved to (or retrieved from) ViewState, ASP.NET generates dynamic assembly for it and performs serialization and deserialization.

Here is an example using ViewState for custom type.

[Serializable]
public class ImageData
{
    public int? Id { get; set; }
    public int? SortOrder { get; set; }
    public string ImagePath { get; set; }
}
//...
private void LoadData()
{        
	List<imagedata> imgData = GetDataFromDB();
	ViewState["ImgData"] = imgData; //problematic!
	//...
}

So how can we get around this? One way might be converting custom type object to JSON string before assigning it to ViewState. Here is a small class that does convert object to JSON and retrieve object from JSON.

using System.Web.Script.Serialization;

public class ViewStateConverter<T> where T : class
{
	public static string ToJson(T source)
	{
		var jss = new JavaScriptSerializer();
		return jss.Serialize(source);		
	}

	public static T FromJson(string jsonString)
	{
		var jss = new JavaScriptSerializer();
		return jss.Deserialize<T>(jsonString);		
	}
}

And with this ViewStateConverter class, the problematic code can be rewritten as follows.

private void LoadData()
{        
	List<ImageData> imgData = GetDataFromDB();
	ViewState["ImgData"] = ViewStateConverter<List<ImageData>>.ToJson(imgData)
	//...
}