Programming Microsoft ASP.NET 2.0 Core Reference
The View State of a Page
ASP.NET pages supply the ViewState property to let applications build a call context and retain values across two successive requests for the same page. The view state represents the state of the page when it was last processed on the server. The state is persisted usually, but not necessarily, on the client side and is restored before the page request is processed.
By default, the view state is maintained as a hidden field added to the page. As such, it travels back and forth with the page itself. Although sent to the client, the view state does not represent, nor does it contain, any information specifically aimed at the client. The information stored in view state is pertinent only to the page and some of its child controls and is not consumed in any way by the browser.
Using the view state has advantages and disadvantages that you might want to carefully balance prior to making your state management decision. First, the view state does not require any server resources and is simple to implement and use. Because it's a physical part of the page, it's fast to retrieve and use. This last point, while in some respects a strong one, turns into a considerable weakness as soon as you consider the page performance from a wider perspective.
Because the view state is packed with the page, it inevitably charges the HTML code transferred over HTTP with a few extra kilobytes of data useless data, moreover, from the browser's perspective. A complex real-world page, especially if it does not even attempt to optimize and restrict the use of the view state, can easily find 20 KB of extra stuff packed in the HTML code sent out to the browser.
In summary, the view state is one of the most important features of ASP.NET, not so much because of its technical relevance but because it allows most of the magic of the Web Forms model. Used without strict criteria, though, the view state can easily become a burden for pages.
The StateBag Class
The StateBag class is the class behind the view state that manages the information that ASP.NET pages and controls want to persist across successive posts of the same page instance. The class works like a dictionary and, in addition, implements the IStateManager interface. The Page and Control base classes expose the view state through the ViewState property. So you can add or remove items from the StateBag class as you would with any dictionary object, as the following code demonstrates:
ViewState["FontSize"] = value;
You should start writing to the view state only after the Init event fires for the page request. You can read from the view state during any stage of the page life cycle, but not after the page enters rendering mode that is, after the PreRender event fires.
View State Properties
Table 13-14 lists all the properties defined in the StateBag class.
Property | Description |
---|---|
Count | Gets the number of elements stored in the object. |
Item | Indexer property. It gets or sets the value of an item stored in the class. |
Keys | Gets a collection object containing the keys defined in the object. |
Values | Gets a collection object containing all the values stored in object. |
Each item in the StateBag class is represented by a StateItem object. An instance of the StateItem object is implicitly created when you set the Item indexer property with a value or when you call the Add method. Items added to the StateBag object are tracked until the view state is serialized prior to the page rendering. Items serialized are those with the IsDirty property set to true.
View State Methods
Table 13-15 lists all the methods you can call in the StateBag class.
Method | Description |
---|---|
Add | Adds a new StateItem object to the collection. If the item already exists, it gets updated. |
Clear | Removes all items from the current view state. |
GetEnumerator | Returns an object that scrolls over all the elements in the StateBag. |
IsItemDirty | Indicates whether the element with the specified key has been modified during the request processing. |
Remove | Removes the specified object from the StateBag object. |
The IsItemDirty method represents an indirect way to call into the IsDirty property of the specified StateItem object.
Note | The view state for the page is a cumulative property that results from the contents of the ViewState property of the page plus the view state of all the controls hosted in the page. |
Common Issues with View State
Architecturally speaking, the importance of the view state cannot be denied, as it is key to setting up the automatic state management feature of ASP.NET. A couple of hot issues are connected to the usage of the view state, however. Most frequently asked questions about the view state are related to security and performance. Can we say that the view state is inherently secure and cannot be tampered with? How will the extra information contained in the view state affect the download time of the page? Let's find out.
Encrypting and Securing
Many developers are doubtful about using view state just because it is stored in a hidden field and left on the client at the mercy of potential intruders. Although the data is stored in a hashed format, there's no absolute guarantee that it cannot be tampered with. The first remark is that the view state as implemented in ASP.NET is inherently more secure than any other hidden fields you might use (and that you were likely using, say, in old classic ASP applications). The second remark is that only data confidentiality is at risk of being disclosed. While this is a problem, it is minor compared to code injection.
Freely accessible in a hidden field named __VIEWSTATE, the view state information is, by default, hashed and Base64 encoded. To decode it on the client, a potential attacker must accomplish a number of steps, but the action is definitely possible. Once decoded, though, the view state only reveals its contents that is, confidentiality is at risk. However, there's no way an attacker can modify the view state to post malicious data. A tampered view state, in fact, is normally detected on the server and an exception is thrown.
For performance reasons, the view state is not encrypted. If needed, though, you can turn the option on by acting on the web.config file, as follows:
<machineKey validation="3DES" />
When the validation attribute is set to 3DES, the view-state validation technique uses 3DES encryption and doesn't hash the contents.
Machine Authentication Check
As mentioned in Chapter 3, the @Page directive contains an attribute named EnableViewStateMac, whose only purpose is making the view state a bit more secure by detecting any possible attempt at corrupting the original data. When serialized, and if EnableViewStateMac is set to true, the view state is appended with a validator hash string based on the algorithm and the key defined in the <machineKey> section of the configuration file. The resulting array of bytes the output of the StateBag's binary serialization plus the hash value is Base64 encoded. By default, the encryption algorithm to calculate the hash is SHA1, and the encryption and decryption keys are auto-generated and stored in the Web server machine's Local Security Authority (LSA) subsystem. The LSA is a protected component of Windows NT, Windows 2000, Windows Server 2003, and Windows XP. It provides security services and maintains information about all aspects of local security on a system.
If EnableViewStateMac is true, when the page posts back, the hash value is extracted and used to verify that the returned view state has not been tampered with on the client. If it has been, an exception is thrown. The net effect is that you might be able to read the contents of the view state, but to replace it you need the encryption key, which is in the Web server's LSA. The MAC in the name of the EnableViewStateMac property stands for Machine Authentication Check, which is enabled by default. If you disable the attribute, an attacker could alter the view-state information on the client and send a modified version to the server and have ASP.NET blissfully use that tampered-with information.
To reinforce the security of the view state, in ASP.NET 1.1 the ViewStateUserKey property has been added to the Page class. The property evaluates to a user-specific string (typically, the session ID) that is known on the server and hard to guess on the client. ASP.NET uses the content of the property as an input argument to the hash algorithm that generates the MAC code.
Size Thresholds and Page Throughput
My personal opinion is that you should be concerned about the view state, but not for the potential security holes it might open in your code it can only let hackers exploit existing holes. You should be more concerned about the overall performance and responsiveness of the page. Especially for feature-rich pages that use plenty of controls, the view state can reach a considerable size, measured in KB of data. Such an extra burden taxes all requests, in downloads and uploads, and ends up creating serious overhead for the application as a whole.
What would be a reasonable size for an ASP.NET page? And for the view state of a page? Let's take a sample page that contains a grid control bound to about 100 records (the Customers table in the Northwind database of SQL Server).
<html> <head runat="server"> <title>Measure Up Your ViewState (2.0)</title> </head> <script language="javascript"> function ShowViewStateSize() { var buf = document.forms[0]["__VIEWSTATE"].value; alert("View state is " + buf.length + " bytes"); } </script> <body> <form runat="server"> <input type="button" value="Show View State Size" onclick="ShowViewStateSize()"> <asp:SqlDataSource runat="server" SelectCommand="SELECT companyname, contactname, contacttitle FROM customers" ConnectionString="<%$ ConnectionStrings:LocalNWind %>" <asp:DataGrid runat="server" DataSource /> </form> </body> </html>
In ASP.NET 2.0, the total size of the page is about 20 KB. The view state alone, though, takes up about 11 KB. If you port the same page back to ASP.NET 1.x, results are even worse. The whole page amounts to 28 KB, while the view state alone amounts to a burdensome 19 KB. Two conclusions can be drawn from these numbers:
-
In ASP.NET 2.0, the view-state field appears to be more compact. (I'll discuss this in more detail later in the chapter.)
-
The view state takes up a large share of the downloaded bytes for the page. You won't be too far from the truth if you estimate the view-state size to be about 60 percent of the entire page size.
What can you do about this? First, let's play with some numbers to determine a reasonable goal for view-state size in our applications. You should endeavor to keep a page size around 30 KB, to the extent that this is possible, of course. For example, the Google home page is less than 4 KB. The home page of http://www.asp.net amounts to about 50 KB. The Google site is not written with ASP.NET, so nothing can be said about the view state. But what about the view-state size of the home of http://www.asp.net? Interestingly enough, that page has only 1 KB of view state. On the other hand, the page http://www.asp.net/ControlGallery/default.aspx?Category=7&tabindex=0 is larger than 500 KB, of which 120 KB is view state.
The ideal size for a view state is around 7 KB; it is optimal if you can keep it down to 3 KB or so. In any case, the view state, regardless of its absolute size, should never exceed 30 percent of the page size.
Note | Where do these numbers come from? "From my personal experience," would perhaps be a valid answer, but not necessarily a good and exhaustive one. Let's put it this way: the smallest you can keep a page is the best size. To me, 30 KB looks like a reasonable compromise, because most things can be stuffed into that size. Clearly, if you have 250 items to display, your page size can grow up to 1 MB or so. In the end, having a smaller or larger view state is a design choice and is mostly application-specific. Within these boundaries, though, a few guidelines can be stated. The most important guideline is not so much that view state should be limited to a few KB, but that it should take a minimal percentage of the overall page size. Which percentage? Being the view-state helper, I'd say no more than 25 percent or 30 percent at the most. But here I'm just throwing out numbers using a bit of common sense. If you can disable the view state altogether, do it. At the very least, you should avoid storing the avoidable items, such as a long list of countries, there. |
Programming Web Forms without View State
By default, the view state is enabled for all server controls; however, this doesn't mean that you strictly need it all the time and for all controls. The use of the view-state feature should be carefully monitored because it can hinder your code. View state saves you from a lot of coding and, more important, makes coding simpler and smarter. However, if you find you're paying too much for this feature, drop view state altogether and reinitialize the state of the size-critical controls at every postback. In this case, disabling view state saves processing time and speeds up the download process.
Disabling View State
You can disable the view state for an entire page by using the EnableViewState attribute of the @Page directive. While this is not generally a recommended option, you should definitely consider it for read-only pages that either don't post back or don't need state to be maintained:
<% @Page EnableViewState="false" %>
A better approach entails disabling the view state only for some of the controls hosted in the page. To disable it on a per-control basis, set the EnableViewState property of the control to false, as shown here:
<asp:datagrid runat="server" EnableViewState="false"> ... </asp:datagrid>
While developing the page, you can keep the size of the view state under control by enabling tracing on the page. The tracer doesn't show the total amount of the view state for the page, but it lets you form a precise idea of what each control does. In Figure 13-8, the page contains a relatively simple DataGrid control. As you can see, the cells of the grid take up a large part of the view state. The TableCell control, in particular, strips the view state of all its user interfaces, including text, column, and row-span style attributes.
Note | Like all other data-bound controls, the DataGrid doesn't store its data source in the view state. However, the data source bound to the control has some impact on the view-state size. Each constituent control in the overall data-bound user interface takes up some view state for its settings. The number of child controls (for example, rows and cells in a DataGrid) obviously depend on the data source. |
Determining When to Disable View State
Let's briefly recap what view state is all about and what you might lose if you ban it from your pages. View state represents the current state of the page and its controls just before the page is rendered to HTML. When the page posts back, the view state a sort of call context for the page request is recovered from the hidden field, deserialized, and used to initialize the server controls in the page and the page itself. However, as pointed out in Chapter 3, this is only the first half of the story.
After loading the view state, the page reads client-side posted information and uses those values to override most of the settings for the server controls. Applying posted values overrides some of the settings read from the view state. You understand that in this case, and only for the properties modified by posted values, the view state represents an extra burden.
Let's examine a typical case. Suppose you have a page with a text box server control. What you expect is that when the page posts back, the text box server control is automatically assigned the value set on the client. Well, to meet this rather common requirement, you don't need view state. Consider the following page:
<% @Page language="c#" %> <form runat="server"> <asp:textbox runat="server" enableviewstate="false" readonly="false" text="Type here" /> <asp:checkbox runat="server" enableviewstate="false" text="Check me" /> <asp:button runat="server" text="Click" onclick="OnPost" /> </form>
Apparently, the behavior of the page is stateful even if view state is disabled for a couple of controls. The reason lies in the fact that you are using two server controls TextBox and CheckBox whose key properties Text and Checked are updated according to the values set by the user. For these properties, posted values will override any setting that view state might have set. As a result, as long as you're simply interested in persisting these properties you don't need view state at all.
Likewise, you don't need view state for all control properties that are set at design time in the .aspx file and are not expected to change during the session. The following code illustrates this point:
<asp:textbox runat="server" Text="Some text" MaxLength="20" ReadOnly="true" />
You don't need view state to keep the Text property of a TextBox up to date; you do need view state to keep up to date, say, ReadOnly or MaxLength, as long as these properties have their values changed during the page lifetime. If the two properties are constant during the page lifetime, you don't need view state for them, either.
So when is view state really necessary?
View state is necessary whenever your page requires that accessory control properties (other than those subject to posted values) are updated during the page lifetime. In this context, "updated" means that their original value changes either the default value or the value you assign to the property at design time. Consider the following form:
<script runat="server"> void Page_Load(object sender, EventArgs e) { if (!IsPostBack) theInput.ReadOnly = true; } </script> <form runat="server"> <asp:textbox runat="server" text="Am I read-only?" /> <asp:button runat="server" text="Click" onclick="OnPost" /> </form>
When the page is first loaded, the text box becomes read-only. Next, you click the button to post back. If view state is enabled, the page works as expected and the text box remains readonly. If view state is disabled for the text box, the original setting for the ReadOnly property is restored in this case, false.
In general, you can do without view state whenever the state can be deduced either from the client or from the run-time environment. In contrast, doing without view state is hard whenever state information can't be dynamically inferred and you can't ensure that all properties are correctly restored when the page posts back. This is exactly what view state guarantees at the cost of extra bytes when downloading and uploading. To save those bytes, you must provide an alternative approach.
Tip | You can enable and disable the view state programmatically for the page as well as individual controls by using the Boolean EnableViewState property. |
Disabling the view state can also pose subtler problems that are difficult to diagnose and fix, especially if you're working with third-party controls or, in general, controls for which you have source-code access. Some ASP.NET controls, in fact, might save to the view state not just properties that are officially part of the programming interface (and that can be set accordingly), but also behavioral properties that serve internal purposes and are marked as protected or even private. Unfortunately, for these controls, you do not have the option of disabling the view state. But ASP.NET 2.0 comes to the rescue with control state.
Changes in the ASP.NET 2.0 View State
Two important changes occurred to the view-state implementation in ASP.NET 2.0. As a result, two major drawbacks have been fixed, or at least greatly mitigated. The size of the view state is significantly reduced as a result of a new serialization format. The contents of the view state have been split into two states: classic view state and control state. Unlike the classic view state, the control state can't be disabled programmatically and is considered a sort of private view state for a control. This feature is ideal for developers building third-party ASP.NET controls, as it helps them to keep critical persistent properties out of the reach of end page developers.
The Serialization Format
In Figure 13-9, you see the same page running under ASP.NET 1.x and ASP.NET 2.0. A clientside button retrieves the view-state string and calculates its length. The JavaScript code needed is pretty simple:
<script language="javascript"> function ShowViewStateSize() { var buf = document.forms[0]["__VIEWSTATE"].value; alert("View state is " + buf.length + " bytes"); } </script>
As you can see, the size of the view state for the same page is quite different, and significantly smaller, in ASP.NET 2.0. Let's see why.
The contents persisted to the view state are ultimately the results of the serialization process applied to an object graph. The object graph results from the hierarchical concatenation of the view state of each individual control participating in the page's construction. The data going to the view state is accumulated in tuples made of special containers such as Pair and Triplet. Pair and Triplet are extremely simple classes containing two and three members, respectively, of type object. Each object is serialized to the stream according to various rules:
-
Simple types such as strings, dates, bytes, characters, Booleans, and all types of numbers are written as is to a binary stream.
-
Enum types, and Color, Type, Unit dictionary objects are serialized in a way that is specific to the type.
-
All other objects are checked to see whether there's a type converter object to convert them to a string. If there is, the type converter is used.
-
Objects that lack a type converter are serialized through the binary formatter. If the type is not serializable, an exception is thrown.
-
Pair, Triplet, and ArrayList objects are recursively serialized.
The resulting stream is hashed (or encrypted, based on configuration settings), Base64 encoded, and then persisted to the hidden field. This is more or less what happens already in ASP.NET 1.x. So where's the difference?
The difference is all in a little detail lying in the folds of type serialization. The type of each member being serialized is identified with a byte specifically, nonprintable characters in the 1 through 50 range. In ASP.NET 1.x, for the same purpose printable characters were used, such as <, >, l, s, I, and a few more. This change brings two benefits a smaller size and a little more speed in deserialization.
Note | The class behind the new serialization format of the ASP.NET 2.0 view state is a new type of formatter object the ObjectStateFormatter class. Specifically designed in ASP.NET 2.0 to serialize and deserialize object graphs to and from a binary writer, the class is a yet another .NET formatter object, as it also implements the IFormatter interface. In ASP.NET 2.0, the ObjectStateFormatter class replaces the LosFormatter class used in ASP.NET 1.x. LosFormatter writes to a text writer. This change is not an optimization per se, but it allows a number of other improvements. For example, it allows indexing of strings and more compact storage, as non-string values (such as numbers and Booleans) are written as binaries and take up much less space. |
The Control State
It is not uncommon for a server control to persist information across postbacks. For example, consider what happens to a DataGrid control modified to support autoreverse sorting. When the user clicks to sort by a column, the control compares the current sort expression and the new sort expression. If the two are equivalent, the sort direction is reversed. How does the DataGrid track the current sort direction? If you don't place the sort direction property in the control's view state, it will be lost as soon as the control renders to the browser.
This kind of property is not intended to be used for plain configuration such as pager style or background color. It has an impact on how the control works. What if the control is then used in a page that has the view state disabled? In ASP.NET 1.x, the control feature just stops working. Private or protected properties that are to be persisted across two requests should not be placed in the view state. In ASP.NET 1.x, you can use the session state, the ASP.NET cache, or perhaps another, custom hidden field.
In ASP.NET 2.0, the control state is a special data container introduced just to create a sort of protected zone inside the classic view state. It is safer to use the control state than the view state because application-level and page-level settings cannot affect it. If your existing custom control has private or protected properties stored in the view state, you should move all of them to the control state in ASP.NET 2.0. Anything you store in the control state remains there until it is explicitly removed. Also the control state is sent down to the client and uploaded when the page posts back. The more data you pack into it, the more data is moved back and forth between the browser and the Web server. You should use control state in ASP.NET 2.0, but you should do so carefully.
Programming the Control State
The implementation of the control state is left to the programmer, which is both good and bad. It is bad because you have to manually implement serialization and deserialization for your control's state. It is good because you can control exactly how the control works and tweak its code to achieve optimal performance in the context in which you're using it. The page's infrastructure takes care of the actual data encoding and serialization. The control state is processed along with the view state information and undergoes the same treatment as for serialization and Base64 encoding. The control state is also persisted within the same view state's hidden field. The root object serialized to the view state stream is actually a Pair object that contains the control state as the first element and the classic view state as the second member.
There's no ready-made dictionary object to hold the items that form the control state. You no longer have to park your objects in a fixed container such as the ViewState state bag you can maintain control-state data in plain private or protected members. Among other things, this means that access to data is faster because it is more direct and is not mediated by a dictionary object. For example, if you need to track the sort direction of a grid, you can do so using the following variable:
private int _sortDirection;
In ASP.NET 1.x, you have to resort to the following:
private int _sortDirection { get {return Parse.Int32(ViewState["SortDirection"]);) set {ViewState["SortDirection"] = value;) }
To restore control state, the Page class invokes the LoadControlState on all controls that have registered with the page object as controls that require control state. The following pseudocode shows the control's typical behavior:
private override void LoadControlState(object savedState) { // Make a copy of the saved state. // You know what type of object this is because // you saved it in the SaveControlState method. object[] currentState = (object[]) savedState; if (currentState == null) return; // Initialize any private/protected member you stored // in the control state. The values are packed in the same // order and format you stored them in the SaveControlState method. _myProperty1 = (int) currentState[0]; _myProperty2 = (string) currentState[1]; ... }
The LoadControlState method receives an object identical to the one you created in SaveControlState. As a control developer, you know that type very well and can use this knowledge to extract any information that's useful for restoring the control state. For example, you might want to use an array of objects in which every slot corresponds to a particular property.
The following pseudocode gives an idea of the structure of the SaveControlState method:
private override object SaveControlState() { // Declare a properly sized array of objects object[] stateToSave = new Object[ ]; // Fill the array with local property values stateToSave[0] = _myProperty1; stateToSave[1] = _myProperty2; ... // Return the array return stateToSave; }
You allocate a new data structure (such as a Pair, a Triplet, an array, or a custom type) and fill it with the private properties to persist across postbacks. The method terminates, returning this object to the ASP.NET runtime. The object is then serialized and encoded to a Base64 stream. The class that you use to collect the control state properties must be serializable. We'll examine control state in my companion book to this one, Programming Microsoft ASP.NET 2.0 Applications: Advanced Topics (Microsoft Press, 2005).
Keeping the View State on the Server
As discussed so far, there's one major reason to keep the view state off the client browser: the more stuff you pack into the view state, the more time the page takes to download and upload because the view state is held in a hidden field. It is important to note that the client-side hidden field is not set in stone, but is simply the default storage medium where the view state information can be stored. Let's see how to proceed to save the view state on the Web server in the sample file.
The LosFormatter Class
To design an alternative storage scheme for the view state, we need to put our hands on the string that ASP.NET stores in the hidden field. The string will be saved in a server-side file and read from where the page is being processed. The class that ASP.NET 1.x actually uses to serialize and deserialize the view state is LosFormatter.
The LosFormatter class has a simple programming interface made of only two publicly callable methods Serialize and Deserialize. The Serialize method writes the final Base64 representation of the view state to a Stream or TextWriter object:
public void Serialize(Stream stream, object viewState); public void Serialize(TextWriter output, object viewState);
The Deserialize method builds an object from a stream, a TextReader object, or a plain Base64 string:
public object Deserialize(Stream stream); public object Deserialize(TextReader input); public object Deserialize(string input);
The LosFormatter class is the entry point in the view-state internal mechanism. By using it, you can be sure everything will happen exactly as in the default case and, more important, you're shielded from any other details.
The ObjectStateFormatter Class
As mentioned, the LosFormatter class is replaced by ObjectStateFormatter in ASP.NET 2.0. LosFormatter is still available for compatibility reasons, however. The new serializer writes to a binary writer, whereas LosFormatter writes to a text writer. LosFormatter needs to turn everything to a string for storage, while ObjectStateFormatter capitalizes on the underlying binary model and writes out just bytes. As a result, ObjectStateFormatter serializes the same object graph into roughly half the size, and spends about half as much time in the serialization and deserialization process.
The Serialize method of ObjectStateFormatter writes the final Base64 representation of the view state to a Stream or a string object:
public string Serialize(Object graph); public void Serialize(Stream stream, Object graph);
The Deserialize method of ObjectStateFormatter builds an object from a stream or a plain Base64 string:
public object Deserialize(Stream stream); public object Deserialize(string input);
Creating a View-Stateless Page
The Page class provides a couple of protected virtual methods that the run time uses when it needs to deserialize or serialize the view state. The methods are named LoadPageStateFromPersistenceMedium and SavePageStateToPersistenceMedium:
protected virtual void SavePageStateToPersistenceMedium(object viewState); protected virtual object LoadPageStateFromPersistenceMedium();
If you override both methods, you can load and save view-state information from and to anything. In particular, you can use a storage medium that is different from the hidden field used by the default implementation? Because the methods are defined as protected members, the only way to redefine them is by creating a new class and making it inherit from Page. The following code just gives you an idea of the default behavior of LoadPageStateFromPersistenceMedium:
string m_viewState = Request.Form["__VIEWSTATE"]; ObjectStateFormatter m_formatter = new ObjectStateFormatter(); StateBag viewStateBag = m_formatter.Deserialize(m_viewState);
The structure of the page we're going to create is as follows:
public class ServerViewStatePage : Page { protected override object LoadPageStateFromPersistenceMedium() { ... } protected override void SavePageStateToPersistenceMedium(object viewState) { ... } }
Saving the View State to a Web Server File
The tasks accomplished by the SavePageStateToPersistenceMedium method are easy to explain and understand. The method takes a string as an argument, opens the output stream, and calls into the LosFormatter serializer:
protected override void SavePageStateToPersistenceMedium(object viewStateBag) { string file = GetFileName(); StreamWriter sw = new StreamWriter(file); ObjectStateFormatter m_formatter = new ObjectStateFormatter(); m_formatter.Serialize(sw, viewStateBag); sw.Close(); return; }
How should we choose the name of the file to make sure that no conflicts arise? The view state is specific to a page request made within a particular session. So the session ID and the request URL are pieces of information that can be used to associate the request with the right file. Alternatively, you could give the view state file a randomly generated name and persist it in a custom hidden field within the page. Note that in this case you can't rely on the __VIEWSTATE hidden field because when overriding the methods, you alter the internal procedure that would have created it.
The GetFileName function in the preceding code gives the file a name according to the following pattern:
SessionID_URL.viewstate
Notice that for an ASP.NET application to create a local file, you must give the ASP.NET account special permissions on a file or folder. I suggest creating a new subfolder to contain all the view-state files. Deleting files for expired sessions can be a bit tricky, and a Windows service is probably the tool that works best.
Loading the View State from a Web Server File
In our implementation, the LoadPageStateFromPersistenceMedium method determines the name of the file to read from, extracts the Base64 string, and calls LosFormatter to deserialize:
protected override object LoadPageStateFromPersistenceMedium() { object viewStateBag; string file = GetFileName(); try { StreamReader sr = new StreamReader(file); string m_viewState = sr.ReadToEnd(); sr.Close(); } catch { throw new HttpException("The View State is invalid."); } LosFormatter m_formatter = new LosFormatter(); try { viewStateBag = m_formatter.Deserialize(m_viewState); } catch { throw new HttpException("The View State is invalid."); } return viewStateBag; }
To take advantage of this feature to keep view state on the server, you only need to change the parent class of the page code file to inherit from ServerViewStatePage:
public partial class TestPage : ServerViewStatePage { }
Figure 13-10 shows the view-state files created in a temporary folder on the Web server machine.
The page shown in Figure 13-10 enables view state, but no hidden field is present in its clientside code.