State Maintenance in ASP.NET
by Craig Utley
In traditional Active Server Pages (ASP) applications, there was a great
amount of debate about how best to maintain state on the server. Maintaining
state in Web applications is an incredibly common task, and it is critical
to building usable Web applications. After all, without being able to
maintain state, your Web applications would be nothing more than brochure-ware.
If you want to have such functionality as allowing people to participate
in threaded discussion groups, order products, or see only the areas to
which they have access, you will have to use some mechanism for maintaining
state information.
ASP applications could benefit from automatic server-side state management,
but there were some problems with ASP's implementation that caused many
developers to shy away from Microsoft's built-in session state mechanism.
This article will discuss how ASP.NET seeks to overcome the ASP limitations
with enhancements to server-side state management.
Maintaining State and HTTP
One of the reasons that state management is an issue is because HTTP
is a stateless protocol. This means that when a client requests a page,
the server responds with the data and then breaks the connection. If the
client then requests another page, this request makes a new connection
and a new page is served up. The server is seeing every request as a new
request.
The stateless nature of HTTP makes maintaining state quite a challenge.
With client/server applications, you log into a server and maintain a
live connection. Maintaining state information is easy, because the server
resources have validated you and keep connections open for you. After
a certain period of inactivity, you may be logged out. HTTP doesn't have
to log you out after a period of inactivity, because it shuts down the
connection as soon as the response has been sent.
It is actually possible to maintain state using HTTP while not using
the ASP or ASP.NET state management mechanisms. However, most of these
require a significant amount of programming effort from the developer.
You can use cookies, hidden fields, or modify the URLs, for example.
- The first option for maintaining state on your own is to use cookies.
Cookies are files that are stored on the client, and allow you to store
data between trips to the server. This means that you can record, in
a client-side cookie, any items you need to remember, such as the items
the shopper places in a shopping cart. When the user goes to check out,
the Web application can request the values from the cookie, and the
values are returned to the server, where they can populate the shopping
cart and the checkout process can occur.
Even if you are using client-side cookies, you must have a program of
some sort on the server that retrieves the cookie values and processes
them. In addition, some users, concerned about privacy, turn off the
ability to accept cookies. This would destroy the cookie method as an
option of maintaining state, and as you will see, this caused problems
for ASP's state management.
- A second option is to write all data into the HTML page as hidden
fields. HTML forms have a variety of field elements, and one of them
is a hidden field. This hidden field can store data that is automatically
passed back to the server with each form submission. Therefore, the
data can be passed back to the server with each form submission. This
approach often requires a significant coding effort on the server in
order to insert the values in hidden fields in the HTML page that is
going back to the client. You must also pass up all the values with
each request from the client browser, as well as having server-side
code that extracts the values from the hidden fields.
- Another approach is to add a unique identifier into all URLs on a
page. When the user first visits your site, you generate a unique number
for them, and on any page you send back, you modify the URLs to include
the unique identifier. Then, with each subsequent request, they are
passing this unique number back to the web server. On the Web server,
you are maintaining the rest of their data, usually in a database.
With any of these approaches, it would be common to store some values
in a database on the server side. For example, if you are just passing
a unique identifier to the client in a cookie, in a hidden field, or by
modifying the URLs, you may be storing the products in their shopping
cart in a database on the server. For example, if you think about BarnesandNoble.com,
they remember who you are when you return to their site, because your
user name is stored in a cookie. However, any items in your wish list
are stored in a database on the server side. This means that if you log
into the site from another machine, you still have access to your wish
list and other settings. Cookies are machine-specific, so information
stored only in cookies is not available as the user moves from one machine
to another.
State Maintenance with ASP.NET
Coding your own state management can be a challenge. There is a significant
amount of effort in coordinating cookies, hidden fields, and database
values. When Microsoft first released ASP, it allowed you to easily access
some of Internet Information Services' (IIS) built-in objects for state
maintenance. This made it very easy to maintain state, which was a boon
for developers. However, there were some serious limitations to the ASP
model, which ASP.NET seeks to remedy. We'll first examine ASP.NET's default
state model, which works just as the ASP model does, and has the same
limitations.
The three session state options for ASP.NET are:
- In-Process
- Out-Of-Process with a Windows Service
- Out-Of-Process with SQL Server
All three options, however, use the same concept of an Application object,
which will be examined first.
Web Applications and the Application Object
When using the term "application" it is often easy to see what is meant
with traditional Windows application. There is typically a single executable
file, along with several DLLs, and potentially a few additional files.
With Web applications, however, there are typically a whole set of ASP,
ASPX, and HTML pages, with the potential addition of CSS and other files.
Unlike a Windows application, people can start by requesting any individual
page, meaning that many Web applications require some coding effort to
define a starting point.
An "application" in ASP/ASP.NET terms is defined by a virtual directory
and its subdirectories. If you create a virtual directory in IIS, the
physical directory to which that virtual directory points, and all of
its subdirectories, contain that application. When the first ASP/ASPX
page is accessed anywhere in that directory structure, IIS creates an
Application object. This Application object then runs until IIS is shut
down, which means that in theory, the Application object stays in memory
forever. Regardless of how many users access the application, there is
only one Application object per application.
The Application object can be used to hold variables that are accessible
to all users of an application. If you think of traditional applications,
a global variable is a variable accessible throughout the entire application,
but the global variable in one instance of the application is not accessible
to other instances of the application. However, with the Application object
in ASP/ASP.NET, all users of an application have access to the same Application
object. Therefore, if you have something that will be the same for all
users, you can store it in the Application object. For example, if you
visit BarnesandNoble.com, you will access the database of books. All users
are connecting to the same database, and are likely using the same connection
information as far as the name of the server and the login credentials.
Therefore, it is not unusual to store the database connection string in
the Application object, because it is the same for everyone. (Note: This
is not saying that the database connection is made in the Application
object, just that the connection string is stored there.)
ASP.NET In-Process State Maintenance
ASP.NET has three different ways to store session state, and the default
method works just like session state in ASP. When the first user visits
an ASPX page, the Application object is created. In addition, a Session
object is created. While the Application object is accessible to all users
of this Web application, the Session object is specific to a particular
user. When the next user comes in requesting a page, a new Session object
is created for that user (but they use the same Application object).
Because each Session object is tied to an individual user, you can place
user-specific variables in the Session object. For example, storing the
user's name at the Application object wouldn't make sense, but storing
it at the Session object makes perfect sense. Because each user gets his
or her own Session object, you can store their name, what they have ordered,
and so forth.
Given that HTTP is a stateless protocol, it is important to understand
how subsequent requests from the same user end up referencing the same
Session object. When IIS creates a new Session object, it generates a
unique number for it, called a SessionID. This SessionID is a property
of the Session object, and the value is passed back to the client using
the cookie mechanism. The cookie is never written to disk on the client;
instead it is stored only in memory. With each subsequent request, however,
the SessionID is passed from the client back to the server. IIS sees the
SessionID that comes with the request, and sends that request back to
the Session object with the same SessionID.
In an ASP/ASP.NET application, there is only one Application object.
However, there is a Session object for each user. If you have a Web site
with a lot of traffic, there can be many, many Session objects in memory.
While the Application object never shuts down, however, the Session objects
do shut down after a certain period of inactivity, the default of which
is 20 minutes. The Session objects automatically start the timer as soon
as they send a page to the client. If the client does not make a new request
within the specified timeframe, the Session object will remove itself
from memory. Any values in the Session object will be lost, although an
event does fire before the Session object is destroyed, and you can use
this event to save values if necessary.
While the default method for handling Session state works fine, it is
not suitable for every case. There are three major issues with the default
ASP.NET session state, and these are the same problems with ASP session
state:
- By default, the Session object runs in the same process as IIS itself.
If IIS is shut down (or the machine crashes), you lose all state in
all sessions.
- In-Process session state is machine specific. If you are using a Web
farm, you are doing so for scalability reasons. However, the Session
object is only created on one machine within the farm. Subsequent user
requests might get routed to a different server in order to help balance
the load. If the user is directed to a different server, their Session
object is not there, and no session state is available. To get around
this, Web farms can force users to be routed to the same server with
each request, but this can destroy some of the scalability you had hoped
to gain by moving to a Web farm in the first place.
- The default implementation of session state requires cookies on the
client. Even though these cookies are memory-only, some clients disable
cookies. This destroys the ability to maintain state with Session objects,
because the SessionID cannot be stored on the client. This means that
each request is seen as a new user, and a new Session object is created
on the server with each new request.
ASP.NET addresses each of these problems with new ways of working with
session state.
Out-Of-Process with a Windows Service
With ASP.NET, you have two ways of storing state outside of the actual
IIS process. You can store state in a Windows service or SQL Server, both
of which run outside of IIS. Both have advantages and disadvantages.
Using the Windows service requires you to start the service. The service
is in the file aspnet_state.exe, which is found in this path:
C:\Windows\Microsoft.NET\Framework\<version number>
You can start the service by typing:
net start aspnet_state
This will start the service, which will run in a separate process from
IIS. Next, you simply configure your application to use the service (described
in just a moment), and no coding changes are required to your application.
The advantage of using the Windows service is not just that the service
runs out of process to IIS, but that a service running on a single machine
can be used by multiple Web servers. This means that you gain the ability
to use a Web farm but maintain state using the Session object. A request
to any machine in the farm is routed to the machine running the service,
and the state information is retrieved.
Out-Of-Process with SQL Server
If you are looking for the highest possible reliability, it is possible
to store your session data in SQL Server. SQL Server is robust, thanks
to the transaction log and two-phase commits. In addition, it is not uncommon
to use SQL Server in a clustered environment, meaning that even if one
computer failed, the other computers continue serving up SQL Server. With
a Web farm and clustered SQL Servers, you would have the highest possible
reliability for your Web application and state management.
In order to use SQL Server, you first need to run a script to create
a SQL Server database. A file named InstallSqlState.sql is located in
the following directory:
C:\Windows\Microsoft.NET\Framework\<version number>
Running this SQL file against SQL Server will create a new database called
ASPState. However, no tables are created in this database; instead, only
stored procedures are created. These stored procedures are used to create
two tables in the tempdb database. Each session gets added as a row in
a table called ASPStateTempSessions. The records have an Expires field
and are cleared out periodically when IIS calls a stored procedure to
remove expired sessions.
Configuring the Application
In order to configure your application to use one of the three methods
of state management, you modify settings in the Web.config file. This
is an XML file that is stored in the root directory of your Web application.
There are a number of sections to the Web.config file, but you are interested
in the <sessionState /> section. By default, the section looks like
this:
<sessionState
mode="InProc"
stateConnectionString="tcpip=127.0.0.1:42424"
sqlConnectionString=
"data source=127.0.0.1;user id=sa;password="
cookieless="false"
timeout="20"
/>
In this section, the mode is the first thing you see. The default mode
is InProc, which means that the Session object
runs in the same process as IIS, just as it did in ASP. With InProc
set, both stateConnectionString and sqlConnectionString
are ignored. The cookieless and timeout
values will be discussed in a moment, but are applicable to any of the
three modes. Also note that these values are case-sensitive, and may change
casing before Visual Studio .NET is released.
In order to use a Windows service to store session state, change the
mode to StateServer. Once you specify that you
are using a Windows service, the stateConnectionString
is used to point to the server hosting the Windows service. By default,
the IP address is 127.0.0.1, which is just the current machine. You can
change this, but leave the port number at 42424, the port on which the
service listens. By changing the IP address, you could point any number
of instances of this application, running on multiple servers, to the
same instance of the service.
In order to use SQL Server to store session state, change the mode to
SQLServer. Now, the stateConnectionString
is ignored again, but the sqlConnectionString
is used. Again, the default is the local machine, with the infamous "sa/no
password" setting for the user credentials. You can change this connection
string, but note that it does not ask for an OLE DB provider or a database
name. This is because it assumes SQL Server, and a database named ASPState.
All this string needs is the server name and the login credentials.
Cookieless Session State
While the three modes all store session information differently on the
server, the SessionID is still passed to the client using the cookie mechanism.
However, not all clients support cookies, either due to security concerns
or because of a less-capable browser. Regardless of the reason, it is
possible to pass the SessionID to the client without using cookies. You
can set the cookieless attribute to true
in order to pass the SessionID without cookies.
Setting cookieless to true
causes IIS to pass the SessionID in the URL. Instead of passing the SessionID
to the client using cookies, the SessionID is placed into the URL of the
page, as well as any relative links on that page. For example, calling
a page named WebForm1.aspx when using cookies would return this as the
URL:
http://localhost/StateTest/WebForm1.aspx
Setting cookieless to true would show this URL in the browser (your SessionID
will vary):
http://localhost/StateTest/(wpnf2emmfafsbc3abdktzp45)/WebForm1.aspx
Changing the Timeout Period
If you want to change the default timeout of 20 minutes, simply change
the timeout value in the <sessionState />
section of Web.config. This allows you to modify the timeout, in minutes,
using an Integer value. The timeout must be greater than zero, but can
be up to 2,147,483,647, if you need a session that lasts over 4,000 years.
A Quick Example
In order to see how the state management works, you'll create a simple
ASPX page. Open Visual Studio .NET and create a new ASP.NET Web Application.
On the WebForm, add a button and a label. Then, go to the code view and
add the code shown in Listing 1 (for VB .NET) or Listing 2 (for
C#), both shown below. This is all you will need to do as far as coding.
Listing 1
(for VB NET)
Listing 2
(for C#)
Run the application. Once the page is shown in the browser, clicking
the button will increment the label by 1 each time you click. This is
because in the code, you are creating a session variable called Name the
first time this user calls the page. The IsPostBack property is checked
to prevent the user from running the code each time to set the value back
to zero.
Now, because you created this with the default session state method,
go and stop the IIS Admin service using the Services applet in Windows
2000/XP. Stopping this service will require other services to be stopped,
most notably the World Wide Web Publishing service. Once all the necessary
services have stopped, this means that IIS has been shut down, and the
Session object created for you is gone. Don't click the button yet; instead,
restart the IIS Admin service and the World Wide Web Publishing service.
Now, click the button again, and you will start back at one. This is because
the session was destroyed, and on your next request, you created a new
session, and your counter started back at zero.
Close the browser and return to the project. In the Web.config file,
change the mode to StateServer. Start the Windows
service using the command mentioned earlier, and run the same test: Start
the project, click the button a few times, and then stop the IIS Admin
service. After restarting the IIS Admin and the World Wide Web Publishing
services, you can click the button and the count will resume where you
left off; state was not lost due to stopping IIS, because state was being
stored outside of IIS. You can test the same scenario using SQL Server
if you prefer.
Summary
Many developers were loathe to use ASP's built-in Session object, because
it was not scalable in a Web farm scenario. ASP.NET addresses the ASP
session state shortcomings by allowing you to store the state out of process
in a Windows service or in SQL Server. You can also easily turn off the
need for session state to use cookies on the client. These new features
reduce some of the major arguments against using built-in state management.
However, none of these new features addresses the concern that the built-in
state management is slow, but that is the subject for another article.
About The Author
Craig Utley is President of CIO Briefings LLC, a consulting and training
firm focused on helping customers develop enterprise-wide solutions with
Microsoft technologies. Craig has been using Visual Basic since version
1.0, and he has guided customers through the creation of highly scalable
web applications using Active Server Pages, Visual Basic, MTS/Component
Services, and SQL Server. Craig's skills in analyzing and designing enterprise-wide
solutions have been used by large corporations and start-up companies
alike. |