State Management in ASP.NET...
By: Chris Sully
Date: March 3, 2003
Printer Friendly Version
Introduction
The web is a stateless medium – state is not maintained between client requests by default. Technologies must
be utilized to provide some form of state management if this is what is required of your application, which
will be the case for all but the simplest of web applications. ASP.NET provides several mechanisms to manage
state in a more powerful and easier to utilize way than classic ASP. It is these mechanisms that are the
subject matter for this article.
Page Level State - ViewState
Page level state is information maintained when an element on the web form page causes a subsequent request to
the server for the same page – referred to as ‘postback’. This is appropriately called ViewState as the data
involved is usually, though not necessarily, shown to the user directly within the page output.
The Control.ViewState property provides a dictionary object for retaining values between such multiple requests
for the same page. This is the method that the page uses to preserve page and control property values between
round trips.
When the page is processed, the current state of the page and controls is hashed into a string and saved in the
page as a hidden field. When the page is posted back to the server, the page parses the view state string at
page initialization and restores property information in the page.
ViewState is enabled by default so if you view a web form page in your browser you will see a line similar to
the following near the form definition in your rendered HTML:
<input type="hidden" name="__VIEWSTATE"
value="dDwxNDg5OTk5MzM7Oz7DblWpxMjE3ATl4Jx621QnCmJ2VQ==" />
When a page is re-loaded two methods pertaining to ViewState are called: LoadViewState and SaveViewState.
Page level state is maintained automatically by ASP.NET but you can disable it, as necessary, by setting the
EnableViewState property to false for either the controls whose state doesn’t need to be maintained or for
the page as a whole. For the control:
<asp:TextBox id=”tbName” runat=”server” EnableViewState=”false” />
for the page:
<%@ Page EnableViewState=”false” %>
You can validate that these work as claimed by analyzing the information presented if you turn on tracing for a
page containing the above elements. You will see that on postback, and assuming ViewState is enabled, that the
LoadViewState method is executed after the Page class’ Init method has been completed. SaveViewState is called
after PreRender and prior to actual page rendering.
You can also explicitly save information in the ViewState using the State Bag dictionary collection, accessed
as follows:
ViewState(key) = value
Which can then be accessed as follows:
Value = ViewState(key)
It is important to remember that page level state is only maintained between consecutive accesses to the
same page. When you visit another page the information will not be accessible via the methods above. For this
we need to look at other methods and objects for storing state information.
Session Level State
What is a user session? Slightly simplifying it’s the interaction between a user’s first request for a page
from a site until the user leaves the site again. Now what if you login into this site at your first request,
assuming this is a site you have previously registered with. How does the application remember who you are for
the rest of the ‘session’? Or if you have items in your shopping cart within an e-commerce web application how
does the application ‘remember’ this information when you request to go to the checkout?
The answer may well be session state though the underlying mechanism by which this is achieved may be one of
several options. ASP.NET creates a session (it reserves a section of memory) for a user when they first arrive
at the site and assigns that user session a unique id that is tied to that section of memory. By default,
ASP.NET then creates a cookie on the client that contains this same id. As this id will be sent with any
subsequent http requests to this server ASP.NET will be able to match the user against the reserved section
of memory. Further the application can store data related to that user session in this reserved memory space
to maintain state between requests. It must be remembered that using session state is using server resources
for every user of the site so you need to consider the resources required of items you choose to store.
An example:
session(“name”)=”Chris Sully”
sets a session variable with key ‘name’ and value “Chris Sully”. To retrieve/ display this we use:
lblUserName.text=session(“name”)
In this case assigning to the text property of a label web server control.
By default the resources associated with this session state maintenance are released if the user does not
interact with the site for 20 minutes. If the user returns after a 20 minutes break a new session will have
been created and any data associated with their previous session will have been lost. However, you can also
destroy the session information for a user earlier within the application if so desired via
Session.Abandon
and you may also change the default value from 20 minutes. To redefine the session timeout property you use:
Session.Timeout = 5
Alternatively, and representing a more likely scenario, you would specify the value in your web.config file:
<configuration>
<system.web>
<sessionState timeout=10 />
</system.web>
</configuration>
which would half the default timeout from 20 to 10 minutes.
We'll return to some of the other sessionState properties in subsequent sections.
Looking at a little more detail at the session initialization process:
- User makes a request of the server
- ASP.NET retrieves the user’s sessionID via the cookie value passed in the HTTP request from the client
computer. If one does not exist ASP.NET creates one, and raises the Session_OnStart event (which can be
reacted to in the global.asax, amongst other locations).
- ASP.NET retrieves any data relating to the sessionID from a session data store. This data store may be of
a variety of types, as we shall explore in subsequent sections.
- A System.Web.SessionState.SessionState object is created and populated with the data from the previous
step. This is the object you access when using the shortcut
session(“name”)=”Chris Sully”
There is also a Session_OnEnd which you can code against in your global.asax.
SQLServer
We can use SQLServer to store our session state. We can use other databases if we want to but ASP.NET
includes built in support for SQLServer as well as other data stores that make life easier for developers.
As you might expect, SQLServer should be the choice for storage of session information in high end,
performance critical web applications.
To enable session state storage with SQLServer we'll need the infrastructure to support this, i.e. the
tables and stored procedures. Microsoft has supplied these and they are located at
C:\winnt\Microsoft.net/framework/[version directory]
On my system:
C:\winnt\Microsoft.net/framework/v1.0.3705/InstallSQLState.sql
And you also have the TSQL to remove the setup: UninstallSQLState.sql.
So, to enable SQLServer session support open up and execute InstallSQLState.sql in query analyzer. If you
then investigate what’s new in your SQLServer setup you will see a new database named AspState with 15 or
so stored procedures used to insert and retrieve the associated session data. You won’t see any new tables!
However, if you expand the tempdb database and view the tables therein, you will see two new tables:
ASPStateTempApplications and ASPStateTempSessions which is where our session state information shall be held.
Now, all we need to do, as ASP.NET takes care of everything else for us, is modify web.config so that the
ASP.Net application knows it should be using SQLServer for session state management:
<configuration>
<system.web>
<sessionState mode=”sqlserver” sqlConnectionString=”connectionString” />
</system.web>
</configuration>
where you should replace connectionString with that for you own machine.
If you want to test this, create a simple page which adds an item to the session state. This can be as simple
as assigning a value to a session variable a la: session(“name”)=”Chris Sully”, perhaps simply placing this
in the OnLoad sub of an otherwise blank aspx. View this in your browser. Don’t close the browser window after
the page is loaded as you’ll end the session. Remember that after the first request by default the session
will last 20 minutes.
If you now examine the contents of the ASPStateTempApplications table in tempdb either with Enterprise Manager
or Query Analyser, you will see an entry corresponding to the above set session variable.
The other possibly important consideration is that the session data is stored external to the ASP.Net
process. Hence, even if we restart the web server the information will remain available to subsequent user
requests. We’ll look at another mechanism for such data isolation shortly.
Cookies
We’ve already introduced that cookies are central to the concept of session – they are the client half of
how session state is maintained over browser requests. As well as using them for user identification purposes
we can also use them to store custom session information. Cookie functionality is exposed via the HttpCookie
class in .NET. This functionality may also be accessed via the Response and Request ASP.NET classes.
Cookies used in this way don’t tie in strongly with the inbuilt state management capabilities of .NET but
they can provide useful custom session state management functionality.
To add a cookie (store on the client browser machine) you use the response object, e.g.:
response.cookies(“ExampleCookie”)(“Time”)= datetime.now.tostring(“F”)
i.e.,
response.cookies(“CookieName”)(“keyName”) = value
‘F’ refers to the Full date/time pattern (long time) by the way.
To retrieve a cookie you use the request object. The cookie is associated with the server URL and hence
will be automatically appended to the request parameter collection and hence accessible as follows:
TimeCookieSet = Request.Cookies(“ExampleCookie”)(“Time”)
There are other options available. For example, you may set the Expires property of the cookie. This may be
a fixed date or a length of time from the present date:
Response.cookies(“ExampleCookie”).Expires = DateTime.FromString(“12/12/2003”)
Response.cookies(“ExampleCookie”).Expires = DateTime.Now.AddMonths(6) Cookie Munging
You may wish to configure your applications to use session state without relying on cookies. There could
be several reasons for this:
- You need to support old browser types that do not support cookies.
- You wish to cater for people who have chosen to disable cookie support within their browser.
- Certain types of domain name redirection mean that cookies / conventional state management will not work.
Cookie munging causes information regarding the session state id to be added to URL information. Thus the
link between client and session data on the server is maintained.
It is simply enabled, as follows:
<configuration>
<system.web>
<sessionState cookieless=”true” />
</system.web>
</configuration>
If you change your web.config file to the above and then view a page which uses both the session object
and postback you’ll see ASP.Net has inserted some extra information in the URL of the page. This extra
information represents the session ID.
Cookie munging certainly does the job in instances where it is required but should be avoided where it is not,
as it is insecure being susceptible to manual manipulation of the URL string.
Session State Server
Session State Server runs independently of ASP.Net, the current application, web server and (possibly) the
server meaning you have good isolation of data and hence if there is a problem with the web server you may
still be able to recover the user session data. This without the need for SQLServer. The State Server also
presents a number of extra facilities including the choice of where and how to deploy the facility.
You can run the StateServer in the same process as the ASP.Net application (“InProc” – the default). This
is similar to how classic ASP managed session state. Why would we want to do this when we have just lauded
the benefits of data isolation? Performance is the answer – data stored in the same process is accessed quickly.
You can test this via restarting IIS (iisreset) when you know you have some session data in your application.
You could also try restarting the ASP.NET application via the MMC snap-in, the effect is the same. This is
achieved by removing and recreating the IIS application (right click-properties on the application
sub-directory/ virtual directory).
A couple of simple test scripts would be:
1:
<configuration>
<system.web>
<sessionState cookieless=”true” />
</system.web>
</configuration><%@ Page Language="VB" %>
<html>
<head>
</head>
<body>
<form runat="server" ID="Form1">
<% session("test")="test" %>
</form>
</body>
</html>
2:
<%@ Page Language="VB" %>
<html>
<head>
</head>
<body>
<form runat="server" ID="Form2">
<%=session("test")%>
</form>
</body>
</html>
So, if you run 1, then 2 directly after without closing the browser window, the value will be maintained
and you’ll see ‘test’ displayed. If you reset IIS or the IIS application in between the browser requests
session information will be lost.
You can also run State Server out of process (“Out-of-Proc”) which means that session state information
can be stored in alternative locations to the ASP.NET process space. Hence, if there is a problem with the
application or web server state is maintained. In this scenario, the ASPNETState process (aspnet_state.exe)
is used, which runs as a service, storing session data in memory.
First thing we need to do therefore is make sure this service is up and running. This can be done via the
command line:
Net start aspnet_state
Though if you’re going to this out of process support for an application you will want to setup the service
to start on machine boot up (admin tools – services).
Next it’s back to that Web.Config file to tell the application to use the facility:
<configuration>
<system.web>
<sessionState mode=”stateserver” stateConnectionString=”tcpip=127.0.0.1:80” />
</system.web>
</configuration>
In actual fact the stateConnectionString attribute is not required when pointing to a port on the
local machine, as it is above (which supplies the default setting), but is important if you wish
to use another machine and port for extra security/ reliability. It is included to demonstrate
the syntax.
If you now go back and try the session maintenance test you won’t lose that session data.
Application level state
Using Application state is similar to using session state: the HttpApplicationState class contains an
Application property that provides dictionary access to variables:
Application(“key”) = value
The difference with session state is that data is stored per IIS application as opposed to per user session.
Thus if you set up:
application(“name”)=”Chris Sully”
in your global.asax file for the application this value is available to all pages of your application and
will be the same value for all users who visit.
Setting state information is not limited to global.asax – any page of the application being run by any user can do this. There is an implication here – multiple users trying to change the value of an application variable could lead to data inconsistencies. To prevent this the HttpApplicationState class provides the lock method that ensures only one user (actually a process thread) is accessing the application state object at any one time. Thus you should lock before amending an application value and unlock immediately afterwards so others have access to the variable.
Other options for state management: caching
Another method of storing data on the server is via the cache class. This effectively extends the capabilities
of storing data at the application level. Frequently used data items may be stored in the cache memory for
presentation. For example, data for a drop down list may be stored in a cached object so that the cached copy
is used rather than obtaining the data from the database on every occasion.
You may store your objects in cache memory and set the properties of the cache to control when the cache
might release its resources. For example, you might use the TimeSpan property that specifies how long the
item should remain in the cache after it is last accessed. You can also specify a dependency of the cached
item on a datasource, for example an XML file. If this file is changed the chance can be programmed to react
to this event accordingly and update the cached object.
For further information on this subject see my article on
caching on dotnetjohn.
Conclusion
I hope this article has provided a useful overview of the state management options and support in .NET.
With all the forms of state management there exists a balance between the needs of the application and the
associated resources used.
In particular, be aware that page level state is enabled by default and should be disabled if not required,
particularly if you are manipulating significant amounts of data within DataBound controls. As well as using
server resources, leaving ViewState enabled in such a situation will increased the page size, and hence
download times, significantly. In such a situation it may be better to cache the data on the server for the
postback rather than transmit the data in the ViewState, or even rely on the data caching facilities of your
chosen DBMS.
ASP.Net provides significantly extended support for session state maintenance via SQLServer and Session
State Server. The choice is down to the needs of your application and, in particular, how important data
isolation from your IIS application is.
References
.NET Framework SDK documentation
ASP.NET: Tips, Tutorials and Code Sams Mitchell et al.
|