State is the current value of all the controls and variables for the current user in the current session. The Web is inherently a stateless environment, which means that every time a page is posted to the server and then sent back to the browser, the page is recreated from scratch. Unless you explicitly preserve the state of all the controls before the page is posted, the state is lost and all the controls are created with default values. One of the great strengths of ASP.NET is that it automatically maintains state for server controls both HTML and ASP. This section will explore how that is done and how you can use the ASP.NET state management capabilities. ASP.NET manages three types of state :
Table 8-2 compares the different kinds of state.
The following sections will examine each type of state. 8.4.1. View StateThe view state is the state of the page and all its controls. The view state is automatically maintained across posts by the ASP.NET Framework. When a page is posted to the server, the view state is read. Just before the page is sent back to the browser the view state is restored. The view state is saved in the state bag (described in the next section) via hidden fields on the page that contain the state encoded in a string variable. Since the view state is maintained in standard html form fields, it works with all browsers. If you don't need to maintain the view state for a page, you can boost performance by disabling view state for that page. For example, if the page does not post back to itself, or if the only control that needs to have its state maintained is populated from a database with every trip to the server, there is no need to maintain the view state for that page. To disable view state for a page, add the EnableViewState attribute with a value of False to the Page directive: <%@ Page Language="VB" EnableViewState="false" %> The default value for EnableViewState is true.
It is also possible to maintain or disable view state for specific controls . This is done with the Control.EnableViewState property, which is a Boolean value with a default of true. Disabling view state for a control, just as for the page, will improve performance slightly. This would be appropriate, for example, in the situation where a control is populated from a database every time the page is loaded. In this case, the contents of the control would simply be overridden by the database query, so there is no point in maintaining view state for that control, especially if a lot of data is involved. There are some situations where view state is not the best place to store data. If there is a large amount of data to be stored, then view state is not an efficient mechanism, since the data is transferred back and forth to the server with every page post. If there are security concerns about the data and it is not otherwise being displayed on the page, then including the data in view state increases the security exposure. Finally, view state is optimized only for strings, integers, Booleans, arrays, lists, and dictionaries. Other .NET types may be serialized and persisted in view state, but will result in degraded performance and a larger view-state footprint. In some of these instances, session state might be a better alternative; on the other hand, view state does not consume any server resources and does not time out like session state. 8.4.2. State BagIf there are values that are not associated with any control and you wish to preserve these values across roundtrips, you can store these values in the page's state bag . The state bag is a data structure containing attribute/value pairs, stored as strings associated with objects. The valid objects are the primitive data types integers, bytes, strings, Booleans, and so on. The state bag is implemented using the StateBag class, which is a dictionary object. You add or remove items from the state bag as with any dictionary object assigning a value to a "key," as shown below. The state bag is maintained using the same hidden fields as the view state. You can set and retrieve values of things in the state bag using the ViewState keyword . To experiment with this, add another page to your application (or create a new application with the new page) named StateBagDemo.aspx. In either case, set the new page as the start page. Between the <div> tags, write the word Counter: and drag a label onto the form, setting its ID to lblCounter and removing any Text attribute (or setting the text to blank through the Properties window). Drag a button onto the form, set its ID to btn and set its text to Increment Counter. When you are done, the HTML in the source window should look like that in Example 8-2. Example 8-2. Counter source<div> Counter: <asp:Label runat="server"></asp:Label> <asp:Button runat="server" Text="Increment Counter" /> </div> You'll track the Counter as a property by adding the code shown in Example 8-3 to the code-behind. Example 8-3. Counter propertyPublic Property Counter( ) As Integer Get If (ViewState("intCounter") IsNot Nothing) Then Return CInt(ViewState("intCounter")) 'extract from view state Else Return 0 End If End Get Set(ByVal value As Integer) ViewState("intCounter") = value 'Add to view state End Set End Property ViewState is the dictionary, "int Counter" is the key, and value is the value associated with that key. Override the OnLoad( ) method (as discussed above), adding the lines shown in bold in Example 8-4. Example 8-4. Overriding the StateBagDemo form OnLoad event handler Protected Overrides Sub OnLoad(ByVal e As System.EventArgs) MyBase.OnLoad(e) lblCounter.Text = Counter.ToString( ) Counter += 1 End StateBagDemo.aspx sets up a Counter that is maintained as long as the session is active. Every time the Increment Counter button is clicked, the page is reloaded, which causes the Counter to increment. To make this work, you need to maintain state between postbacks. One way to do so is to store the value of the counter in a state bag. ASP.NET provides that state bag in its ViewState collection, which you access by indexing into it by name. In this example, you access the ViewState collection through the Counter Property. The property's get accessor casts the value stored in the ViewState to an integer, because ViewState stores objects. Each time the page is loaded, you display the value of the Counter property and then increment it by 1: Protected Overrides Sub OnLoad(ByVal e As System.EventArgs) MyBase.OnLoad(e) lblCounter.Text = Counter.ToString( ) Counter += 1 End Sub In the Get block of the Counter property, the contents of the statebag named intCounter are tested to see if anything is there: If (ViewState("intCounter") IsNot Nothing) Then If the intCounter state bag is not empty, the value stored is returned; otherwise, the value 0 is returned. To retrieve the value, you access it by name, but you must cast the object back to an integer to use it in your program. If (ViewState("intCounter") IsNot Nothing) Then Return CInt(ViewState("intCounter")) Else Return 0 End If
8.4.3. Session StateWhile an application is running, there will be many sessions. A session is a series of requests coming from a single browser client in a more or less continuous manner. If there are no requests from that client within a specified period of time (the timeout period), then the session ends. The default timeout period is 20 minutes. As noted above, the Web is an inherently stateless environment. The HTTP protocol has no means of identifying which requests should be grouped together in the same session. A session must be imposed on top of HTTP. ASP.NET provides session state with the following features:
Session state is stored in server memory separately from the ASP.NET process. This means that if the ASP.NET process crashes or is restarted, the session state is not lost. Session state can also be stored on a dedicated and shared machine or even within a database. Sessions are identified and tracked with a 120-bit SessionID that is passed from client to server and back using either an HTTP cookie or a modified URL, depending on how the application is configured. The SessionID is handled automatically by the .NET Framework; there is no need to manipulate it programmatically. The SessionID consists of URL-legal ASCII characters that have two important characteristics:
Session state is implemented using the Contents collection of the HttpSessionState class. This collection is a (key-value) dictionary containing all the session state dictionary objects that have been directly added using code. To see session state at work, create a new page SessionState.aspx (either in this application or in a new application) and set it to be the start page. Drag a RadioButtonList onto your page, set its ID to rbl and use the smart tag to edit the items. Add three items. The first has the text .NET and the value n, the second has the text Databases and the value d, and the third has the text Hardware and the value h, as shown in Figure 8-31. Figure 8-31. Adding three items to RadioButtonListAlso add a button, and set its ID to btn and its Text to Submit. Add a label control (id=lblMsg), and a drop-down list with the ID ddl. Set its Visible property to False. You're all set. Now just change to Code view and implement a handler for the button's Click event, and one for the SelectedIndexChanged event in the RadioButtonList control. The complete source for these methods is shown in Example 8-5. Example 8-5. SessionState.aspx.vbPartial Class SessionState_aspx Inherits System.Web.UI.Page Protected Sub btn_Click( _ ByVal sender As Object, _ ByVal e As System.EventArgs) Handles btn.Click If (rbl.SelectedIndex = -1) Then lblMsg.Text = "You must select a book category." Else Dim sb As StringBuilder = New StringBuilder( ) sb.Append("You have selected the category ") sb.Append(CStr(Session("cattext"))) sb.Append(" with code """) sb.Append(CStr(Session("catcode"))) sb.Append(""".") lblMsg.Text = sb.ToString( ) ddl.Visible = True Dim CatBooks() As String = CType(Session("books"), String( )) ' Populate the DropDownList. Dim i As Integer ddl.Items.Clear( ) For i = 0 To CatBooks.GetLength(0) - 1 ddl.Items.Add(New ListItem(CatBooks(i))) Next End If End Sub Protected Sub rbl_SelectedIndexChanged( _ ByVal sender As Object, _ ByVal e As System.EventArgs) Handles rbl.SelectedIndexChanged If (rbl.SelectedIndex <> -1) Then Dim Books(3) As String Session("cattext") = rbl.SelectedItem.Text Session("catcode") = rbl.SelectedItem.Value Select Case (rbl.SelectedItem.Value) Case "n" Books(0) = "Programming Visual Basic 2005" Books(1) = "Programming ASP.NET" Books(2) = "C#: A Developer's Notebook" Case "d" Books(0) = "Oracle & Open Source" Books(1) = "SQL in a Nutshell" Books(2) = "Transact-SQL Programming" Case "h" Books(0) = "PC Hardware in a Nutshell" Books(1) = "Dictionary of PC Hardware and " + _ "Data Communications Terms" Books(2) = "Linux Device Drivers" End Select Session("books") = Books End If End Sub End Class Look first at rbl_SelectedIndexChanged, the RadioButtonList event handler. After testing to ensure that something is selected, rbl_SelectedIndexChanged defines a string array to hold the lists of books in each category. Then it assigns the selected item Text and Value properties to two Session dictionary objects. Session("cattext") = rbl.SelectedItem.Text Session("catcode") = rbl.SelectedItem.Value The first line stores the text of the selected item in session state using the key "cattext." rblSelectedIndexChanged next uses a Select Case statement to fill the previously declared string array (Books) with a list of books, depending on the book category selected. Finally, the method assigns the string array to a Session dictionary object. Session("books") = Books This example stores only strings and an array in the Session dictionary objects. However, you can store any object that inherits from ISerializable. These include all the primitive data types and arrays comprising of primitive data types, as well as the DataSet, DataTable, HashTable, and Image objects. This allows you to store query results, for example, or a collection of items in session state to implement a user's shopping cart. The other event-handler method, btn_Click, is called whenever the user clicks on the Submit button. It first tests to verify that a radio button has been selected. If not, then the Label is filled with a warning message. If (rbl.SelectedIndex = -1) Then lblMsg.Text = "You must select a book category." The Else clause of the If statement is the meat of this page. It retrieves the Session dictionary objects and uses the StringBuilder class to concatenate the strings into a single string for display in the Label control. dim sb as StringBuilder = new StringBuilder( ) sb.Append("You have selected the category ") sb.Append(Cstr(Session("cattext"))) sb.Append(" with code """) sb.Append(Cstr(Session("catcode"))) sb.Append(""".") lblMsg.Text = sb.ToString( ) The btn_Click method also unhides the DropDownList that was created and made invisible in the HTML portion of the page. The method then retrieves the string array from the Session dictionary object and populates the DropDownList. ddl.Visible = true dim CatBooks( ) as string= CType(Session("books"), string( )) ' Populate the DropDownList. dim i as integer ddl.Items.Clear( ) for i = 0 to CatBooks.GetLength(0) - 1 ddl.Items.Add(new ListItem(CatBooks(i))) next Because the Page directive in the VB.NET example sets Strict="true", it is necessary to explicitly cast the Session dictionary object containing the string array from object to type string array using the CType function. The results are shown in Figure 8-32. Figure 8-32. Session state demonstrationAs you look at this example, you might wonder what advantage is gained here by using session state, rather than just using the programmatically accessible control values. The answer is that since this example is fairly trivial, no advantage is gained. However, in a real-life application with many different pages, session state provides an easy method for values and objects to be passed from one page to the next, with all the advantages listed at the beginning of this section. 8.4.4. Session-State ConfigurationThe configuration of session state is controlled on a page-by-page basis by entries in the Page directive at the top of the page. On an application-wide basis, it is controlled by a file called Web.config, typically located in the virtual root directory of the application. Session state is enabled by default. You can enable session state for a specific page by adding the EnableSessionState attribute to the Page directive, as in the following VB Page directive: <%@ Page Language="VB" Strict="true" EnableSessionState="true"%> To disable session state for the page you would use: <%@ Page Language="VB" Strict="true" EnableSessionState="false"%> To enable session state in a read-only mode that is, values can be read but not changed use the ReadOnly value of EnableSessionState, as in: <%@ Page Language="VB" Strict="true" EnableSessionState="ReadOnly"%> (All of the values for EnableSessionState are case-insensitive.) The reason for either disabling session state or making it read-only is performance. If you know that you will not be using session state on a page, you can gain a performance boost by disabling it. If the ASP.NET process crashes or is restarted, the session state is not lost. In addition to unplanned outages, ASP.NET can be configured to periodically perform a preventive restart of each process after a specified number of requests or after a specified length of time, improving availability and stability (this is configurable in machine.config and/or Web.config). Web.config is an XML file and as such it must be well-formed . The values are case-sensitive, and the file consists of sections delimited by tags. The session-state configuration information is contained within the <system.web> section, which is contained within the <configuration> section. Thus, a typical session-state configuration snippet will look something like Example 8-6. Example 8-6. Code excerpt from Web.config<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.web> . . . <sessionState mode="InProc" cookieless="false" timeout="20" stateConnectionString="tcpip=127.0.0.1:42424" sqlConnectionString="data source=127.0.0.1;userid=sa;password=" /> There are five possible attributes for the sessionState section:
8.4.5. Session-Scoped Application ObjectsOne additional way of providing information across the session is through the use of static objects, which are declared in the global.asax file. Once declared with the Scope attribute set to Session, the objects are accessible to the session by name anywhere within the application code. |