Working with Session State


A user's session is the time period from when he makes his first request in an application until he leaves the site. A user's session ends when the web application has not received a request from that user within a specified timeout period.

Session state allows the web application to store and retrieve information that pertains to the given user and her session. This information often includes things such as the user's list of security privileges, shopping cart, wish list, or temporary data used for specific pages, such as the most recently browsed products or the last set of search criteria used.

Sessions are identified with a unique identifier that is generated for each session. In most cases, the user's session ID is stored in a temporary cookie in the browser's memory. When the browser makes a request for a page, it sends the collection of cookies that apply for the site, including the user's session ID. This allows the server to locate all of the data stored for a given user based on the user's session ID.

If the user has disabled cookies in her browser, you can use a special technique that rewrites website URLs so that each URL contains the user's session ID. This allows the server to access the user's session state without reading it from the collection of cookies transmitted by the browser.

This section covers the various ways in which you can maintain session state within an ASP.NET application, such as the in-process state provider and out-of-process state providers like the ASP.NET State Server and SQL Server. You will also learn how to handle events specific to session state within your ASP.NET application.

Using the Default In-Process State Provider

Because all session state providers expose the same functionality, the use of each provider will appear to be very similar. However, there are some major differences among the providers that can cause potential problems if you aren't aware of them.

The default session state provider can handle pretty much anything you want to store in a session, within reason. The more data you store in the session state, the larger the memory footprint of your application.

Managing Session Variables

When deciding on what you want to store in session state and what you want to store in a relational back end such as SQL Server or Oracle, you should consider a few things. First, consider the size of the data being stored. Is it fairly small? Secondly, consider that you can have as many open sessions as you have simultaneous users. In fact, you can have more than that if you have a long session timeout period. So, for every piece of data you plan on storing in session, multiply the size of that data by the maximum number of users you expect to have visiting your site at any given time, plus 5%. This will give you a worst-case-scenario memory cost for storing that item in the session state. If you absolutely must store that data in the session, and that data is extremely costly memory-wise, consider using SQL Server as your session state provider because it is designed to handle larger data sizes.


All session state is exposed through an instance of the System.Web.SessionState.HttpSessionState class in the Session property of the Page class. Table 23.1 contains the list of properties available for the HttpSessionState class, and Table 23.2 contains the list of methods available for that class.

Table 23.1. Commonly Used HttpSessionState Properties

Property

Description

CookieMode

Indicates whether the session state is currently configured for cookieless (URL rewriting) operation or normal operation. Values are : AutoDetect (detect if browser supports cookies; if not, use URL rewriting), UseCookies (always use cookies), UseDeviceProfile (checks browser capabilities to determine use of cookies), and UseUri (always use URL rewriting).

Count

Returns the number of data items stored in the session. Note that this count, and all other data, is for the current user session.

IsCookieless

Indicates if the session ID is stored in the URL.

IsNewSession

Indicates that the session was created on the current request. Very handy for detecting a user's first page hit on a site.

IsReadOnly

Indicates whether the session is read only.

IsSynchronized

Indicates whether access to session variables is thread-safe (synchronized).

Item

Get or set individual session items. C# allows the use of the indexer ([]) notation instead of this property.

Keys

The keys of all items stored in session state.

LCID

Gets or sets the locale of the current session.

Mode

Gets the current state mode (provider): Custom, InProc, Off, SqlServer, or StateServer.

SessionID

Returns the unique ID string of the current session.

StaticObjects

Gets a list of all objects declared as static by <object> tags within Global.asax, scoped as session scope.

SyncRoot

Object against which thread-safe locks can be made to synchronize access to data items.

Timeout

Indicates the amount of time (in minutes) that can elapse between requests for pages without expiring the user's session. If the timeout period elapses without a page request by the same user, the user's session expires.


Table 23.2. HttpSessionState Methods

Method

Description

Abandon

Abandons the current session, removing all data items from the user's session. The next page request made by this user will create a new session.

Add

Adds a new data item to session state.

Clear

Removes all data items from session state.

Remove

Removes a data item from session state.

RemoveAll

Removes all items from session state.

RemoveAt

Removes the data item from the indicated index.


To get started working with session state, create a new website using the menu option File, New, Web Site. This will create an empty website with a single page: default.aspx. By default, in-process session state is enabled. Drop two labels onto the default.aspx page called lblStatus and lblCounter, separated by <br> tags. Finally, drop a button onto the page called btnSubmit with the text "Click Here". Double-click the button to rig up an empty event handler, and then set the C# code portion of the page to match the code contained in Listing 23.2.

Listing 23.2. Session State Demo

using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) {     lblStatus.Text = string.Format("This is a(n) {0} session!",         Session.IsNewSession ? "New" : "Old");     if (Session.IsNewSession)         Session["counter"] = 0;     UpdateCounterDisplay(); } protected void UpdateCounterDisplay() {     lblCounter.Text = string.Format("Counter value is <b>{0}</b>", Session["counter"]); } protected void btnSubmit_Click(object sender, EventArgs e) {     Session["counter"] = (int)Session["counter"] + 1;     UpdateCounterDisplay(); } } 

This page basically displays the value of a session counter, along with some text that indicates whether the session was created during that request or not (using the IsNewSession property). The first time you run this application (just press F5 in Visual Studio and choose Yes to have a debug-enabled Web.config file created for you), you will see the page shown in Figure 23.1.

Figure 23.1. First run of a session state demo.


After clicking the Click Here button a few times to increment the session counter, not only can you see the session counter being incremented, but Figure 23.2 also shows that the session is no longer a new session.

Figure 23.2. Subsequent run of a session state demo.


You can add the following lines of code to the preceding sample to display the name and value of every piece of data stored in the session:

Response.Write("<hr/>"); foreach (string key in Session.Keys) {     Response.Write(string.Format("<B>{0}</B>: {1}<br/>", key, Session[key])); } Response.Write("<hr/>"); 


Working with the default, in-process session state provider is fairly easy. No initial setup is required, you don't have to change the Web.config file to support it, and you don't have any firm restrictions on the kinds of objects that can be placed in session state. The next two providers discussed both require manipulation of the Web.config file and have requirements on the types of objects that can be stored and retrieved.

Tip

Keep in mind that the Session object's keys are all strings. This also means that the keys are case sensitive. In an application that has dozens of session keys and data items, it can quickly become a maintenance nightmare for large teams to remember which keys are being used for which variables. In such a situation, it is often helpful to create a wrapper around the Session object that takes an enumeration as parameters. Instead of accessing the Session via code like Session["myVariable"], you could access it via SessionWrapper[SessionKeys.MyVariable]. This way, anyone on the team who needs a new session key must either reuse an existing key or add one to the SessionKeys enumeration.


Using the ASP.NET State Server Provider

The State Server is a Windows service provided by ASP.NET 2.0 that is installed whenever ASP.NET is installed on a machine. By default, this service is off. As mentioned earlier, your code will work directly with the HttpSessionState class instance stored in the Session property of the Page class regardless of which state provider you select.

The reason for using an out-of-process state server is simple: web farms. In a web farm, more than one server is used to host multiple copies of the same web application that act as one. These web applications need to be able to share the same session state, because a user could make requests of multiple servers throughout their session, and the session state needs to remain consistent between those requests. The ASP.NET State Server is a lightweight out-of-process server. SQL Server can be used as a "heavyweight" out-of-process state server.

The ASP.NET State Server must be turned on and configured before you can use it. To turn it on, open the Services control panel, right-click it, and choose Start. If you are going to want it on all the time for your application, you will need to make sure that the service is always on (by default it is set to Manual). Figure 23.3 shows the Services control panel with the State Server service highlighted.

Figure 23.3. The State Server service.


To use the State Server service, you'll need to modify the Web.config of your website to indicate the use of the State Server in the <sessionState> element, as shown in Listing 23.3.

Listing 23.3. A Web.config File Modified to Work with the State Server Service

<?xml version="1.0"?> <configuration>   <appSettings/>   <connectionStrings/>   <system.web>      <compilation debug="true"/>     <sessionState mode="StateServer"          stateConnectionString="tcpip=localhost:42424" />     <authentication mode="Windows"/>   </system.web> </configuration> 

The stateConnectionString attribute is the most crucial part of the configuration. This string takes the format tcpip=address:port. You can use localhost or 127.0.0.1 if the state server is local to the web application, or you can specify a host name or IP address for a remote server. The state server always listens on the same port, but you can put the state server on any machine you like.

After you have modified the Web.config file and you have turned on the State Server service, you're ready to go. There is one caveat, however: You cannot store and retrieve nonserializable objects or MarshalByRefObjects. Any attempt to store an object that inherits from MarshalByRefObject or that is not marked as serializable will result in the following error message:

   Unable to serialize the session state. In 'StateServer' and 'SQLServer' mode,    ASP.NET will serialize the session state objects, and as a result non-serializable    objects or MarshalByRef objects are not permitted. The same restriction applies if    similar serialization is done by the custom session state store in 'Custom' mode. 


The reason that ASP.NET serializes session state objects for out-of-process servers is their out-of-process nature. If ASP.NET attempted to store an actual object reference, that reference would be tied directly to the one server that tried to store the reference. Put simply: the complexity of the Remoting code that would be required to get object references to be stored in an out-of-process state server make it nearly impossible to accomplish. As such, ASP.NET serializes all objects being stored in out-of-process state servers.

Tip

Not only do you need to make sure that you aren't using a MarshalByRefObject class, but you need to make sure that your class is marked as Serializable. One common problem that developers run into, even though their class is marked Serializable, is in state consistency. If "side-effect" code takes place in a set or a get accessor, there is a chance that the serialization code in ASP.NET might not give you back the same object you stored. To avoid these pitfalls, developers often make a rule such that only structs can be stored in session state, with only public fields. Excessive use of private fields, side-effect code (code inside accessors), or even parameterized constructors can lead to serialization inconsistency.


To try out the State Server session provider, you can basically follow the same steps you followed in the preceding sample: create a website, drop two labels onto the form (lblStatus and lblCounter), and drop a button on the form named btnSubmit. Double-click the button to wire up the event handler.

Before you do anything else, add a struct to your project (you might be prompted to create the App_Code folder) called Customer:

using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; /// <summary> /// Really good guideline for out-of-proc state: /// 1. structs only /// 2. lightweight /// 3. must be serializable /// 4. use only public fields, no accessors /// 5. no private members /// 6. constructors cannot create a state that is unachievable ///    through simple property/field setting (serialization/deserialization) /// </summary> [Serializable()] public struct Customer {     public string FirstName;     public string LastName;     public Customer(string last, string first)     {         FirstName = first;         LastName = last;     } } 


Now you can set the C# (default.aspx.cs) to the code shown in Listing 23.4.

Listing 23.4. Using a Serializable Struct In an Out-of-Process Session State Server

using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) {     if (Session.IsNewSession)     {         lblStatus.Text = "This session is <b>New</b>.";         Session["counter"] = 0;         Session["customer"] = new Customer("Hoffman", "Kevin");     }     else     {         lblStatus.Text = string.Format(             "This session is <b>Old</b>. Stored Customer is {0}",             ((Customer)Session["customer"]).LastName + ", " +             ((Customer)Session["customer"]).FirstName);     }     UpdateCounterLabel(); } private void UpdateCounterLabel() {     lblCounter.Text = string.Format(         "Counter value is <b>{0}</b>.",         Session["counter"]); } protected void btnSubmit_Click(object sender, EventArgs e) {     Session["counter"] = (int)Session["counter"] + 1;     UpdateCounterLabel(); } } 

As long as you remember the fact that the ASP.NET State Server service is designed for lightweight (small amounts of data), out-of-process (consistently serializable) session state management, you will be able to take full advantage of this provider.

Using the SQL Server Session State Provider

The SQL Server session state provider is designed for more robust out-of-process session state management. This means that SQL Server can handle a larger number of concurrent user sessions than the ASP.NET State Server. It can also handle larger amounts of data per session than the ASP.NET State Server (though that too is not without limit).

There are two steps to getting the SQL Server session state provider to work. First, you need to create the state management database within your SQL Server instance. Second, you need to modify Web.config to point to that SQL Server instance.

To install the appropriate schema in SQL Server, you can run the InstallSqlState.sql script on the server on which the SQL Server instance resides. Do not attempt to run this script on your web server (unless that is also your SQL Server). You can find this script in %SystemRoot%\Microsoft.NET\Framework\version. For example, on the author's machine, the file was in

C:\Windows\Microsoft.NET\Framework\v2.0.50727 


To run this script, you should not execute it from within SQL Server 2005 Management Studio. Instead, you should run the script using the aspnet_regsql command-line tool. To use this tool, open a Visual Studio 2005 Command Prompt window. To get the full help text, type aspnet_regsql -?. You can also use this tool to add support for other providers in SQL Server such as roles, membership, profiles, personalization, and so on.

You use the sstype option to indicate the type of session state support to add to the SQL Server database:

  • Temporary The session state will be temporary and stored in the tempdb database. This is an ideal option for most situations because tempdb cleans itself up and you don't have to worry about maintenance. If you don't need to be able to restore session state if your SQL Server crashes, this is the option you should use.

  • Persistent The session state will be stored in a SQL database called ASPState. The stored procedures for maintaining session state will also be stored in this database.

  • Custom The session state data and corresponding stored procedures will both be placed in a database named on the command line using the -d option.

When you create the session state database at the command line, you also need to specify the SQL connection information, as shown in the following line of code:

aspnet_regsql -S localhost -ssadd -sstype p -E 


The -E option uses a trusted connection to SQL Server using the current Windows credentials. To specify an alternate username and password, you can use the -U and -P options. Figure 23.4 shows the SQL Server 2005 Management Studio application examining the contents of the newly created ASPState database.

Figure 23.4. The ASPState database.


With the session state management database properly configured in your SQL Server (you can do this in SQL Express as well, provided you give .\SQLExpress for the server name for the local SQL Express instance), you can then start using SQL Server for your session state store.

Because the code in Listing 23.4 is designed to work using serializable structures with an out-of-process session state store, it is ideal to use as a sample to test SQL Server session state management.

Take the code from that sample and modify the Web.config file to contain a new <sessionState> element as shown in the following example:

<?xml version="1.0"?> <configuration>     <appSettings/>     <connectionStrings/>     <system.web>     <sessionState mode="SQLServer"       sqlConnectionString="data source=localhost; Integrated Security=SSPI;" />         <compilation debug="true"/>         <authentication mode="Windows"/>     </system.web> </configuration> 


Note that the connection string for the SQL Server session state store doesn't contain the database name. You never supply the database name unless you have used the Custom Database option from the aspnet_regsql command-line tool. This allows your application to use temporary and persistent session state management interchangeably without having to modify your Web.config file.

Handling Session State Events

If your application has a global.asax file (you can add this by choosing to add a new item, and selecting Global Application Class from the template list), you can write code that responds to several events specific to session state.

Your code can respond to events triggered when a user's session is first created and when a user's session is terminated. A session is created when a user makes a request for a page in your application and has no current session ID. A session is terminated when a specified time period has elapsed since the user's last request for any page within the application.

In the global.asax file there are two methods specific to session state events: Session_Start and Session_End. The start event is extremely useful for initializing data to make it available to the rest of the application, such as counters and other data. The end event is used to clean up data related to the user's session. You don't need to use the end event to manually remove items from the user's session because those will be automatically disposed, but you can use this event to perform other operations such as database queries that need to be done when a user is no longer connected to a website.

To illustrate these two events, you can create a sample application that maintains a list of all connected users at the application level and a counter at the session level.

Session_End

Keep in mind that the Session_End event is only raised when the session state mode is set to InProc. This event is meaningless and will never be fired in any out-of-process mode such as SQL Server, State Server, or your own custom state provider implementation.

The code for such a global.asax file is contained in Listing 23.5.

Listing 23.5. Event Handling in global.asax

<%@ Application Language="C#" %> <script runat="server"> void Application_Start(object sender, EventArgs e) { // Code that runs on application startup } void Application_End(object sender, EventArgs e) { //  Code that runs on application shutdown } void Application_Error(object sender, EventArgs e) { // Code that runs when an unhandled error occurs } void Session_Start(object sender, EventArgs e) {   System.Collections.Generic.List<string> users = null;   if (Application["users"] == null)   {     users = new System.Collections.Generic.List<string>();     Application["users"] = users;   }   else     users =       (System.Collections.Generic.List<string>)Application["users"];  users.Add(Context.User.Identity.Name.Substring(  Context.User.Identity.Name.IndexOf('\\')+1,  Context.User.Identity.Name.Length -  Context.User.Identity.Name.IndexOf('\\')-1));  Session["counter"] = 0; } void Session_End(object sender, EventArgs e) {  System.Collections.Generic.List<string> users =    (System.Collections.Generic.List<string>)Application["users"];  users.Remove(User.Identity.Name); } </script> 

The code in the preceding listing adds the user's name (with the domain name stripped off) to an application-wide List<string> class when a session starts and removes that same name from the list when the user's session ends. This effectively creates a "who's online" list that can then be rendered by any page. A sample of this is included with the chapter's code, but you could try to create a "who's online" page on your own as an exercise.



Microsoft Visual C# 2005 Unleashed
Microsoft Visual C# 2005 Unleashed
ISBN: 0672327767
EAN: 2147483647
Year: 2004
Pages: 298

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net