asp.netwebformsgarbage-collectionviewstate

Are objects in Web Forms persistent in memory?


I have lots of experience with C#, but I am fairly new to Web Forms.

I am working on a project where a lot of data is stored in the ViewState and I don't understand why.

My understanding is that the object representing my Page (System.Web.UI.Page) is persistent for the life of that Page. There may be some behind-the-scenes magic where an identifier is stored in ViewState, but when reacting to events on that page, can I not simply refer to "this" and its properties/methods?

When would I explicitly store data in the ViewState as opposed to simply using the properties of the current object (Page in this case)?


Solution

  • My understanding is that the object representing my Page (System.Web.UI.Page) is persistent for the life of that Page

    This is correct, however the lifetime of the page is only for a single request, after the HTML has been delivered to the client then the Page instance is destroyed.

    I recommend reading this article: https://msdn.microsoft.com/en-us/library/ms178472.aspx

    Note that WebForms' design: an abstraction of the stateless-web into a pseudo-stateful WinForms-like environment is largely considered a mistake and that's why ASP.NET MVC and ASP.NET Core has a radically different design (though Controller instances can be compared to Page instances, with similar lifecycle semantics but considerably less overhead).

    There may be some behind-the-scenes magic where an identifier is stored in ViewState, but when reacting to events on that page, can I not simply refer to "this" and its properties/methods?

    I'm having difficulty understanding this - but if you think that all instance members of a Page, including custom properties using only a backing field are automatically persisted between requests (including "postback" requests) then no, that is not true. There is no magic persistence for fields, you need to use the Page.ViewState property as a backing-store for those properties, and that data is only persisted between special "postback" POST requests.

    Let me try my own explanation:

    1. Browser makes request for GET /MyForm.aspx
    2. ASP.NET creates a new instance of MyForm : System.Web.UI.Page, creates all child-control instances declared in the .aspx file, and calls the Init and Load events on all the Controls, then Render to generate output HTML, then Unload. The MyForm instance for the request is then garbage-collected.

    If MyForm.aspx contains a <form runat="server"> and makes use of "postback", then when the user does something that triggers a postback then:

    1. Browser makes a request to POST /MyForm.aspx where the request body is the data from the <input > elements. Under the rules of HTML, only the contents of <input> (and <select>, <textarea>, etc) are submitted in the POST request and not the entire DOM - this is a problem for ASP.NET WebForms because all those Controls have many properties and settings (e.g. <asp:Label FontColor=""> which are not persisted through their own <input type="hidden" name="label123_FontColor">, besides, even if they were that's a lot of verbose data to send back to the server.

    So instead, ASP.NET instructs all the Control instances to serialize all their non-<input> data to the ViewState dictionary (which represents the state of the View (in MVP/MVC parlance, the .aspx/HTML is the View - hence persisting its state separate from the Request state which by-definition is short-lived).

    In a Control or Page subclass you need to use ViewState, not a backing field:

    public String SomeName {
        get { return this.ViewState["SomeName"] as String; }
        set { this.ViewState["SomeName"] = value; }
    }
    

    ...and then ViewState is serialized and compressed and signed and rendered to the page in <input name="__VIEWSTATE" type="hidden">).

    The reason state is carried in __VIEWSTATE instead of simply regenerating the entire page's data on every page request is because sometimes generating the page initially can involve heavy lifting (e.g. a DB query). The argument is that if you have a page with a list of data and the user is simply manipulating that data before saving it back to the DB then you shouldn't need to reload from the DB and instead store that view-level data in the page's __VIEWSTATE as though it's some kind of super-cookie or something.