Saturday 10 August 2013

why text-box will get updated data from view state and label control can't in asp.net

Introduction

The aim of this article is to clarify the question that many new Web developers might have about ViewState.

Why do some Web controls like Textbox retain values even after disabling the ViewState while others do not?

Background

Let’s build a simple Web application to examine how ViewState works.
Create a blank Web project and paste the code given below in the page:
<script runat="server">
    Protected Sub btnSubmit_Click(ByVal sender As Object, ByVal e As System.EventArgs) 
      Handles btnSubmit.Click
        lblMessage.Text = "Goodbye everyone"
        lblMessage1.Text = "Goodbye everyone"
        txtMessage.Text = "Goodbye everyone"
        txtMessage1.Text = "Goodbye everyone"
    End Sub
</script>
<form id="form1" runat="server">
  
   <asp:Label runat="server" ID="lblMessage" EnableViewState =true  
   Text="Hello World"></asp:Label>
   <asp:Label runat="server" ID="lblMessage1" EnableViewState =false  
   Text="Hello World"></asp:Label>
  <asp:Textbox runat="server" ID="txtMessage" EnableViewState =true  
   Text="Hello World"></asp:Textbox>
   <asp:Textbox runat="server" ID="txtMessage1" EnableViewState =false  
   Text="Hello World"></asp:Textbox>
<br />
<asp:Button runat="server" 
  Text="Change Message" ID="btnSubmit"></asp:Button>
<br />
<asp:Button ID="btnEmptyPostBack" runat="server" Text="Empty Postback"></asp:Button>
</form>
The page rendered will have four controls (two text boxes and two labels) initialized with Hello World and two buttons.
Click on the Change Message button, the value in controls will be changed to Goodbye Everyone.
Now click on the Empty Postback button.
The expected result is, after postback the Textbox (txtMessage) and label (lblMessage) with EnableViewState = false should not retain the value and hence the value should be Hello world, while the controls with ViewStateenabled (txtMessage1 and lblMessage1) should retain the value and hence value should be Goodbye world.
But this does not happen. Both the Textbox will maintain the value irrespective of whether ViewState is enabled or disabled, but in the case of label control if ViewState is disabled, the value we changed programmatically is not retained.
Let's examine why this happens?

Page LifeCycle and ViewState

In page life cycle, two events are associated with ViewState:
  • Load View State: This stage follows the initialization stage of page lifecycle. During this stage, ViewStateinformation saved in the previous postback is loaded into controls. As there is no need to check and load previous data, when the page is loaded for the first time this stage will not happen. On subsequent postback of the page as there may be previous data for the controls, the page will go through this stage.
  • Save View State: This stage precedes the render stage of the page. During this stage, current state (value) of controls is serialized into 64 bit encoded string and persisted in the hidden control (__ViewState) in the page.
  • Load Postback Data stage: Though this stage has nothing to do with ViewState, it causes most of the misconception among developers. This stage only happens when the page has been posted back. ASP.NET controls which implement IPostBackEventHandler will update its value (state) from the appropriate postback data. The important things to note about this stage are as follows:
  1. State (value) of controls are NOT retrieved from ViewState but from posted back form.
  2. Page class will hand over the posted back data to only those controls which implementIPostBackEventHandler.
  3. This stage follows the Load View State stage, in other words state of controls set during the Load View State stage will be overwritten in this stage.

Answers

Now with the above information, let us try to answer the question:
Why some controls retain values even after disabling the ViewState while others do not?
The answer is Controls which implements IPostBackEventHandler like TextboxCheckbox, etc. will retain the state even after disabling the viewstate. The reason is during the Load Postback Data stage, these controls will get state information from Posted back form.
But controls like label which do not implement IPostBackEventHandler will not get any state information from posted back data and hence depend entirely on viewstate to maintain the state.
Following are the changes in textbox during the page life cycle.
Textbox with ViewState Enabled
Page EventsPage is visited for first timeOn click of “Change Message” buttonOn click of “Empty Post back” button
InitTextbox is set value Hello WorldTextbox is set value Hello WorldTextbox is set value Hello World
Load View StateTextbox is set with value Good Bye Everyone from ViewState
Load Post back data stagePostback data is Hello World so Textbox is set with Hello WorldPostback data is Goodbye Everyone so Textbox is set withGoodbye Everyone
Controls Postback event (button click )Textbox is set with Goodbye everyone
Save view stateHello World is saved toViewStateGoodbye Everyone is saved to ViewStateGoodbye Everyone is saved toViewState
RenderTextbox is rendered with textHello worldTextbox is rendered with text Goodbye EveryoneTextbox is rendered with textGoodbye Everyone
Textbox with ViewState Disabled
Page EventsPage is visited for first timeOn click of “Change Message” buttonOn click of “Empty Post back” button
InitTextbox is set value Hello WorldTextbox is set value Hello WorldTextbox is set value Hello World
Load View StateTextbox is set with value Good Bye Everyone from ViewState
Load Post back data stagePostback data is Hello World so Textbox is set with Hello WorldPostback data is Goodbye Everyone so Textbox is set withGoodbye Everyone
Controls Postback event (button click )Textbox is set with Goodbye everyone
Save view state
RenderTextbox is rendered with textHello worldTextbox is rendered with text Goodbye EveryoneTextbox is rendered with textGoodbye Everyone
Label with ViewState Enabled
Page EventsPage is visited for first timeOn click of “Change Message” buttonOn click of “Empty Post back” button
InitLabel is set valueHello WorldLabel is set value Hello WorldLabel is set value Hello World
Load View StateLabel is set with value Good Bye Everyone from ViewState
Load Post back data stage
Controls Postback event (button click)Label is set withGoodbye everyone
Save view stateHello World is saved to labelGoodbye Everyone is saved to ViewStateGoodbye Everyone is saved toViewState
RenderLabel is rendered with text Hello worldLabel is rendered with text Goodbye EveryoneLabel is rendered with textGoodbye Everyone
Label with ViewState Disabled
Page EventsPage is visited for first timeOn click of “Change Message” buttonOn click of “Empty Post back” button
InitLabel is set valueHello WorldLabel is set value Hello WorldLabel is set valueHello World
Load View State
Load Post back data stage
Controls Postback event (button click )Label is set with Goodbye everyone
Save view state
RenderLabel is rendered with text Hello worldLabel is rendered with textGoodbye EveryoneLabel is rendered with text Hello World

Point of Interest

An interesting behavior is if we make a control which implements IPostBackEventHandler interface disabled then the ASP.NET will not process the control during postback. So in the above sample, if we make the Textbox (one withEnableViewState = false) disabled then it will not retain the changed value and behave like a label control.

Conclusion

In this article, we examined how the ViewState of control is persisted during the life cycle of page and why some controls maintain the state even if the ViewState is disabled. Hope the information provided here is useful.

ViewState: Various ways to reduce performance overhead

Introduction

In this article, I am going to explore the ViewState. ViewState is one thing that I always like to use. It makes life easier. As we all know, Viewstate is one way of maintaining the state in web applications.
As we know, a web page is created every time when a page is posted to the server. This means the values that the user entered in the webpage are going to vanish after the postback or submit. To get rid of this problem, the ASP.NET framework provides a way to maintain these values by virtue of ViewState. When we enable the viewstate of any control, the value is going to be maintained on every postback or server roundtrip.
But how are these values maintained? It doesn't come free. ViewState uses a hidden variable that resides on the page to store control values. This means that if a page has lots of controls with viewstate enabled, the page size would become heavy, in several kilobytes; i.e., it will take longer to download the page. And also, on every postback, all the data is posted to the server, increasing the network traffic as well.
In new era applications, we use lots of heavy controls like gridviews etc., on our pages which makes the page size exponentially heavy. It is always recommended to use View State judiciously, and some programmers try to avoid using it because of the performance overhead.
Here, I am going to discuss how we can reduce the performance overhead caused by View State.

What is ViewState

As we all know, the Web is a stateless medium; i.e., states are not maintained between requests by default. A web page is created every time a page is posted to the server. The ASP.NET framework provides us several ways to maintain the state. These are:
States.JPG
Various State Management technique in ASP.NET
Here, we going to discuss one of the client state management techniques: ViewState. We can persist data during postbacks with the help of ViewState. Why do we call ViewState as client-side? Because the data is stored on the page, and if we move from one page to another, the data would be lost.
So where does the ViewState gets stored? The ViewState is stored on the page in a hidden field named __viewstate.
I will not discuss the basics of ViewState in detail; for details, have a look at a very good article here on CodeProject:Beginner's Guide To View State [^].

Problems with ViewState

In new era applications, we generally have lots of rich and heavy controls on our page, and also provide lots of functionality on the page with the help of latest technologies like AJAX etc. To accomplish our tasks, we use ViewState a lot, but as we know, it doesn't come free - it has a performance overhead.
The ViewState is stored on the page in the form of a hidden variable. Its always advised to use the ViewState as little as possible. We also have other ways to reduce the performance overhead. Here, I am going to discuss the following ways:
  • By compressing/decompressing the ViewState
  • Store the ViewState in another server, say on a web server
We will see all these with an example.
First, let's see a normal scenario!!
Here, I have a webpage with a GridView which shows a lot of data (16 rows). I have enabled ViewState so that I don't need to repopulate the whole grid on every postback. But what am I paying for that? Let's see the example.
First, let's see the page size on postback with enableviewstate=true, which is true by default.
Page with View State enabled
Page with View State enabled
Here we can see the request size is 4.9 K. Also, we can see the ViewState of the page by viewing the View Source. Let's see it:
gridwithviewstatesrc.JPG
__Viewstate hidden form field with Viewstate enabled
You can see the __viewstate variable on the screen and the amount of data it has. If we have several controls like this, imagine your page size will be.
But the benefit is, we dont need to hit the database to fetch the data and bind it on every postback.
Now, let's see what happens when we disable the viewstate; i.e., set enableviewstate=false.
gridviewwithoutviewstate.JPG
"Page with Viewstate disabled
and there is no data on the page because I loaded the data for the first time on page load. On subsequent postbacks, I don't bind it. That's why when I click on PostBack, the data gets lost, but the page size is here is just 773 B. Also, let's examine the View Source of the page:
gridwithoutviewstatesrc.JPG
__Viewstate hidden form field with Viewstate disabled
We see the __viewstate variable, but with very less data. Although we have disabled viewstate, ASP.NET uses viewstate for some data to maintain the page state, but this is very little and not going to cause any performance overhead.
But what do we get from the viewstate? We don't need to repopulate the grid again. Just bind it on the first page-load, and on future postbacks, the data gets populated from the viewstate. Note: Here I am using Firebug for the analysis purpose.
Now we can imagine the overhead of viewstate. You can check your application (if you are using viewstate) and see the page size is increased by the viewstate. Now we will discuss one way to reduce the page size.

View State Compression/Decompression

We can compress the viewstate to reduce the page size. By compressing the viewstate, we can reduce the viewstate size by more than 30%. But here, the question arises, when do we compress and decompress the viewstate? For that, we have to dig into the page life cycle. As we know, viewstate is used by ASP.NET to populate controls. So we should compress the viewstate after all the changes are done in the viewstate, and save it after compression, and we have to decompress the viewstate just before it is used by ASP.NET to load all the controls from the viewstate. Let's jump to the page life cycle and see how we can fit our requirement.
pagelifecycle.JPG
ASP.NET: page life cycle
As we can see, there are two methods. SaveViewState is responsible for collecting the view state information for all of the controls in the control hierarchy and persist it in the __viewstate hidden form field. The view state is serialized to the hidden form field in the SavePageStateToPersistenceMedium() method during the save view state stage, and is deserialized by the Page class' LoadPageStateFromPersistenceMedium() method in the load view state stage. In these methods, we can compress and decompress the viewstate. Let's take a pictorial view.
viewstate.JPG
ASP.NET: How viewstate gets maintained
here, we need to override the methods SavePageStateToPersistenceMedium() for compressing the viewstate, and SavePageStateToPersistenceMedium() for decompressing the viewstate. I am going to use GZip for compression (provided by .NET). And this is available in the namespace System.IO.Compression. Let's jump to the code.
I have a class CompressViewState that is inherited from System.Web.UI.Page. I have overridden the above two methods. I also made two private methods: for compressing a byte stream, and for decompressing it. Let's have a look at the compressing one:
/// This Method takes the byte stream as parameter 
/// and return a compressed bytestream.
/// For compression it uses GZipStream
private byte[] Compress(byte[] b)
{
    MemoryStream ms = new MemoryStream();
    GZipStream zs = new GZipStream(ms, CompressionMode.Compress, true);
    zs.Write(b, 0, b.Length);
    zs.Close();
    return ms.ToArray();
}
As you can see, the Compress method takes a byte array as a parameter and returns compressed data in byte array form. In the method, I have used GZipStream for compressing the data. Now, let's look at the decompressing one:
/// This method takes the compressed byte stream as parameter
/// and return a decompressed bytestream.

private byte[] Decompress(byte[] b)
{
    MemoryStream ms = new MemoryStream();
    GZipStream zs = new GZipStream(new MemoryStream(b), 
                                   CompressionMode.Decompress, true);
    byte[] buffer = new byte[4096];
    int size;
    while (true)
    {
        size = zs.Read(buffer, 0, buffer.Length);
        if (size > 0) 
            ms.Write(buffer, 0, size);
        else break;
    }
    zs.Close();
    return ms.ToArray();
}
As you can see, this method takes compressed data as a parameter and returns decompressed data.
Let's now look at the overridden methods. First, LoadPageStateFromPersistenceMedium:
protected override object LoadPageStateFromPersistenceMedium()
{
    System.Web.UI.PageStatePersister pageStatePersister1 = this.PageStatePersister;
    pageStatePersister1.Load();
    String vState = pageStatePersister1.ViewState.ToString();
    byte[] pBytes = System.Convert.FromBase64String(vState);
    pBytes = Decompress(pBytes);
    LosFormatter mFormat = new LosFormatter();
    Object ViewState = mFormat.Deserialize(System.Convert.ToBase64String(pBytes));
    return new Pair(pageStatePersister1.ControlState, ViewState);
}
As you can see, in this method, we have taken the viewstate in a string variable from PageStatePersister, decompressed it (as it is compressed in the Save viewstate stage; we'll discuss after this), and deserialized it in anObject, and returned the new Pair.
Now moving to SavePageStateToPersistenceMedium.
protected override void SavePageStateToPersistenceMedium(Object pViewState)
{
    Pair pair1;
    System.Web.UI.PageStatePersister pageStatePersister1 = this.PageStatePersister;
    Object ViewState;
    if (pViewState is Pair)
    {
        pair1 = ((Pair)pViewState);
        pageStatePersister1.ControlState = pair1.First;
        ViewState = pair1.Second;
    }
    else
    {
        ViewState = pViewState;
    }
    LosFormatter mFormat = new LosFormatter();
    StringWriter mWriter = new StringWriter();
    mFormat.Serialize(mWriter, ViewState);
    String mViewStateStr = mWriter.ToString();
    byte[] pBytes = System.Convert.FromBase64String(mViewStateStr);
    pBytes = Compress(pBytes);
    String vStateStr = System.Convert.ToBase64String(pBytes);
    pageStatePersister1.ViewState = vStateStr;
    pageStatePersister1.Save();
}
Here, we read the viewstate information from pageStatePersister and then serialize it, and finally, after compressing it, save it in a page persister.
This is all about compressing and decompressing the viewstate. I have put the class in the App_Code folder so that it is available to the entire application. And wherever we need to use it, we should inherit it from CompressViewState. Thus, we can reduce the viewstate size significantly and make our applications perform much better :).
Now, let's move to another methodology: saving the viewstate into the file system.

Points to Remember

This methodology should be used only if you have enough data in the viewstate; else, you will need more time in compressing and decompressing the viewstate between requests without much gain.

Saving View State on the Server

I am going to explain two approaches here:
  • Using Session
  • Using a hidden field

Using Session

This way, we can reduce the viewstate size to zero. There is no performance overhead on a round trip to the server. Still, there is a bit of overhead in reading and saving the viewstate to the file system. We'll discuss this later.
Now in this methodology, we need to use two methods:
  • LoadPageStateFromPersistenceMedium
  • SavePageStateToPersistenceMedium
Now we will see how to save the viewstate to the file system. But how will we save the ViewState? What would be the name of the file? Does it have to be unique for every user? Do we need multiple files for the same user session.
There are a lot of questions. One answer, we don't need multiple files for the same user at the same time because ViewState is confined to every page, and when the user moves to another page, the previous page's ViewState gets ripped off. At a time, we just need to save the current page's ViewState.
As session ID is unique for every user, why should we not use the session ID in the file to make it unique, and whenever we'll need to get the file, we'll just pick it by using the session ID.
Let's jump to the code.
Here, I have a file name PersistViewStateToFileSystem that is inherited from System.web.UI.Page and resides in the App_code folder so that it is available through the whole application. Here, we have a property which returns the full path of the ViewState file. I used the session ID as a name of the file, and .vs as the extension. We also have a property that returns the path of the ViewState file:
public string ViewStateFilePath
{
    get
    {
        if (Session["viewstateFilPath"] == null)
        {
            string folderName = Path.Combine(Request.PhysicalApplicationPath, 
                                             "PersistedViewState");
            string fileName = Session.SessionID + "-" + 
                   Path.GetFileNameWithoutExtension(Request.Path).Replace("/", "-") + ".vs";
            string filepath = Path.Combine(folderName, fileName);
            Session["viewstateFilPath"] = filepath;
        }
        return Session["viewstateFilPath"].ToString();
    }
}
Again, here we have implemented both the functions.
LoadPageStateFromPersistenceMedium loads the file from the ViewState file, if it exists. Let's have a look at the code for LoadPageStateFromPersistenceMedium:
protected override object LoadPageStateFromPersistenceMedium()
{
    // determine the file to access
    if (!File.Exists(ViewStateFilePath))
        return null;
    else
    {
        // open the file
        StreamReader sr = File.OpenText(ViewStateFilePath);
        string viewStateString = sr.ReadToEnd();
        sr.Close();

        // deserialize the string
        LosFormatter los = new LosFormatter();
        return los.Deserialize(viewStateString);
    }
}
and SavePageStateToPersistenceMedium saves the file in the file system instead of pagepersister. Moving to the code for LoadPageStateFromPersistenceMedium:
protected override void SavePageStateToPersistenceMedium(object state)
{
    LosFormatter los = new LosFormatter();
    StringWriter sw = new StringWriter();
    los.Serialize(sw, state);

    StreamWriter w = File.CreateText(ViewStateFilePath);
    w.Write(sw.ToString());
    w.Close();
    sw.Close();
}
We need to inherit our page from the class PersistViewStateToFileSystem to use it.

Removing ViewState File from the Server

What is the best time to remove the ViewState file from the server? Our requirement is to remove the file at session end. So we can use the Application level Session_End (that is defined in the Global.asax file) to remove the file from the web server. I save the ViewState file path in the session, and access the file path from there. Let's see the code forSession_End.
void Session_End(object sender, EventArgs e) 
{
    //Deleting the viewstate file
    string filePath = Session["viewstateFilPath"] as string;
    if (!string.IsNullOrEmpty(filePath) && File.Exists(filePath))
    {
        File.Delete(filePath);
    }
}
You can get the full sample application in the attachment.
Now, let's move to our example. Let's see the request size.
gridonserver.JPG
Viewstate saved on server
It's just 2.5 K. We can see that our page size has reduced dramatically. Let's now see the View Source of the page:
viewstateserver.JPG
__Viewstate hidden form field with ViewState saved on the server
Cool! There is no data in the ViewState variable.

Points to Remember

  • This methodology only works if cookies are not disabled on client machines.
  • We need to remove the ViewState file after using it. Here, I have taken the approach of removing it atSession_End, which works only in InProc mode.
  • Only use if ViewState size is heavy, because it will take more to save the ViewState in the file system and on reading.
  • We can also compress the ViewState while saving the ViewState on the server.
  • This option might not work on a webfarms and webgarden scenario.

Using a Hidden Field

First, I would say thanks to all who shared their valuable feedback. I am adding a section because of them. As the session ID approach has many limitations as mentioned in the Points to Remember section, I have added the following new approach.

Using a hidden field

We can use a GUID instead of session ID to uniquely identify the ViewState file. And to get the GUID, we will use a hidden field to store the GUID so that we can get the file name as and when we require. So now here, we only need to change the View State file path property, and need to have a hidden field on every page. The hidden field on the page can be as:
<asp:hiddenfield runat="server" id="hfVSFileName">
So actually, in my demo application, I have added a key isSessionId in the app.config section of web.config; if it set true, then we are using the Session ID approach, else the hidden field approach. Let's look at the property code:
public string ViewStateFilePath
{
    get
    {
        bool isSessionId = Convert.ToBoolean(
             System.Configuration.ConfigurationManager.AppSettings["isSessionId"]);
        string folderName = Path.Combine(Request.PhysicalApplicationPath, 
                                         "PersistedViewState");
        string fileName = string.Empty;
        string filepath = string.Empty;
        if (!isSessionId)
        {
            HiddenField hfVSFileName = null;
            string VSFileName = "";

            // Get the HiddenField Key from the page
            hfVSFileName = FindControl(this, "hfVSFileName") as HiddenField;

            // Get the HiddenField value from the page
            string hfVSFileNameVal = GetValue(hfVSFileName.UniqueID.ToString());
            if (!string.IsNullOrEmpty(hfVSFileNameVal))
            {
                VSFileName = hfVSFileNameVal;
            }

            if (!Page.IsPostBack)
            {
                VSFileName = GenerateGUID();
                 hfVSFileName.Value = VSFileName;

                //Removing files from Server
                RemoveFilesfromServer();
            }

                fileName = VSFileName + "-" + 
                   Path.GetFileNameWithoutExtension(
                   Request.Path).Replace("/", "-") + ".vs";
                filepath = Path.Combine(folderName, fileName);

                return filepath;
            }
            else
            {
                if (Session["viewstateFilPath"] == null)
                {

                    fileName = Session.SessionID + "-" + 
                      Path.GetFileNameWithoutExtension(
                      Request.Path).Replace("/", "-") + ".vs";
                    filepath = Path.Combine(folderName, fileName);
                    Session["viewstateFilPath"] = filepath;
                }
                return Session["viewstateFilPath"].ToString();
            }
        }
    }
In the hidden field approach, I am creating a GUID for uniqueness of the filename, and also, it is created only once on page load.

Removing ViewState Files From the Server

I have made a change regarding removing files from the server. Here, I am removing files from the server which are older than 3 days:
private void RemoveFilesfromServer()
{
    try
    {
        string folderName = Path.Combine(Request.PhysicalApplicationPath, 
                                         "PersistedViewState");
        DirectoryInfo _Directory = new DirectoryInfo(folderName);
        FileInfo[] files = _Directory.GetFiles();
        DateTime threshold = DateTime.Now.AddDays(-3);
        foreach (FileInfo file in files)
        {
            if (file.CreationTime <= threshold)
                file.Delete();
        }
    }
    catch (Exception ex)
    {
        throw new ApplicationException("Removing Files from Server");
    }
}
I am calling this method once on every new page request. You can call this method based on your design. We can also make the number of days configurable.

Conclusion

At last, I just want to say, since we generally use lots of ViewState controls on our page, at least we should use one of the discussed approaches.