First, you can cache an entire HTTP response the entire Web page using a mechanism called output caching.. Output Caching Output caching is a way to keep the dynamically generated page c
Trang 1Chapter 22: State Management
expensive database retrieval, those UserControls can retrieve the necessary data from
HttpContext.Items The database is hit only once
❑ When individual units within a single HttpRequest need to act on the same or similar data:If
the lifetime of your data is just one request, consider usingHttpContext.Items, as a short term
cache
TheItemscollection holds objects, just like many of the collections that have been used in this chapter
You need to cast those objects back to their specific type when they are retrieved
Within a Web-aware Database Access Layer, per-request caching can be quickly implemented with the
simple coding pattern shown here Note that this sample code is a design pattern and there is noMyData
class; it’s for illustration
VB
Public Shared Function GetExpensiveData(ID As Integer) As MyData
Dim key as string = "data" & ID.ToString()
Dim d as MyData = _
CType(HttpContext.Current.Items(key), MyData)
If d Is Nothing Then
d = New Data()
’Go to the Database, do whatever
HttpContext.Current.Items(key) = d
End If
Return d
End Function
C#
public static MyData GetExpensiveData(int ID)
{
string key = "data" + ID.ToString();
MyData d = (MyData) HttpContext.Current.Items[key];
if (d == null)
{
d = new Data();
//Go to the Database, do whatever
HttpContext.Current.Items[key] = d;
}
return d;
}
This code checks theItemscollection of the currentHttpContextto see if the data is already there If
not, the data is retrieved from the appropriate backing store and then stored in theItemscollection
Subsequent calls to this function within the sameHttpRequestreceive the already-cached object
As with all optimizations and caching, premature optimization is the root of all evil Measure your need
for caching, and measure your improvements Don’t cache just because it feels right; cache because it
makes sense
Trang 2Evjen c22.tex V2 - 01/28/2008 3:19pm Page 1069
Chapter 22: State Management
Summar y
This chapter explored the many ways to manage State within your ASP.NET application TheSession
object and its providers offer many choices Each has its own pros and cons for managing state in the
form of object references and serialized objects in a way that can be made largely transparent to
the application Server-side Session state data can have its unique identifying key stored in a cookie
or the key can be carried along in the URL Cookies can also be used independently to store small
amounts of data and persist it between visits, albeit in much smaller amounts and with simpler types
Hidden fields, ViewState, ControlState, postbacks, and new cross-page postbacks offer new possibilities for managing small bits of state within a multi-page user experience.HttpContext.Current.Itemsoffers
a perfect place to hold transient state, living the life of only a singleHttpRequest QueryStrings are an
old standby for holding non-private state that is appropriate for navigation
ASP.NET has improved on ASP.NET 1.x’s state management options with a flexible Session State
Provider module, the addition of Control State for user controls, and cross-page postbacks for a more
mature programming model
1069
Trang 3Performance is a key requirement for any application or piece of code that you develop The browser
helps with client-side caching of text and images, whereas the server-side caching you choose to
implement is vital for creating the best possible performance Caching is the process of storing
frequently used data on the server to fulfill subsequent requests You will discover that
grab-bing objects from memory is much faster than re-creating the Web pages or items contained in
them from scratch each time they are requested Caching increases your application’s performance,
scalability, and availability The more you fine-tune your application’s caching approach, the better
it performs
This chapter focuses on caching, including the SQL invalidation caching capabilities that ASP.NET
provides This chapter takes a close look at this unique aspect of caching When you are using SQL
cache invalidation, if the result set from SQL Server changes, the output cache can be triggered to
change automatically This ensures that the end user always sees the latest result set, and the data
presented is never stale After introducing SQL cache invalidation, this chapter also covers other
performance enhancements It discusses the new Post-Cache Substitution feature, which caches
entire pages while dynamically replacing specified bits of content Lastly, this chapter covers a new
capability that enables a developer to create custom dependencies
Caching
There are several ways to deal with caching in ASP.NET First, you can cache an entire HTTP
response (the entire Web page) using a mechanism called output caching Two other methods are
partial page caching and data caching The following sections describe these methods
Output Caching
Output caching is a way to keep the dynamically generated page content in the server’s memory
or disk for later retrieval This type of cache saves post-rendered content so it won‘t have to be
Trang 4Evjen c23.tex V2 - 01/28/2008 3:38pm Page 1072
Chapter 23: Caching
when any subsequent requests are made to the server You apply output caching by inserting an
OutputCachepage directive at the top of an.aspxpage, as follows:
<%@ OutputCache Duration="60" VaryByParam="None" %>
TheDurationattribute defines the number of seconds a page is stored in the cache TheVaryByParam
attribute determines which versions of the page output are actually cached You can generate different
responses based on whether an HTTP-POST or HTTP-GET response is required Other than the attributes
for theOutputCachedirective, ASP.NET includes theVaryByHeader,VaryByCustom,VaryByControl, and
Locationattributes Additionally, theSharedattribute can affect UserControls, as you’ll see later
Caching in ASP.NET is implemented as anHttpModulethat listens to allHttpRequests that come through
the ASP.NET worker process TheOutputCacheModulelistens to the application’sResolveRequestCache
andUpdateRequestCacheevents, handles cache hits and misses, and returns the cached HTML,
bypass-ing the Page Handler if need be
VaryByParam
TheVaryByParamattribute can specify whichQueryStringparameters cause a new version of the page
to be cached:
<%@ OutputCache Duration="90" VaryByParam="pageId;subPageId" %>
For example, if you have a page callednavigation.aspxthat includes navigation information in the
QueryString, such aspageIdandsubPageId, theOutputCachedirective shown here caches the page for
every different value ofpageIdandsubPageId In this example, the number of pages is best expressed
with an equation:
cacheItems = (num of pageIds) * (num of subPageIds)
wherecacheItemsis the number of rendered HTML pages that would be stored in the cache Pages are
cached only after they’re requested and pass through theOutputCacheModule The maximum amount of
cache memory in this case is used only after every possible combination is visited at least once Although
these are just potential maximums, creating an equation that represents your system’s potential maximum
is an important exercise
If you want to cache a new version of the page based on any differences in theQueryStringparameters,
useVaryByParam = "*", as in the following code
<%@ OutputCache Duration="90" VaryByParam="*" %>
It’s important to ‘‘do the math’’ when using the VaryBy attributes For example, you could add
VaryByHeaderand cache a different version of the page based on the browser’s reportedUser-Agent
HTTP Header
<%@ OutputCache Duration="90" VaryByParam="*" VaryByHeader="User-Agent"%>
TheUser-Agentidentifies the user’s browser type ASP.NET can automatically generate different
ren-derings of a given page that are customized to specific browsers, so it makes sense in many cases to
save these various renderings in the cache A Firefox user might have slightly different HTML than an
IE user so we don’t want to send all users the exact same post-rendered HTML Literally dozens, if not
hundreds, ofUser-Agentstrings exist in the wild because they identify more than just the browser type;
1072
Trang 5Chapter 23: Caching
thisOutputCachedirective could multiply into thousands of different versions of this page being cached, depending on server load In this case, you should measure the cost of the caching against the cost of
re-creating the page dynamically
Always cache what will give you the biggest performance gain, and prove that
assumption with testing Don’t ‘‘cache by coincidence’’ using attributes like
VaryByParam = "*" A common rule of thumb is to cache the least possible amount
of data at first and add more caching later if you determine a need for it Remember
that the server memory is a limited resource so you may want configure the use of
disk caching in some cases Be sure to balance your limited resources with security
as a primary concern; don’t put sensitive data on the disk.
VaryByControl
VaryByControlcan be a very easy way to get some serious performance gains from complicated
User-Controls that render a lot of HTML that doesn’t change often For example, imagine a UserControl that renders a ComboBox showing the names of all the countries in the world Perhaps those names are
retrieved from a database and rendered in the combo box as follows:
<%@ OutputCache Duration="2592000" VaryByControl="comboBoxOfCountries" %>
Certainly the names of the world’s countries don’t change that often, so theDurationmight be set to a month (in seconds) The rendered output of the UserControl is cached, allowing a page using that control
to reap performance benefits of caching the control while the page itself remains dynamic
VaryByCustom
Although theVaryByattributes offer a great deal of power, sometimes you need more flexibility If
you want to take theOutputCachedirective from the previous navigation example and cache by a
value stored in a cookie, you can addVaryByCustom The value ofVaryByCustomis passed into the
GetVaryByCustomStringmethod that can be added to theGlobal.asax.cs This method is called every time the page is requested, and it is the function’s responsibility to return a value
A different version of the page is cached for each unique value returned For example, say your users
have a cookie calledLanguagethat has three potential values: en, es, and fr You want to allow users to specify their preferred language, regardless of their language reported by their browser.Languagealso
has a fourth potential value — it may not exist! Therefore, theOutputCachedirective in the following
example caches many versions of the page, as described in this equation:
cacheItems = (num of pageIds) * (num of subPageIds) * (4 possible Language values)
To summarize, suppose there were 10 potential values forpageId, five potentialsubPageIdvalues for
eachpageId, and 4 possible values forLanguage That adds up to 200 different potential cached versions
of this single navigation page This math isn’t meant to scare you away from caching, but you should
realize that with great (caching) power comes great responsibility
The followingOutputCachedirective includespageIdandsubPageIdas values forVaryByParam, and
VaryByCustompasses in the value of"prefs"to theGetVaryByCustomStringcallback function in
Listing 23-1:
Trang 6Evjen c23.tex V2 - 01/28/2008 3:38pm Page 1074
Chapter 23: Caching
<%@ OutputCache Duration="90" VaryByParam="pageId;subPageId" VaryByCustom="prefs"%>
Caching in ASP.NET involves a tradeoff between CPU and memory: how hard is it to make this page,
versus whether you can afford to hold 200 versions of it If it’s only 5 KB of HTML, a potential megabyte
of memory could pay off handsomely versus thousands and thousands of database accesses Since most
pages will hit the database at least once during a page cycle, every page request served from the cache
saves you a trip to the database Efficient use of caching can translate into cost savings if fewer database
servers and licenses are needed
The code in Listing 23-1 returns the value stored in theLanguagecookie Theargparameter to the
GetVaryByCustomStringmethod contains the string"prefs", as specified inVaryByCustom
Listing 23-1: GetVaryByCustomString callback method in the HttpApplication
VB
Overrides Function GetVaryByCustomString(ByVal context As HttpContext, _
ByVal arg As String) As String
If arg.ToLower() = "prefs" Then
Dim cookie As HttpCookie = context.Request.Cookies("Language")
If cookie IsNot Nothing Then Return cookie.Value End If
End If
Return MyBase.GetVaryByCustomString(context, arg)
End Function
C#
public override string GetVaryByCustomString(HttpContext context, string arg)
{
if(arg.ToLower() == "prefs")
{
HttpCookie cookie = context.Request.Cookies["Language"];
if(cookie != null)
{
return cookie.Value;
}
}
return base.GetVaryByCustomString(context, arg);
}
TheGetVaryByCustomStringmethod in Listing 23-1 is used by theHttpApplicationinGlobal.asax.cs
and will be called for every page that uses theVaryByCustom OutputCachedirective If your application
has many pages that useVaryByCustom, you can create a switch statement and a series of helper functions
to retrieve whatever information you want from the user’s HttpContext and to generate unique values
for cache keys
Partial Page (UserControl) Caching
Similar to output caching, partial page caching enables you to cache only specific blocks of a Web page You
can, for example, cache only the center of the page the user sees Partial page caching is achieved with
the caching of user controls so you can build your ASP.NET pages to utilize numerous user controls and
1074
Trang 7Chapter 23: Caching
then apply output caching to the selected user controls This, in essence, caches only the parts of the page that you want, leaving other parts of the page outside the reach of caching This is a nice feature and, if done correctly, it can lead to pages that perform better This requires a modular design to be planned up front so you can partition the components of the page into logical units composed of user controls
Typically, UserControls are designed to be placed on multiple pages to maximize reuse of common func-tionality However, when these UserControls (ASCX files) are cached with the@OutputCachedirective’s default attributes, they are cached on a per-page basis That means that even if a UserControl outputs the identical HTML when placed onpageA.aspxas it does when placed onpageB.aspx, its output is cached twice By enabling theShared = "true"attribute, the UserControl’s output can be shared among multiple pages and on sites that make heavy use of shared UserControls:
<%@ OutputCache Duration="300" VaryByParam="*" Shared="true" %>
The resulting memory savings can be surprisingly large since you only cache one copy of the
post-rendered user control instead of caching a copy for each page As with all optimizations, you need to test both for correctness of output as well as memory usage
If you have an ASCX UserControl using the OutputCache directive, remember that
the UserControl exists only for the first request If a UserControl has its HTML
retrieved from the OutputCache , the control doesn’t really exist on the ASPX page.
Instead, a PartialCachingControl is created that acts as a proxy or ghost of that
control.
Any code in the ASPX page that requires a UserControl to be constantly available will fail if that control is reconstituted from theOutputCache So be sure to always check for this type of caching before using any control The following code fragment illustrates the kind of logic required when accessing a potentially cached UserControl:
VB
Protected Sub Page_Load()
If Not PossiblyCachedUserControl is Nothing Then
" Place code manipulating PossiblyCachedUserControl here
End If
End Sub
C#
protected void Page_Load()
{
if (PossiblyCachedUserControl != null)
{
// Place code manipulating PossiblyCachedUserControl here
}
}
Post-Cache Substitution
Output caching has typically been an all-or-nothing proposition The output of the entire page is cached for later use However, often you want the benefits of output caching, but you also want to keep a small bit of dynamic content on the page It would be a shame to cache a page but be unable to output a
dynamic ‘‘Welcome, Scott!’’
Trang 8Evjen c23.tex V2 - 01/28/2008 3:38pm Page 1076
Chapter 23: Caching
ASP.NET 2.0 added post-cache substitution as an opportunity to affect the about-to-be-rendered page
A control is added to the page that acts as a placeholder It calls a method that you specify after the
cached content has been returned The method returns any string output you like, but you should be
careful not to abuse the feature If your post-cache substitution code calls an expensive stored procedure,
you could easily lose any performance benefits you might have expected
Post-cache substitution is an easy feature to use It gives you two ways to control the substitution:
❑ Call the newResponse.WriteSubstitutionmethod, passing it a reference to the desired
substi-tution method callback
❑ Add a<asp:Substitution>control to the page at the desired location, and set itsmethodName
attribute to the name of the callback method
To try this feature, create a new Web site with aDefault.aspx Drag a label control and a substitution
control to the design surface The code in Listing 23-2 updates the label to display the current time, but the
page is cached immediately and future requests return that cached value Set themethodNameproperty
in the substitution control toGetUpdatedTime, meaning the name of the static method that is called after
the page is retrieved from the cache
The callback function must be static because the page that is rendered doesn’t really exist at this point (an
instance of it doesn’t) Because you don’t have a page instance to work with, this method is limited in its
scope However, the currentHttpContextis passed into the method, so you have access to theSession,
Request, andResponse The string returned from this method is injected into theResponsein place of
the substitution control
Listing 23-2: Using the substitution control
ASPX
<%@ Page Language="C#" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ OutputCache Duration="30" VaryByParam="None" %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head >
<title>Substitution Control</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="Label1" Runat="server" Text="Label"></asp:Label>
<br />
<asp:Substitution ID="Substitution1" Runat="server"
methodName="GetUpdatedTime" />
<br />
</div>
</form>
</body>
</html>
VB
Partial Class _Default
Inherits System.Web.UI.Page Public Shared Function GetUpdatedTime(ByVal context As HttpContext) As String
1076
Trang 9Chapter 23: Caching
Return DateTime.Now.ToLongTimeString() + " by " + _
context.User.Identity.Name End Function
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles Me.Load Label1.Text = DateTime.Now.ToLongTimeString()
End Sub
End Class
C#
public partial class _Default : System.Web.UI.Page
{
public static string GetUpdatedTime(HttpContext context)
{
return DateTime.Now.ToLongTimeString() + " by " +
context.User.Identity.Name;
}
protected void Page_Load(object sender, EventArgs e)
{
Label1.Text = DateTime.Now.ToLongTimeString();
}
}
The ASPX page in Listing 23-2 has a label and a Post-Cache Substitution Control The control acts as a
placeholder in the spot where you want fresh content injected after the page is returned from the cache The very first time the page is visited only the label is updated because no cached content is returned The second time the page is visited, however, the entire page is retrieved from the cache — the page handler isn’t called and, consequently, none of the page-level events fire However, theGetUpdatedTimemethod
is called after the cache module completes its work Figure 23-1 shows the result if the first line is cached and the second line is created dynamically
Figure 23-1
Trang 10Evjen c23.tex V2 - 01/28/2008 3:38pm Page 1078
Chapter 23: Caching
HttpCachePolicy and Client-Side Caching
Caching is more than just holding data in memory on the server-side A good caching strategy should
also include the browser and its client-side caches, controlled by the Cache-Control HTTP Header HTTP
Headers are hints and directives to the browser on how to handle a request
Some people recommend using HTML<META>tags to control caching behavior Be aware that neither
the browsers nor routers along the way are obligated to pay attention to these directives You might have
more success using HTTP Headers to control caching
Because HTTP Headers travel outside the body of the HTTP message, you have several options for
viewing them You can enable tracing (see Chapter 21) and view the Headers from the tracing output In
Figure 23-2 I’m using the free TcpTrace redirector fromPocketSoap.com
For background information on HTTP headers and controlling caching, see the
document RFC 2616: Hypertext Transfer Protocol - HTTP/1.1, available on the World
Wide Web Consortium’s site at www.w3c.org You might also check out Fiddler at
www.fiddlertool.com/fiddler and FireBug for Firefox at www.getfirebug.com
Commercial tools such as HttpWatch from www.httpwatch.com add more features.
Create aDefault.aspxthat writes the current time in itsLoadevent Now, view the default HTTP
Headers used by ASP.NET, as in Figure 23-2 with page-level tracing turned on Note that one header,
Cache-Control: private, indicates to routers and other intermediates that this response is intended only
for you (private)
Figure 23-2
1078