Pages

Tuesday, December 1, 2009

ViewState and Dynamic Control

ViewState and Dynamic Control



I thought I understand ViewState, until I came cross this exception:

Failed to load viewstate. The control tree into which viewstate is being loaded must match the control tree that was used to save viewstate during the previous request. For example, when adding controls dynamically, the controls added during a post-back must match the type and position of the controls added during the initial request.



This is a question asked by someone on a .NET mailing list. My first guess of what causing the problem is that on a page postback, when LoadViewState() is invoked to restore the saved ViewState values to the page and its controls (both Control tree and ViewState tree have been created at this stage), somehow, the ViewState tree doesn't match the control tree. So when ASP.NET tries to restore a ViewState value to a control, no control or a wrong control is found and then the exception occurs.

Note: the ViewState tree (type of Triplet or Pair) is NOT the ViewState property (type of StateBag) of the page or any of its controls. You can think it as an object representation of the ViewState value on the html page (the __VIEWSTATE hidden field), which contains all the values need to be written back to the controls during a page postback. If you don't change the default behavior, during the page initialize/load phrase, the ViewState tree will be created by de-serializing the value __VIEWSTATE field by LoadPageStateFromPersistenceMedium(), and the values on the ViewState tree will be put into the controls ViewState bag in LoadViewState() . During the page save/render phrase, the ViewState tree will be created again by SaveViewState (), then serialized and written onto html page by SavePageStateToPersistenceMedium ()

So, I thought I could reproduce same exception with something simple like this:

Defualt.aspx







Default.aspx.cs

public partial class _Default : Page

{

protected override void OnInit(EventArgs e)

{

base.OnInit(e);

if (!IsPostBack)

{

Button btnClickMe = new Button();

form1.Controls.Add(btnClickMe);

}

}

}

It is indeed a very simple page with a button named btnPostback created statically on .aspx file, and another button named btnClickMe created dynamically in Page.OnInit(), and I will not recreate the btnClickMe for postbacks. So on a page postback, by the time OnInit() and LoadPageStateFromPersistenceMedium() is executed, the control tree and ViewState tree would have different structure, the ViewState tree will have value for btnClickMe, but the control tree will not have the control btnClickMe. I thought it would be good enough to cause the exception, but soon I was proved wrong, there was no exception thrown.

To find out why, let's have a look of the actual ViewState value generated on the html page

“/wEPDwUKMTQ2OTkzNDMyMWRkOWxNFeQcY9jzeKVCluHBdzA6WBo=”

With a little help from ViewState Decoder I got this:







1469934321







There is no view state data for the neither of the buttons! I did expect something like for a control has empty state though.

So, I think here is the first thing I learned:

For a control on the Control tree, there may not be a corresponding item on the ViewState tree (if there is no state for this control need to be saved). If there is nothing found on ViewState tree for a control, the control’s LoadViewState() will not be invoked.

So, let's do something to make the button "dirty" and its ViewState saved.

public partial class _Default : Page

{

protected override void OnInit(EventArgs e)

{

base.OnInit(e);

if (!IsPostBack)

{

Button btnClickMe = new Button();

form1.Controls.Add(btnClickMe);

btnClickMe.Text = "Click me";

}

}

}



The ViewState now became:



/wEPDwUKMTQ2OT

in reference to:

"ViewState and Dynamic Control"
- ViewState and Dynamic Control (view on Google Sidewiki)

Monday, November 16, 2009

compress your http response

 add this code on global.ascx PreRequestHandlerExecute events


<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.IO.Compression" %>

Friday, November 6, 2009

useing web service without webrefrence

Imports System.CodeDom


Imports System.CodeDom.Compiler

Imports System.Security.Permissions

Imports System.Web.Services.Description

Imports System.Reflection





Public Function CallWebService(ByVal webServiceAsmxUrl As String, _

ByVal serviceName As String, ByVal methodName As String, _

ByVal args() As Object) As Object



Try

Dim client As System.Net.WebClient = New System.Net.WebClient()



'-Connect To the web service

Dim stream As System.IO.Stream = _

client.OpenRead(webServiceAsmxUrl + "?wsdl")



'Read the WSDL file describing a service.

Dim description As ServiceDescription = ServiceDescription.Read(stream)



'LOAD THE DOM'''''''''''''''''''''''''''



'--Initialize a service description importer.

Dim importer As ServiceDescriptionImporter = New ServiceDescriptionImporter()

importer.ProtocolName = "Soap12" ' Use SOAP 1.2.

importer.AddServiceDescription(description, Nothing, Nothing)



'--Generate a proxy client.



importer.Style = ServiceDescriptionImportStyle.Client

'--Generate properties to represent primitive values.

importer.CodeGenerationOptions = _

System.Xml.Serialization.CodeGenerationOptions.GenerateProperties



'Initialize a Code-DOM tree into which we will import the service.

Dim nmspace As CodeNamespace = New CodeNamespace()

Dim unit1 As CodeCompileUnit = New CodeCompileUnit()

unit1.Namespaces.Add(nmspace)



'Import the service into the Code-DOM tree.

'This creates proxy code that uses the service.



Dim warning As ServiceDescriptionImportWarnings = _

importer.Import(nmspace, unit1)



If warning = 0 Then



'--Generate the proxy code

Dim provider1 As CodeDomProvider = _

CodeDomProvider.CreateProvider("VB")

'--Compile the assembly proxy with the // appropriate references

Dim assemblyReferences() As String

assemblyReferences = New String() {"System.dll", _

"System.Web.Services.dll", "System.Web.dll", _

"System.Xml.dll", "System.Data.dll"}

Dim parms As CompilerParameters = New CompilerParameters(assemblyReferences)

parms.GenerateInMemory = True '(Thanks for this line nikolas)

Dim results As CompilerResults = provider1.CompileAssemblyFromDom(parms, unit1)



'-Check For Errors

If results.Errors.Count > 0 Then



Dim oops As CompilerError

For Each oops In results.Errors

System.Diagnostics.Debug.WriteLine("========Compiler error============")

System.Diagnostics.Debug.WriteLine(oops.ErrorText)

Next

Throw New System.Exception("Compile Error Occured calling webservice.")

End If



'--Finally, Invoke the web service method

Dim wsvcClass As Object = results.CompiledAssembly.CreateInstance(serviceName)

Dim mi As MethodInfo = wsvcClass.GetType().GetMethod(methodName)

Return mi.Invoke(wsvcClass, args)



Else

Return Nothing

End If



Catch ex As Exception

Throw ex

End Try

End Function

Calling

Call this web service in a Webform or wndows form or method etc... as below:

Collapse

Dim WebserviceUrl As String = "http://www.abc.com/lgl/test/webservice/v1_00/security.asmx"



'specify service name

Dim serviceName As String = "SecurityAndSessionManagement"



'specify method name to be called

Dim methodName As String = "Session_Start"



'Paraments passed to the method

Dim arArguments(1) As String

arArguments(0) = "abc"

arArguments(1) = "xxxx"



Dim objCallWS As New DynamicWebService

sSessionID = objCallWS.CallWebService(WebserviceUrl, serviceName, _

methodName, arArguments)

MsgBox("new SessionID: " & sSessionID)