
Pro CSharp 2008 And The .NET 3.5 Platform [eng]
.pdf
1262 CHAPTER 33 ■ ASP.NET STATE MANAGEMENT TECHNIQUES
Figure 33-6. The cache application GUI
In the Click event handler of the Add This Car button, insert the new record into the AutoLot database using the InventoryDAL type. Once the record has been inserted, call a helper function named RefreshGrid(), which will update the UI:
protected void btnAddCar_Click(object sender, EventArgs e)
{
//Update the Inventory table
//and call RefreshGrid().
InventoryDAL dal = new InventoryDAL(); dal.OpenConnection(@"Data Source=(local)\SQLEXPRESS;" +
"Initial Catalog=AutoLot;Integrated Security=True"); dal.InsertAuto(int.Parse(txtCarID.Text), txtCarColor.Text,
txtCarMake.Text, txtCarPetName.Text); dal.CloseConnection();
RefreshGrid();
}
private void RefreshGrid()
{
InventoryDAL dal = new InventoryDAL(); dal.OpenConnection(@"Data Source=(local)\SQLEXPRESS;" +
"Initial Catalog=AutoLot;Integrated Security=True");

CHAPTER 33 ■ ASP.NET STATE MANAGEMENT TECHNIQUES |
1263 |
DataTable theCars = dal.GetAllInventory(); dal.CloseConnection();
carsGridView.DataSource = theCars; carsGridView.DataBind();
}
Now, to test the use of the cache, begin by running the current program (Ctrl+F5) and copy the URL appearing in the browser to your clipboard. Next, launch a second instance of Internet Explorer (using the Start button) and paste the URL into this instance. At this point you should have two instances of your web browser, both viewing Default.aspx and showing identical data.
In one instance of the browser, add a new automobile entry. Obviously, this results in an updated GridView viewable from the browser that initiated the postback.
In the second browser instance, click the Refresh button (F5). You should not see the new item, given that the Page_Load event handler is reading directly from the cache. (If you did see the value, the 15 seconds had already expired. Either type faster or increase the amount of time the DataTable will remain in the cache.) Wait a few seconds and click the Refresh button from the second browser instance one more time. Now you should see the new item, given that the DataTable in the cache has expired and the CacheItemRemovedCallback delegate target method has automatically updated the cached DataTable.
As you can see, the major benefit of the Cache type is that you can ensure that when a member is removed, you have a chance to respond. In this example, you certainly could avoid using the Cache and simply have the Page_Load() event handler always read directly from the AutoLot database. Nevertheless, the point should be clear: the cache allows you to automatically refresh data using the cache mechanism.
■Source Code The CacheState project is included under the Chapter 33 subdirectory.
Maintaining Session Data
So much for our examination of application-level and cached data. Next, let’s check out the role of per-user data. As mentioned earlier, a session is little more than a given user’s interaction with a web application, which is represented via a unique HttpSessionState object. To maintain stateful information for a particular user, the HttpApplication-derived type and any System.Web.UI.Page-derived types may access the Session property. The classic example of the need to maintain per-user data would be an online shopping cart. Again, if 10 people all log on to an online store, each individual will maintain a unique set of items that she (may) intend to purchase.
When a new user logs on to your web application, the .NET runtime will automatically assign the user a unique session ID, which is used to identify the user in question. Each session ID is assigned a custom instance of the HttpSessionState type to hold on to user-specific data. Inserting or retrieving session data is syntactically identical to manipulating application data, for example:
// Add/retrieve a session variable for current user.
Session["DesiredCarColor"] = "Green";
string color = (string) Session["DesiredCarColor"];
The HttpApplication-derived type allows you to intercept the beginning and end of a session via the Session_Start() and Session_End() event handlers. Within Session_Start(), you can freely create any per-user data items, while Session_End() allows you to perform any work you may need to do when the user’s session has terminated:

1264 CHAPTER 33 ■ ASP.NET STATE MANAGEMENT TECHNIQUES
<%@ Application Language="C#" %>
...
void Session_Start(Object sender, EventArgs e)
{
// New session! Prep if required.
}
void Session_End(Object sender, EventArgs e)
{
// User logged off/timed out. Tear down if needed.
}
Like the HttpApplicationState type, HttpSessionState may hold any System.Object-derived type, including your custom classes. For example, assume you have a new web application (SessionState) that defines a class named UserShoppingCart:
public class UserShoppingCart
{
public string desiredCar; public string desiredCarColor; public float downPayment; public bool isLeasing;
public DateTime dateOfPickUp;
public override string ToString()
{
return string.Format
("Car: {0}<br>Color: {1}<br>$ Down: {2}<br>Lease: {3}<br>Pick-up Date: {4}", desiredCar, desiredCarColor, downPayment, isLeasing, dateOfPickUp.ToShortDateString());
}
}
Within the Session_Start() event handler, you can now assign each user a new instance of the
UserShoppingCart class:
void Session_Start(Object sender, EventArgs e)
{
Session["UserShoppingCartInfo"] = new UserShoppingCart();
}
As the user traverses your web pages, you are able to pluck out the UserShoppingCart instance and fill the fields with user-specific data. For example, assume you have a simple *.aspx page that defines a set of input widgets that correspond to each field of the UserShoppingCart type and a Button used to set the values and two Labels that will be used to display the user’s session ID and session information (see Figure 33-7).

CHAPTER 33 ■ ASP.NET STATE MANAGEMENT TECHNIQUES |
1265 |
Figure 33-7. The session application GUI
The server-side Click event handler for the Button type is straightforward (scrape out values from TextBoxes and display the shopping cart data on a Label type):
protected void btnSubmit_Click(object sender, EventArgs e)
{
// Set current user prefs.
UserShoppingCart cart = (UserShoppingCart)Session["UserShoppingCartInfo"];
cart.dateOfPickUp = myCalendar.SelectedDate; cart.desiredCar = txtCarMake.Text; cart.desiredCarColor = txtCarColor.Text; cart.downPayment = float.Parse(txtDownPayment.Text); cart.isLeasing = chkIsLeasing.Checked; lblUserInfo.Text = cart.ToString(); Session["UserShoppingCartInfo"] = cart;
}
Within Session_End(), you may wish to persist the fields of the UserShoppingCart to a database or whatnot (however, as you will see at the conclusion of this chapter, the ASP.NET Profile API will do so automatically). As well, you may wish to implement Session_Error() to trap any faulty input (or perhaps make use of various validation controls on the Default.aspx page to account for such user errors).


CHAPTER 33 ■ ASP.NET STATE MANAGEMENT TECHNIQUES |
1267 |
Understanding Cookies
The next state management technique examined here is the act of persisting data within a cookie, which is often realized as a text file (or set of files) on the user’s machine. When a user logs on to a given site, the browser checks to see whether the user’s machine has a cookie file for the URL in question and, if so, appends this data to the HTTP request.
The receiving server-side web page could then read the cookie data to create a GUI that may be based on the current user preferences. I’m sure you’ve noticed that when you visit one of your favorite websites, it somehow “just knows” the sort of content you wish to see. The reason (in part) may have to do with a cookie stored on your computer that contains information relevant to a given website.
■Note The exact location of your cookie files will depend on which browser and operating system you happen to be using.
The contents of a given cookie file will obviously vary among URLs, but keep in mind that they are ultimately text files. Thus, cookies are a horrible choice when you wish to maintain sensitive information about the current user (such as a credit card number, password, or whatnot). Even if you take the time to encrypt the data, a crafty hacker could decrypt the value and use it for purely evil pursuits. In any case, cookies do play a role in the development of web applications, so let’s check out how ASP.NET handles this particular state management technique.
Creating Cookies
First of all, understand that ASP.NET cookies can be configured to be either persistent or temporary. A persistent cookie is typically regarded as the classic definition of cookie data, in that the set of name/value pairs is physically saved to the user’s hard drive. A temporary cookie (also termed a session cookie) contains the same data as a persistent cookie, but the name/value pairs are never saved to the user’s hard drive; rather, they exist only within the HTTP header. Once the user shuts down the browser, all data contained within the session cookie is destroyed.
■Note Most browsers support cookies of up to 4,096 bytes. Because of this size limit, cookies are best used to store small amounts of data, such as a user ID that can be used to identify the user and pull details from a database.
The System.Web.HttpCookie type is the class that represents the server side of the cookie data (persistent or temporary). When you wish to create a new cookie, you access the Response.Cookies property. Once the new HttpCookie is inserted into the internal collection, the name/value pairs flow back to the browser within the HTTP header.
To check out cookie behavior firsthand, create a new ASP.NET web application (CookieStateApp) and create the UI displayed in Figure 33-8.

1268 CHAPTER 33 ■ ASP.NET STATE MANAGEMENT TECHNIQUES
Figure 33-8. The UI of CookieStateApp
Within the first button’s Click event handler, build a new HttpCookie and insert it into the Cookie collection exposed from the HttpRequest.Cookies property. Be very aware that the data will not persist itself to the user’s hard drive unless you explicitly set an expiration date using the HttpCookie.Expires property. Thus, the following implementation will create a temporary cookie that is destroyed when the user shuts down the browser:
protected void btnCookie_Click(object sender, EventArgs e)
{
// Make a temp cookie.
HttpCookie theCookie =
new HttpCookie(txtCookieName.Text, txtCookieValue.Text);
Response.Cookies.Add(theCookie);
}
However, the following generates a persistent cookie that will expire on March 24, 2009:
protected void btnCookie_Click(object sender, EventArgs e)
{
HttpCookie theCookie =
new HttpCookie(txtCookieName.Text, txtCookieValue.Text);
theCookie.Expires = DateTime.Parse("03/24/2009");
Response.Cookies.Add(theCookie);
}
Reading Incoming Cookie Data
Recall that the browser is the entity in charge of accessing persisted cookies when navigating to a previously visited page. To interact with the incoming cookie data under ASP.NET, access the HttpRequest.Cookies property. To illustrate, implement the Click event handler for the second button as so:

CHAPTER 33 ■ ASP.NET STATE MANAGEMENT TECHNIQUES |
1269 |
protected void btnShowCookie_Click(object sender, EventArgs e)
{
string cookieData = "";
foreach (string s in Request.Cookies)
{
cookieData +=
string.Format("<li><b>Name</b>: {0}, <b>Value</b>: {1}</li>", s, Request.Cookies[s].Value);
}
lblCookieData.Text = cookieData;
}
If you now run the application and click your new button, you will find that the cookie data has indeed been sent by your browser (see Figure 33-9).
Figure 33-9. Viewing cookie data
■Source Code The CookieStateApp project is included under the Chapter 33 subdirectory.
The Role of the <sessionState> Element
At this point in the chapter, you have examined numerous ways to remember information about your users. As you have seen, view state and application, cache, session, and cookie data are manipulated in more or less the same way (via a class indexer). As you have also seen, the HttpApplication type is often used to intercept and respond to events that occur during your web application’s lifetime.
By default, ASP.NET will store session state using an in-process *.dll hosted by the ASP.NET worker process (aspnet_wp.exe). Like any *.dll, the plus side is that access to the information is as

1270 CHAPTER 33 ■ ASP.NET STATE MANAGEMENT TECHNIQUES
fast as possible. However, the downside is that if this AppDomain crashes (for whatever reason), all of the user’s state data is destroyed. Furthermore, when you store state data as an in-process *.dll, you cannot interact with a networked web farm. This default behavior is recorded in the
<sessionState> element of your machine.config file like so:
<sessionState
mode="InProc"
stateConnectionString="tcpip=127.0.0.1:42626" sqlConnectionString="data source=127.0.0.1;Trusted_Connection=yes" cookieless="false"
timeout="20"
/>
This default mode of storage works just fine if your web application is hosted by a single web server. As you might guess, however, this model is not ideal for a farm of web servers, given that session state is “trapped” within a given AppDomain.
Storing Session Data in the ASP.NET Session State Server
Under ASP.NET, you can instruct the runtime to host the session state *.dll in a surrogate process named the ASP.NET session state server (aspnet_state.exe). When you do so, you are able to offload the *.dll from aspnet_wp.exe into a unique *.exe, which can be located on any machine within the web farm. Even if you intend to run the aspnet_state.exe process on the same machine as the web server, you do gain the benefit of partitioning the state data in a unique process (as it is more durable).
To make use of the session state server, the first step is to start the aspnet_state.exe Windows service on the target machine. At the command line, simply type
net start aspnet_state
Alternatively, you can start aspnet_state.exe using the Services applet accessed from the Administrative Tools folder of the Control Panel, as shown in Figure 33-10.
The key benefit of this approach is that you can configure aspnet_state.exe to start automatically when the machine boots up using the Properties window. In any case, once the session state server is running, add the following <sessionState> element of your Web.config file as follows:
<sessionState
mode="StateServer"
stateConnectionString="tcpip=127.0.0.1:42626" sqlConnectionString="data source=127.0.0.1;Trusted_Connection=yes" cookieless="false"
timeout="20"
/>
Here, the mode attribute has been set to StateServer. That’s it! At this point, the CLR will host session-centric data within aspnet_state.exe. In this way, if the AppDomain hosting the web application crashes, the session data is preserved. Notice as well that the <sessionState> element can also support a stateConnectionString attribute. The default TCP/IP address value (127.0.0.1) points to the local machine. If you would rather have the .NET runtime use the aspnet_state.exe service located on another networked machine (again, think web farms), you are free to update this value.

CHAPTER 33 ■ ASP.NET STATE MANAGEMENT TECHNIQUES |
1271 |
Figure 33-10. Starting aspnet_state.exe using the Services applet
Storing Session Data in a Dedicated Database
Finally, if you require the highest degree of isolation and durability for your web application, you may choose to have the runtime store all your session state data within Microsoft SQL Server. The appropriate update to the Web.config file is simple:
<sessionState
mode="SQLServer"
stateConnectionString="tcpip=127.0.0.1:42626" sqlConnectionString="data source=127.0.0.1;Trusted_Connection=yes" cookieless="false"
timeout="20"
/>
However, before you attempt to run the associated web application, you need to ensure that the target machine (specified by the sqlConnectionString attribute) has been properly configured. When you install the .NET Framework 3.5 SDK (or Visual Studio 2008), you will be provided with two files named InstallSqlState.sql and UninstallSqlState.sql, located by default under C:\Windows\Microsoft.NET\Framework\<version>. On the target machine, you must run the InstallSqlState.sql file using a tool such as the Microsoft SQL Server Management Studio (which ships with Microsoft SQL Server 2005).
Once this SQL script has executed, you will find a new SQL Server database has been created (ASPState) that contains a number of stored procedures called by the ASP.NET runtime and a set of tables used to store the session data itself (also, the tempdb database has been updated with a set of tables for swapping purposes). As you would guess, configuring your web application to store session data within SQL Server is the slowest of all possible options. The benefit is that user data is as durable as possible (even if the web server is rebooted).