State in ASP.NET Applications
Preserving state across HTTP requests is a major problem in Web programming, and ASP.NET provides several facilities that are convenient to use. There are two main types of state to be preserved.
Application state is global information that is shared across all users of a Web application.
Session state is used to store data for a particular user across multiple requests to a Web application.
Shared Data Members
Shared data members of a class are shared across all instances of a class. Hence shared data members can be used to hold application state.
In our case study the class Global has a single shared member acmedat of the class Acme.
Thus the broker and custs objects within Acme will hold shared data that is the same for all users of the application. Each user will see the same list of hotels. You can view the source code for the Acme class in the AcmeLib project.
Public Class Acme Implements IAcmeUser, IAcmeAdmin Public broker As HotelBroker Private custs As Customers Private users As ArrayList Private currUser As User
If you like, you may perform a small experiment at this stage. The directory HotelAdmin contains a special version of the Acme Web site that makes available the hotel administration interface IHotelAdmin to the special user with user ID of "admin". When this privileged user logins, a special home page will be displayed that provides a link to "Administer Hotels," as illustrated in Figure 1431.
Figure 14-31 Home page of the Acme Web site tailored for administrators.
Run this Web application, either from the "Hotel Admin" link on the example programs home page or else via the URL http://localhost/ Chap14/HotelAdmin/Main.aspx. Log in as "admin" and follow the link to "Administer Hotels." You will be brought to a page showing a list of all the hotels. Select the first hotel (Dixie) on the list and click the Delete Selected Hotel button and then the Refresh button. You will now see an updated list of hotels, as shown in Figure 1432.
Figure 14-32 Hotel administration page after deleting the hotel Dixie.
If your Web server is on a network, you can now try running the same Web application from a different client. Use the URL
http://<server-name>/Chap14/HotelAdmin/Main.aspx
where <server-name> is the name of your server machine.6 Again log in as "admin" and go to the "Hotel Admin" page. You should see the same list of hotels seen by the other client, with hotel Dixie not on the list.7 You can also experiment with different browser sessions on the same machine, adding and deleting hotels, and using the Refresh button.
Application Object
You can store global application information in the built-in Application object, an instance of the class HttpApplicationState. You can conveniently access this object through the Application property of the Page class. The HttpApplicationState class provides a key-value dictionary that you can use for storing both objects and scalar values.
For example, as an alternative to using the class Global with the shared member acmedat that we previously used, we could instead use the Application object. We make up a string name for the keyfor example, "Hotel-State." In Global.asax we can then instantiate an Acme object and store it in the Application object using the following code.
Sub Application_Start(ByVal sender As Object, _ ByVal e As EventArgs) Application("HotelState") = New Acme() End Sub
You can then retrieve the Acme object associated with "HotelState" by using the index expression on the right-hand side and casting to Acme, as illustrated in the code,
Dim acmedat As Acme = _ CType(Application("HotelState"), Acme) Dim name As String = acmedat.CurrentUserInfo.FirstName
As a little exercise in employing this technique, you may wish to modify Step 2 of AcmeWeb to use the Application object in place of a shared data member. The solution to this exercise can be found in the directory Applica-tionObject.8
Session Object
You can store session information for individual users in the built-in Session object, an instance of the class HttpSessionState. You can conveniently access this object through the Session property of the Page class. The HttpSessionState class provides a key-value dictionary that you can use for storing both objects and scalar values, in exactly the same manner employed by HttpApplicationState.
Our AcmeWeb case study provides an example of the use of a session variable UserId for storing a string representing the user ID. The session variable is created and initialized in Global.asax.
Sub Session_Start(ByVal sender As Object, _ ByVal e As EventArgs) ' Fires when the session is started Session("UserId") = "" End Sub
We use this session variable in the Page_Load event of our home page Main.aspx to detect whether we have a returning user or a new user. A new user is redirected to the login page. (Note that "returning" means coming back to the home page during the same session.)
Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load 'Put user code to initialize the page here Dim userid As String = CStr(Session("UserId")) If userid = "" Then Response.Redirect("Login.aspx") End If If Not IsPostBack Then Dim name As String = _ Global.acmedat.CurrentUserInfo.FirstName lblUserName.Text = "Welcome, " & name lblLogin.Text = "(If you are not " & name & _ ", please login)" End If End Sub
There are some interesting issues in the implementation of session variables.
Typically, cookies are used to identify which requests belong to a particular session. What if the browser does not support cookies, or the user has disabled cookies?
There is overhead in maintaining session state for many users. Will session state "expire" after a certain time period?
A common scenario in high-performance Web sites is to use a server farm. How can your application access its data if a second request for a page is serviced on a different machine from that on which the first request was serviced?
SESSION STATE AND COOKIES
Although by default ASP.NET uses cookies to identify which requests belong to a particular session, it is easy to configure ASP.NET to run cookieless. In this mode the Session ID, normally stored within a cookie, is instead embedded within the URL. We will discuss cookieless configuration in the next section.
SESSION STATE TIMEOUT
By default session state times out after 20 minutes. This means that if a given user is idle for that period of time, the session is torn down; a request from the client will now be treated as a request from a new user, and a new session will be created. Again, it is easy to configure the timeout period, as we will discuss in the section on Configuration.
SESSION STATE STORE
ASP.NET cleanly solves the Web farm problem, and many other issues, through a session state model that separates storage from the application's use of the stored information. Thus different storage scenarios can be implemented without affecting application code. The .NET state server does not maintain "live" objects across requests. Instead, at the end of each Web request, all objects in the Session collection are serialized to the session state store. When the same client returns to the page, the session objects are dese-rialized.
By default, the session state store is an in-memory cache. It can be configured to be memory on a specific machine, or to be stored in an SQL Server database. In these cases the data is not tied to a specific server, and so session data can be safely used with Web farms.