Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Pro CSharp And The .NET 2.0 Platform (2005) [eng]

.pdf
Скачиваний:
111
Добавлен:
16.08.2013
Размер:
10.35 Mб
Скачать

904 CHAPTER 24 ASP.NET 2.0 WEB APPLICATIONS

Figure 24-4. The cache application GUI

In the page’s Load event handler, configure your GridView to display the current contents of the cached DataSet the first time the user posts to the page:

protected void Page_Load(object sender, EventArgs e)

{

if(!IsPostBack)

{

carsGridView.DataSource = (DataSet)Cache["AppDataSet"]; carsGridView.DataBind();

}

}

In the Click event handler of the Add this Car button, insert the new record into the Cars database using an ADO.NET SqlCommand object. Once the record has been inserted, call a helper function named RefreshGrid(), which will update the UI via an ADO.NET SqlDataReader (so don’t forget to “use” the System.Data.SqlClient namespace). Here are the methods in question:

protected void btnAddCar_Click(object sender, EventArgs e)

{

//Update the Inventory table

//and call RefreshGrid().

SqlConnection cn = new SqlConnection(); cn.ConnectionString =

"User ID=sa;Pwd=;Initial Catalog=Cars;" + "Data Source=(local)";

cn.Open(); string sql; SqlCommand cmd;

CHAPTER 24 ASP.NET 2.0 WEB APPLICATIONS

905

// Insert new Car. sql = string.Format

("INSERT INTO Inventory(CarID, Make, Color, PetName) VALUES" + "('{0}', '{1}', '{2}', '{3}')",

txtCarID.Text, txtCarMake.Text, txtCarColor.Text, txtCarPetName.Text);

cmd = new SqlCommand(sql, cn); cmd.ExecuteNonQuery(); cn.Close();

RefreshGrid();

}

private void RefreshGrid()

{

// Populate grid.

SqlConnection cn = new SqlConnection(); cn.ConnectionString =

"User ID=sa;Pwd=;Initial Catalog=Cars;Data Source=(local)"; cn.Open();

SqlCommand cmd = new SqlCommand("Select * from Inventory", cn); carsGridView.DataSource = cmd.ExecuteReader(); carsGridView.DataBind();

cn.Close();

}

Now, to test the use of the cache, launch two instances of your web browser and navigate to this *.aspx page. At this point, you should see that both DataGrids display identical information. From one instance of the browser, add a new Car. Obviously, this results in an updated GridView viewable from the browser that initiated the postback.

In the second browser instance, click the Refresh button. 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 DataSet 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 DataSet in the cache has expired and the CacheItemRemovedCallback delegate target method has automatically updated the cached DataSet.

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 Cars database. Nevertheless, the point should be clear: the cache allows you to automatically refresh data using

.NET delegates.

Note Unlike the HttpApplicationState type, the Cache class does not support Lock() and Unlock() methods. If you need to update interrelated items, you will need to directly make use of the types within the System.Threading namespace or the C# lock keyword.

Source Code The CacheState files are included under the Chapter 24 subdirectory.

906 CHAPTER 24 ASP.NET 2.0 WEB APPLICATIONS

Maintaining Session Data

So much for our examination of application-level state data. Next, let’s check out the role of peruser data stores. As mentioned earlier, a session is little more than a given user’s interaction with a web application, which is represented via the HttpSessionState type. To maintain stateful infor-

mation 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 ten 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:

<%@ Application Language="C#" %> <script runat="server">

...

void Session_Start(Object sender, EventArgs e)

{

}

void Session_End(Object sender, EventArgs e)

{

}

</script>

Like the HttpApplicationState type, the 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 helper 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:

CHAPTER 24 ASP.NET 2.0 WEB APPLICATIONS

907

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 (see Figure 24-5).

Figure 24-5. The session application GUI

The server-side Click event handler 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 u = (UserShoppingCart)Session["UserShoppingCartInfo"];

u.dateOfPickUp = myCalendar.SelectedDate; u.desiredCar = txtCarMake.Text; u.desiredCarColor = txtCarColor.Text;

908 CHAPTER 24 ASP.NET 2.0 WEB APPLICATIONS

u.downPayment = float.Parse(txtDownPayment.Text); u.isLeasing = chkIsLeasing.Checked; lblUserInfo.Text = u.ToString();

Session["UserShoppingCartInfo"] = u;

}

Within Session_End(), you may wish to persist the fields of the UserShoppingCart to a database or whatnot. In any case, if you were to launch two or three instances of your browser of choice, you would find that each user is able to build a custom shopping cart that maps to his unique instance of HttpSessionState.

Additional Members of HttpSessionState

The HttpSessionState class defines a number of other members of interest beyond the type indexer. First, the SessionID property will return the current user’s unique ID:

lblUserID.Text = string.Format("Here is your ID: {0}", Session.SessionID);

The Remove() and RemoveAll() methods may be used to clear items out of the user’s instance of

HttpSessionState:

Session.Remove["SomeItemWeDontNeedAnymore"];

The HttpSessionState type also defines a set of members that control the expiration policy of the current session. Again, by default each user has 20 minutes of inactivity before the HttpSessionState object is destroyed. Thus, if a user enters your web application (and therefore obtains a unique session ID), but does not return to the site within 20 minutes, the runtime assumes the user is no longer interested and destroys all session data for that user. You are free to change this default 20-minute expiration value on a user-by-user basis using the Timeout property. The most common place to do so is within the scope of your Global.Session_Start() method:

protected void Session_Start(Object sender, EventArgs e)

{

// Each user has 5 minutes of inactivity.

Session.Timeout = 5; Session["UserShoppingCartInfo"]

= new UserShoppingCart();

}

Note If you do not need to tweak each user’s Timeout value, you are able to alter the 20-minute default for all users via the Timeout attribute of the <sessionState> element within the Web.config file (examined at the end of this chapter).

The benefit of the Timeout property is that you have the ability to assign specific timeout values discretely for each user. For example, imagine you have created a web application that allows users to pay cash for a given membership level. You may say that Gold members should time out within one hour, while Wood members should get only 30 seconds. This possibility begs the question, how can you remember user-specific information (such as the current membership level) across web visits? One possible answer is through the user of the HttpCookie type. (And speaking of cookies . . .)

Source Code The SessionState files are included under the Chapter 24 subdirectory.

CHAPTER 24 ASP.NET 2.0 WEB APPLICATIONS

909

Understanding Cookies

The final state management technique examined here is the act of persisting data within 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 if 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 am 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. For example, when I log on to http://www.ministryofsound.com, I am automatically shown content that reflects my musical tastes. The reason (in part) has to do with a cookie stored on my computer that contains information regarding the type of music I tend to play.

The exact location of your cookie files will depend on which browser you happen to be using. For those using Microsoft Internet Explorer, cookies are stored by default under C:\Documents and Settings\<loggedOnUser>\Cookies (see Figure 24-6).

Figure 24-6. Cookie data as persisted under Microsoft Internet Explorer

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. Temporary cookies (also termed session cookies) contain the same data as a persistent cookie, but the name/value pairs are never saved to the user’s machine; rather, they exist only within the HTTP header. Once the user logs off your site, 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.

910 CHAPTER 24 ASP.NET 2.0 WEB APPLICATIONS

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 24-7.

Figure 24-7. The UI of CookiesStateApp

Within the 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 btnInsertCookie_Click(object sender, System.EventArgs e)

{

// Make a new (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:

private void btnInsertCookie_Click(object sender, EventArgs e)

{

// Make a new (persistent) cookie.

HttpCookie theCookie =

new HttpCookie(txtCookieName.Text, txtCookieValue.Text);

theCookie.Expires = DateTime.Parse("03/24/2009"); Response.Cookies.Add(theCookie);

}

If you were to run this application and insert some cookie data, the browser automatically persists this data to disk. When you open this text file, you will see something similar to Figure 24-8.

CHAPTER 24 ASP.NET 2.0 WEB APPLICATIONS

911

Figure 24-8. The persistent cookie data

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, if you were to update your current UI with the means to obtain current cookie data via a Button widget, you could iterate over each name/value pair and present the information within a Label widget:

protected void btnShowCookies_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 24-9).

Figure 24-9.

912 CHAPTER 24 ASP.NET 2.0 WEB APPLICATIONS

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. Next up: the role of the Web.config file.

Source Code The CookieStateApp files are included under the Chapter 24 subdirectory.

Configuring Your ASP.NET Web Application Using

Web.config

During your examination of .NET assemblies, you learned that client applications can leverage an XML-based configuration file to instruct the CLR how it should handle binding requests, assembly probing, and other runtime details. The same holds true for ASP.NET web applications, with the notable exception that web-centric configuration files (introduced in Chapter 23) are always named Web.config (unlike *.exe configuration files, which are named based on the related client executable).

When you insert a Web.config file to your site using the WebSite Add New Item menu option, the default structure looks something like the following (comments removed for clarity):

<?xml version="1.0"?>

<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0"> <appSettings/>

<connectionStrings/>

<system.web>

<compilation debug="false"/> <authentication mode="Windows"/>

</system.web>

</configuration>

Like any *.config file, Web.config defines the root-level <configuration> element. Nested within the root is the <system.web> element, which can contain numerous subelements used to control how your web application should behave at runtime. Under ASP.NET, the Web.config file can be modified using any text editor. Table 24-4 outlines some of the subelements that can be found within a Web.config file.

Note Look up the topic “ASP.NET Settings Schema” within the .NET Framework 2.0 SDK documentation for full details on the format of Web.config.

Table 24-4. Select Elements of a Web.config File

Element

Meaning in Life

<appSettings>

This element is used to establish custom name/value pairs that can be

 

programmatically read in memory for use by your pages.

<authentication>

This security-related element is used to define the authentication mode

 

for this web application.

<authorization>

This is another security-centric element used to define which users can

 

access which resources on the web server.

CHAPTER 24 ASP.NET 2.0 WEB APPLICATIONS

913

Element

Meaning in Life

<compilation>

This element is used to enable (or disable) debugging and define the

 

default .NET language used by this web application, and it may

 

optionally define the set of external .NET assemblies that should be

 

automatically referenced.

<connectionStrings>

This element is used to hold external connection strings used within

 

this website.

<customErrors>

This element is used to tell the runtime exactly how to display errors

 

that occur during the functioning of the web application.

<globalization>

This element is used to configure the globalization settings for this web

 

application.

<sessionState>

This element is used to control how and where session state data will

 

be stored by the .NET runtime.

<trace>

This element is used to enable (or disable) tracing support for this web

 

application.

 

 

A Web.config file may contain additional subelements above and beyond the set presented in Table 24-4. The vast majority of these items are security-related, while the remaining items are useful only during advanced ASP.NET scenarios such as creating with custom HTTP headers or custom HTTP modules (not covered here). If you wish to see the complete set of elements that can appear in a Web.config file, look up the topic “ASP.NET Settings Schema” using the online help.

Enabling Tracing via <trace>

The first aspect of the Web.config file you’ll examine is the <trace> subelement. This XML entity may take any number of attributes to further qualify its behavior, as shown in the following skeleton:

<trace enabled="true|false" localOnly="true|false" pageOutput="true|false" requestLimit="integer" traceMode="SortByTime|SortByCategory"/>

Table 24-5 hits the highlights of each attribute.

Table 24-5. Attributes of the <trace> Element

Attribute

Meaning in Life

Enabled

Specifies whether tracing is enabled for an application as a whole (the default

 

is false). As you saw in the previous chapter, you can selectively enable tracing

 

for a given *.aspx file using the @Page directive.

localOnly

Indicates that the trace information is viewable only on the host web server

 

and not by remote clients (the default is true).

pageOutput

Specifies how trace output should be viewed.

requestLimit

Specifies the number of trace requests to store on the server. The default is

 

10. If the limit is reached, trace is automatically disabled.

traceMode

Indicates that trace information is displayed in the order it is processed. The

 

default is SortByTime, but it can also be configured to sort by category.

 

 

Recall from the previous chapter that individual pages may enable tracing using the <%@Page%> directive. However, if you wish to enable tracing for all pages in your web application, simply update <trace> as follows: