Output Caching


Output caching provides the ability to cache entire rendered Web pages, either on the server or on the client. Output caching is suitable for pages that contain relatively static content and that are served the same way for multiple clients . Often, a Web page will contain a mix of content that combines dynamic content with more static content. Fragment caching , or partial-page caching , is a special type of output caching that is appropriate for mixed content pages. Fragment caching enables you to cache the static portions of the page once they have been broken out into separate user controls. You can then set individual cache policies for each of the user controls in the page.

Enabling Output Caching

ASP.NET provides two ways to enable output caching:

  • Declarative: Using the @ OutputCache directive at the top of an *.aspx page. This directive provides several attributes for setting the page cacheability , including Location, which specifies where the page should be cached.

  • Programmatic: Using the HttpCachePolicy object, which is conveniently exposed via the Page object's Cache property.

The HttpCachePolicy object is the programmatic counterpart to the @ OutputCache directive, meaning that you can configure the same cache settings using either approach. Cache settings are actually stored using several HTTP headers, the most important of which is the Cache-Control header. ASP.NET simply provides two different APIs for accessing the same set of headers. In fact, you could even bypass ASP.NET and configure the cache settings directly using the Internet Information Server (IIS) Administrative utility. However, you would miss out on the more granular control that ASP.NET provides for setting cache policies. The header settings in IIS apply to all pages in the Web application, whereas ASP.NET allows you to set the cache policy on a per-page basis.

The location of the cached page has a lot of significance for the caching behavior. ASP.NET unfortunately sets the default location to "Any," which puts no constraints on where the page may be cached. The other possible locations include the following:

  • Server: The server where the page originates.

  • Client: The client machine that requests the page.

  • Proxy servers: Servers that sit between the client and the server. Proxy servers will cache pages for all the clients to which they route. This shared cache may prevent individual clients from receiving cached pages tailored to their specific request.

An interesting thing about caching is that it was originally intended to occur only on client and proxy servers downstream of the origin server. The HTTP Cache-Control header provides a cache policy for downstream clients. ASP.NET allows for server-side caching, which goes above and beyond the original caching specification. ASP.NET manages output caching using an HttpModule called OutputCacheModule. This module is responsible for the range of server-side caching options that ASP.NET provides. In addition, this module enables specialized features such as sliding expiration.

Note

The term proxy servers is becoming outdated with today's newest generation of intermediary servers, such as Microsoft Internet Security and Acceleration Server (ISA Server). These so-called "caching intermediaries" act as both firewalls and as smart caching administrators. The "Caching with ISA Server" section discusses this further.

You will usually want to cache pages on the Web server because this allows pages to be served to multiple clients from a centralized cache. ASP.NET will typically choose the server as the cache location, but you do not want to leave this issue to chance. You especially do not want to allow proxy servers to cache pages for a group of clients. The recommended setting for the HTTP Cache-Control header is "no-cache," which means that the page may still be cached on the server, but it will never get cached on any downstream client or proxy server. For example, the following page sets the no-cache header and also varies the cache by the client's Accept-Language header:

[Declarative Code]

 <%@ OutputCache Duration="60" VaryByParam=" None" VaryByHeader=" accept-language"      Location=" Server"%> 

[Programmatic Code]

 With Page.Response.Cache     .SetLastModified(DateTime.Now)     .SetExpires(DateTime.Now.AddSeconds(60))     .SetCacheability(HttpCacheability.Server)     .SetValidUntilExpires(True)     .VaryByHeaders.UserLanguage = True ' Equivalent syntax on next line     .VaryByHeaders("Accept-Language") = True ' Equivalent to .UserLanguage End With 

Incidentally, when you set the @ OutputCache Location attribute to "Downstream," this is equivalent to setting the HTTP Cache-Control header to "public." This setting enables content to be cached on any machine that receives the HTTP response, including the client or proxy servers.

Let's look in detail at how to enable output caching at both the page and partial-page levels. This discussion will focus exclusively on using the @ OutputCache directive for enabling output caching. The "Enabling Caching Using the HttpCachePolicy Object" section describes how to accomplish the same task programmatically with the HttpCachePolicy object.

Enabling Page-Level Output Caching

You can declare the cache policy using the @ OutputCache directive at the top of an *.aspx page. For example, use the following syntax to specify that a page should be cached on the server for 20 seconds and for all requests:

 <%@ OutputCache Duration="20" VaryByParam=" None" Location=" Server"%> 

Table 5-1 summarizes the @ OutputCache directive's attributes for page-level output caching only. The @ OutputCache directive provides an additional attribute called VaryByControl, which is specific to user controls. The "Fragment Caching Using the VaryByControl Attribute" section discusses this attribute in more detail.

Table 5-1: @ OutputCache Attributes for Page-Level Output Caching

ATTRIBUTE

DESCRIPTION

Duration

(Required.) The expiration policy for the HTTP response, specified in seconds. In other words, this is the length of time that the page will be cached.

Location

(Optional.) The location of the output cache. The values for this attribute come from the OutputCacheLocation enumeration, which is a member of the System.Web.UI namespace. These values are as follows :

None : The output cache is disabled for the request.

Server : The output cache is stored on the Web server where the request is processed .

Client : The output cache is stored on the client that makes the actual request, not on an intermediate proxy server (if present). In addition, this setting allows the client's browser settings to override the server's caching instructions in the HTTP response.

Downstream : The output cache is stored on the client or proxy server where the request originates.

Any : The output cache may be stored on any server or client that is involved with the request. This attribute is not required, and will default to "Any" if it is not specified. However, you should always specify the Location and set the value to "Server," unless you specifically want the page to be cached elsewhere.

VaryByParam

(Required.) A semicolon-delimited list of GET and POST parameters used to vary the cache. If multiple parameters are specified, then the cache will vary when any individual parameter value differs from a previous request. The cache does not vary based on a unique combination of parameter values; it varies on individual parameter values. The allowable values for VaryByParam are:

None : The output cache does not vary based on GET and POST parameters.

{Asterisk } *: The output cache will vary by all GET and POST parameters.

{List }: The output cache will vary by a specified semicolon-delimited list of GET and POST parameters.

VaryByHeader

(Optional.) A semicolon-delimited list of HTTP request headers that are used to vary the cache. If multiple headers are specified then the cache will vary when any individual header value differs from a previous request. The cache does not vary based on a unique combination of header values; it varies on individual header values.

VaryByCustom

(Optional.) A custom string that is used to vary the cache. If this attribute is set to "browser," then the output cache will vary based on the browser name and its major version information. For example, the cache will vary between Internet Explorer 4.0, Netscape Navigator 4.6, and Internet Explorer 6.0. Any other attribute value is treated as a custom string.

To use the VaryByCustom attribute with a custom string requires an additional implementation step: You must open the Global.asax file and override the GetVaryByCustomString() method of the HttpApplication class. For example, you can use VaryByCustom to vary the output cache by the minor version of the browser.

Note that the Duration and VaryByParam attributes are always required for page-level caching. Let's look at some examples that illustrate page-level output caching using the VaryByParam and VaryByHeader attributes.

Output Caching Using the VaryByParam Attribute

Figure 5-1 shows a simple page, ap_OutputCaching.aspx , that illustrates page-level output caching. The page provides a text input field called txtUserName and a Label control that displays the system time and posted username. The label updates every time the page is submitted.

click to expand
Figure 5-1: Page-level output caching using VaryByParam

The page's output cache varies by the posted value of txtUserName , using the following directive:

 <%@ OutputCache Duration="60" Location=" Server" VaryByParam=" txtUserName"%> 

You can test this page by opening it in side-by-side browser windows . Enter a different username on each page and then click the Submit buttons repeatedly. Both pages will cache, but they will display different server timestamps for each unique username. This indicates you are receiving multiple cached versions of the same page.

Now let's consider an example that illustrates how to use the VaryByHeader attribute.

Output Caching Using the VaryByHeader Attribute

Figure 5-2 shows a simple page, ap_OutputCaching2.aspx , that serves customized content based on the Accept-Language HTTP request header. The page will display a different language string to users with the following header values: us-en, fr, and de-lu.

click to expand
Figure 5-2: Page-level output caching using VaryByHeader

The page simulates multiple international users by auto-assigning a different Accept-Language header value every 20 seconds. For example, a client is assigned "fr" if they request the page between 20 and 39 seconds after the minute. (The alternative was a coordinated international effort to ping the same page!) The code-behind file for doing this is shown in Listing 5-1.

Listing 5-1: Using the Accept-Language Header
start example
 Private Sub Page_Load(ByVal sender As System.Object, _     ByVal e As System.EventArgs) Handles MyBase.Load     Dim dtNow As Date = Now     ' Cycle through 3 languages every 20 seconds     With dtNow         If .Second > 0 And .Second < 20 Then             Response.AddHeader("Accept-Language", "us-en")             Me.lblDateTime.Text = "Welcome! The time is: "& dtNow.ToLongTimeString         ElseIf .Second > 20 And .Second < 40 Then             Response.AddHeader("Accept-Language", "fr")             Me.lblDateTime.Text = "Bienvenue! L'heure est: "& .ToLongTimeString         Else             Response.AddHeader("Accept-Language", "de-lu")             Me.lblDateTime.Text = "Wilkommen! Die Zeit ist: "& .ToLongTimeString         End If     End With End Sub 
end example
 

In addition, the page will cache for a 30-second period following the initial request and will vary only by the Accept-Header value. You set this cache policy using the following directive:

 <%@ OutputCache Duration="30" Location=" Server" VaryByParam=" None"     VaryByHeader=" Accept-Language"%> 

Notice that the VaryByParam attribute is still specified because it is a required attribute for page-level output caching. It is set here to "None" so that it does not interfere with the VaryByHeader cache policy. You can test this page by opening it in a browser window and clicking the Submit button several times over a 30-plus second interval. For example, the following is one possible sequence of strings you can get if you click the Submit button every 10 seconds:

 Welcome! The time is: 12:16:08 PM      {Submit button was clicked at 12:16:08 PM} Welcome! The time is: 12:16:08 PM      {Submit button was clicked at 12:16:18 PM} Welcome! The time is: 12:16:08 PM      {Submit button was clicked at 12:16:28 PM} Bienvenue! L'heure est: 12:16:38 PM    {Submit button was clicked at 12:15:38 PM} 

This wraps up our discussion of output caching using the @ OutputCache directive. Now turn your attention to fragment caching , also known as partial-page caching .

Enabling Fragment Caching (Partial Page-Level)

Fragment caching provides the ability to set different cache policies within the same page. This approach is suitable for pages that mix dynamic content with static content. The dynamic content cannot be cached and will be regenerated every time the page is requested . You can separate the static content into a user control and then set a specific cache policy. Fragment caching accommodates as many different cache policies as you need. Every user control on a page can have its own unique cache policy. So, you just need to break out one user control per unique cache policy on a page.

You can configure fragment caching for a user control with the @ OutputCache directive. User controls support a limited range of attributes for this directive, as compared to what an *.aspx Page object supports. Table 5-2 summarizes the @ OutputCache directive's attributes for partial page-level output caching only.

Table 5-2: @ OutputCache Attributes for Fragment (Partial Page-Level) Caching

ATTRIBUTE

DESCRIPTION

Duration

(Required.) The expiration policy for the HTTP response, specified in seconds. In other words, this is the length of time that the user control will be cached.

VaryByParam

(Optional.) A semi- colon delimited list of GET and POST parameters that are used to vary the cache. If multiple parameters are specified then the cache will vary when any individual parameter value differs from a previous request. The cache does not vary based on a unique combination of parameter values; it varies on individual parameter values. The allowable values for VaryByParam are:

None : The output cache does not vary based on GET and POST parameters.

{Asterisk } *: The output cache will vary by all GET and POST parameters.

{List }: The output cache will vary by a specified semicolon-delimited list of GET and POST parameters.

VaryByControl

(Optional.) A semicolon-delimited list of user control properties used to vary the cache. VaryByControl also enables the user control to cache based on the values of its own child controls. The cache does not vary based on a unique combination of property values; it varies on individual property values.

VaryByCustom

(Optional.) A custom string that is used to vary the cache. See the full definition in Table 5-1.

Shared

(Optional.) A Boolean value that determines whether user control output can be shared between multiple pages in the same application. Set this value to true in order to create a shared cache. The default attribute value is "False." This attribute is only supported in ASP.NET 1.1 or later.

Fragment caching does not support the Location and VaryByHeader attributes. You will receive a parsing error if you attempt to specify them for the @ OutputCache directive in a user control. In addition, the VaryByParam attribute is not required like it is for page-level output caching, although you must specify either VaryByParam or VaryByControl in the @ OutputCache directive (or both).

A major inefficiency with fragment caching in ASP.NET 1.0 was that user control output was cached on a per-page basis. If multiple pages referenced the same user control, then the server was forced to maintain separate cached versions of the control for each page. ASP.NET 1.1 overcomes this inefficiency by introducing the @ OutputCache directive's Shared attribute. This attribute is a Boolean value that determines whether user control output can be shared. For example, consider a user control that contains a child Textbox control, with an ID of TextBox1 . You would implement shared caching for this user control (in its design view) as follows:

 <%@ OutputCache Duration="60" VaryByControl=" TextBox1" Shared=" True" %> 

In this example, the server will continue to hold multiple copies of the control for every unique value entered into the textbox. However, with the Shared attribute value set to "True," the server can now economize its cache by sharing content between multiple pages. Users who enter the same textbox value on different pages will be able to share the same cached copy of the user control.

Note

ASP.NET 1.1 introduces the Shared attribute for fragment caching of user controls. This attribute determines whether user control output can be shared between multiple pages. ASP.NET 1.0 does not provide the Shared attribute.

Fragment Caching Using the VaryByParam Attribute

Figure 5-3 shows a page, ap_OutputCaching3.aspx , that illustrates fragment caching. The page provides a dynamic timestamp that updates every time the page is requested. The page also contains a simple user control, which is bounded with a rectangle frame for clarity. The user control displays its own timestamp and the name of the user who requested the page. The user control's output cache varies by the txtUserName posted value. It can do this because the user control is inserted within the form on the host page. All of the other controls, including the username textbox and the submit button, are contained within the host page.

click to expand
Figure 5-3: Fragment caching using VaryByParam

You design the *.ascx page for the user control as shown in Listing 5-2 (minus the length style attribute values).

Listing 5-2: The *.ascx Page
start example
 <%@ OutputCache Duration="10" VaryByParam=" txtUserName"%> <%@ Control Language=" vb" AutoEventWireup=" false"     Codebehind=" ap_UserControl1.ascx.vb" Inherits=" AspNetChap5.ap_UserControl1"     TargetSchema=" http://schemas.microsoft.com/intellisense/ie5" %>     <asp:Label id=" Label2">User Control posted at:</asp:Label> <asp:Label id=" lblDateTime"></asp:Label> 
end example
 

Listing 5-3 shows the code-behind file for the user control.

Listing 5-3: The Code-Behind File for the User Control
start example
 Public MustInherit Class ap_UserControl1     Inherits System.Web.UI.UserControl     Protected lblDateTime As System.Web.UI.WebControls.Label     Private Sub Page_Load(ByVal sender As System.Object, _         ByVal e As System.EventArgs) Handles MyBase.Load         Dim dtNow As Date = Now         If Not Page.IsPostBack Then             Me.lblDateTime.Text = dtNow.ToLongTimeString         Else             If Page.Request("txtUserName") <> "" Then                 Me.lblDateTime.Text = dtNow.ToLongTimeString & "by "& _                     Page.Request("txtUserName")             Else                 Me.lblDateTime.Text = dtNow.ToLongTimeString             End If         End If     End Sub End Class 
end example
 

The host page registers the control and inserts it into the form as shown in Listing 5-4.

Listing 5-4: Registering and Inserting the Control
start example
 <%@ Register TagPrefix=" Apress1" TagName=" UC1"          Src=" controls/ap_UserControl1.ascx" %> <%@ Page Language=" vb" AutoEventWireup=" false"          Codebehind=" ap_OutputCaching3.aspx.vb"          Inherits=" AspNetChap5.ap_OutputCaching3"%> <HTML>          <HEAD><!-- not shown --></HEAD>          <body MS_POSITIONING=" GridLayout">                    form id=" Form1" method=" post" runat=" server">                    <asp:label id=" Label1">Output Caching Example</asp:label>                    <asp:label id=" lblPageDateTime"></asp:label>                    <asp:label id=" Label2">Host page posted at: </asp:label>                    <asp:label id=" lblUserName">Username:</asp:label>                    <APRESS1:UC1 id=" MyUC1" runat=" server"></APRESS1:UC1>                    <asp:textbox id=" txtUserName"></asp:textbox>                    <asp:Button id=" btnSubmit"></asp:Button></form>          </body> </HTML> 
end example
 

You will notice that the user control specifies a cache policy in its designer code, but the host page does not specify any cache policy. The code-behind file for the host page is extremely simple:

 Public Class ap_OutputCaching3     Inherits System.Web.UI.Page     Protected WithEvents btnSubmit As System.Web.UI.WebControls.Button     Protected lblPageDateTime As System.Web.UI.WebControls.Label     Protected txtUserName As System.Web.UI.WebControls.TextBox     Private Sub Page_Load(ByVal sender As System.Object, _         ByVal e As System.EventArgs) Handles MyBase.Load         Dim dtNow As Date = Now         Me.lblPageDateTime.Text = dtNow.ToLongTimeString     End Sub End Class 

You can test this page by loading it into a browser window and submitting the form repeatedly. The host page's timestamp will update with every page request, and the user control's timestamp will update every 10 seconds. This example is simple, but you can extend the same principles to more complicated user controls that perform sophisticated processing on request parameters. This example shows just one user control, but you can easily add multiple user controls to the page, where each user control specifies its own cache policy.

Let's look at another fragment caching example, this time using the VaryByControl attribute.

Fragment Caching Using the VaryByControl Attribute

The VaryByControl attribute enables the caching of user controls. The purpose of this attribute is to vary the cache based on the values of child controls within the user control.

Figure 5-4 illustrates this behavior. The host page contains a dynamic timestamp and an embedded user control. The user control provides its own timestamp, and it contains a DropDownList control and a Submit button. The cache varies based on the value of the DropDownList control. When a user clicks the Submit button, the host page gets reposted, and the user control either regenerates or is retrieved from the cache. Figure 5-4 shows side-by-side browser windows of the same page. The left window was originally loaded at 10:37:46 A.M., and the right window was originally loaded at 10:37:47 A.M. Both pages were resubmitted again at 10:37:56 A.M., which falls within the 20-second cache window for each page. This indicates you are receiving multiple cached versions of the same page.

click to expand
Figure 5-4: Fragment caching using VaryByControl

You design the *.ascx page for the user control as shown in Listing 5-5 (minus the length style attribute values).

Listing 5-5: The *.axcx Page
start example
 <%@ Control Language=" vb" AutoEventWireup=" false"          Codebehind=" ap_UserControl2.ascx.vb"          Inherits=" AspNetChap5.ap_UserControl2"          TargetSchema=" http://schemas.microsoft.com/intellisense/ie5" %> <%@ OutputCache Duration="20" VaryByControl=" cboUserName"%> <asp:Label id=" Label2" runat=" server">User Control posted at:</asp:Label> <asp:Label id=" lblDateTime" runat=" server"></asp:Label> <asp:Label id=" Label1" runat=" server">Select a User:</asp:Label> <asp:DropDownList id=" cboUserName" runat=" server">          <asp:ListItem Value=" jeff">Jeffrey Hasan</asp:ListItem>          <asp:ListItem Value=" ken">Kenneth Tu</asp:ListItem> </asp:DropDownList> <asp:Button id=" btnChoose" Text=" Submit" runat=" server"></asp:Button> 
end example
 

Listing 5-6 shows the code-behind for the user control.

Listing 5-6: The Code-Behind File
start example
 Public MustInherit Class ap_UserControl2     Inherits System.Web.UI.UserControl     Protected cboUserName As System.Web.UI.WebControls.DropDownList     Protected lblDateTime As System.Web.UI.WebControls.Label     Protected WithEvents btnChoose As System.Web.UI.WebControls.Button     Private Sub Button1_Click(ByVal sender As Object, _         ByVal e As System.EventArgs) Handles btnChoose.Click         Dim dtNow As Date = Now         Me.lblDateTime.Text = dtNow.ToLongTimeString & "by "& _             Me.cboUserName.SelectedItem.Value     End Sub End Class 
end example
 

The host page, ap_OutputCaching4.aspx , registers the control and inserts it into the form as shown in Listing 5-7.

Listing 5-7: ap_OutputCaching4.aspx
start example
 <%@ Register TagPrefix=" Apress1" TagName=" UC2"         Src=" controls/ap_UserControl2.ascx" %> <%@ Page Language=" vb" AutoEventWireup=" false"         Codebehind=" ap_OutputCaching4.aspx.vb"         Inherits=" AspNetChap5.ap_OutputCaching4"%> <HTML>     <HEAD><!-- not shown --></HEAD>     <body MS_POSITIONING=" GridLayout">         <form id=" Form1" method=" post" runat=" server">             <asp:label id=" Label1">Output Caching Example</asp:label>             <asp:label id=" Label2" runat=" server">Host page posted at: </asp:label>             <asp:label id=" lblPageDateTime" runat=" server"></asp:label>             <APRESS1:UC2 id=" MyUC2" runat=" server"></APRESS1:UC2>         </form>      </body> </HTML> 
end example
 

Finally, you can test this fragment caching example by opening side-by-side browser windows and submitting the form with different users. You will see that each user creates a new version of the cached page.

Enabling Caching Using the HttpCachePolicy Object

The @ OutputCache directive will handle most caching scenarios for you. However, there are times when you will want to set the cache policy programmatically using the HttpCachePolicy object. This API provides more granular control over setting cache policies. In other words, it provides more access to the HTTP headers compared to what the @ OutputCache directive provides.

The HttpCachePolicy object is accessible using the Page.Cache property, and it is a member of the System.Web namespace. The API provides everything the @ OutputCache directive does (and more); however, there is not a one-to-one match between the class members and the directive attributes. You already saw one example that demonstrates how the SetCacheability() and SetExpires() methods configure the same settings as the directive. The following is a variation on the previous example:

[Declarative Code]

 <%@ OutputCache Duration="60" VaryByParam=" UserName" Location=" Server"%> 

[Programmatic Code]

 With Page.Response.Cache     .SetLastModified(DateTime.Now)     .SetExpires(DateTime.Now.AddSeconds(60))     .SetCacheability(HttpCacheability.Server)     .SetValidUntilExpires(True)     .VaryByParams("UserName") = True End With 

The API calls are a lot more involved than using the directive, so if your cache policies are straightforward, then you should always use the @ OutputCache directive.

Table 5-3 shows the more interesting members of the HttpCachePolicy class. The close relationship between the API and the HTTP headers is quite apparent from the class member descriptions.

Table 5-3: HttpCachePolicy Class Members

MEMBER

DESCRIPTION

SetCacheability()

Sets the Cache-Control HTTP header to one of the HttpCacheability enumeration values. These values are as follows:

NoCache : The output cache is disabled for the request. This value sets the Cache-Control:no-cache header.

Server : The output cache is stored on the Web server where the request is processed. This value also sets the Cache-Control:no-cache header; however, the page will still be cached on the origin server.

Private : The output cache is stored on the client that makes the actual request, not on an intermediate proxy server (if present). This value sets the Cache-Control:private header.

Public : The output cache is stored on the client or proxy server where the request originates. This value sets the Cache-Control:public header.

SetExpires()

Sets the Expires HTTP header to an absolute date and time. If you need to specify a relative duration (for example, 60 minutes), then you must set the date-time to the current date-time plus the duration length. For example, this is how you specify a cache duration of 60 seconds:

Page.Response.Cache.SetExpires (DateTime.Now.AddSeconds(60))

SetValidUntilExpires

Specifies that the ASP.NET cache should ignore any attempts by the client to override the HTTP Cache-Control headers.

SetLastModified

Sets the Last-Modified HTTP header to a specific date-time stamp.

SetSlidingExpiration

Sets the cache expiration to sliding scale. This will renew the page's HTTP headers every time that the server responds to the request.

VaryByHeaders

Gets or sets the list of HTTP headers that will be used to vary the cache.

VaryByParams

Gets or sets the list of HTTP parameters that will be used to vary the cache.

SetNoServerCaching

Stops the origin server from caching items. Once this method is called, caching may not be reenabled for the remaining duration of the response.

Figure 5-5 shows a page that sets its cache policy using the HttpCachePolicy object.

click to expand
Figure 5-5: Caching with the HttpCachePolicy object

The page displays the timestamp for the current request and varies the output cache by the value of the submitted username. The page will cache for a duration of 60 seconds per unique username. Listing 5-8 shows this page.

Listing 5-8: Caching with the HttpCachePolicy Object
start example
 Public Class ap_HttpCachePolicy     Inherits System.Web.UI.Page     Protected WithEvents TextBox1 As System.Web.UI.WebControls.TextBox     Protected WithEvents Button1 As System.Web.UI.WebControls.Button     Private Sub Page_Load(ByVal sender As System.Object, _         ByVal e As System.EventArgs) Handles MyBase.Load         With Page.Response.Cache             .SetLastModified(DateTime.Now)             .SetExpires(DateTime.Now.AddSeconds(60))             .SetCacheability(HttpCacheability.Server)             .SetValidUntilExpires(True)             .VaryByParams("*") = True         End With         Response.Write("The current request was issued at: "& _             Me.Context.Timestamp.ToLongTimeString)     End Sub End Class 
end example
 

The following are some sample responses (without the leading text):

 1:14:26 PM      {Invoke button was clicked at 1:14:26 PM; txtUser = jeff} 1:14:54 PM      {Invoke button was clicked at 1:14:54 PM; txtUser = ken} 1:14:26 PM      {Invoke button was clicked at 1:14:56 PM; txtUser = jeff} 1:15:28 PM      {Invoke button was clicked at 1:15:28 PM; txtUser = jeff} 1:14:54 PM      {Invoke button was clicked at 1:15:52 PM; txtUser = ken} 



Performance Tuning and Optimizing ASP. NET Applications
Performance Tuning and Optimizing ASP.NET Applications
ISBN: 1590590724
EAN: 2147483647
Year: 2005
Pages: 91

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