If you could take those data elements and store them in a medium that offers quicker access than the database for example, the computer’s internal memory, your site could potentially ser
Trang 1Chapter 15 Application Data Caching 331
4 Run the application to make sure it works That is, it should connect to the
DotNetReferences table and bind the DataList to the table from the database
The GetInventory and BindToInventory methods are called by the Page_Load method How often is Page_Load called? Every time a new page is created—which happens for every single HTTP request destined for the UseDataList page In the case of running this application on a single computer with one client (in a testing situation), perhaps connecting to the database for every request isn’t a big deal However, for applications that are expected to serve thou-sands of users making frequent requests, repeated database access actually becomes a very big deal Accessing a database is actually a very expensive operation As we’ll see shortly, it may take up to a half second to simply connect to this access database and read the mere
25 rows contained in the DotNetReferences table Data access can only get more expensive as the size of the tables in the database grows A half second in the computer processing time scale is eons to the program
Now think about the nature of the inventory table Does it change often? Of course, not in the case of this simple application However, think about how this might work in a real ap-plication The items carried within an inventory may not change as often as other data sets might (and such changes might occur at regular, predictable intervals) If that’s the case, why does the application need to hit the database each time a page is loaded? Doing so is certainly overkill If you could take those data elements and store them in a medium that offers quicker access than the database (for example, the computer’s internal memory), your site could potentially serve many more requests than if it had to make a round-trip to the database every time it loads a page This is a perfect opportunity to cache the data (The caveat here is that if the inventory data set begins fl uctuating quickly, it will become a poor candidate for caching.)
Using the Data Cache
Using the data cache in the simplest and most naive way supported by ASP.NET is very much like accessing the Session object Remember, accessing the Session object involves using an indexer (the square bracket syntax) and a consistent index to store and retrieve data The data cache works in exactly the same way (although it has some other features for managing items
in the cache)
Trang 2332 Part III Caching and State Management
The strategy for caching a piece of data usually involves these steps:
1 Look in the cache for the data element
2 If it’s there, use it (bypassing the expensive database round-trip)
3 If the data element is unavailable in the cache, make a round-trip to the database to fetch it
4 If you had to fetch the data, cache the data element so it’s available next time around
The next example modifi es the UseDataList page so that it stores the data item in the cache after acquiring it for the fi rst time Although the fi rst time Page_Load is called, it may take a while (on a computer’s time scale), subsequent calls are much faster
Using the cache
1 Open the UseDataList.aspx.cs fi le and go to the GetInventory method
2 Modifying the method to use the cache is fairly straightforward The following listing
highlights the changes First, check to see if the item is in the cache If searching the cache for the DataSet turns up a valid object reference, then you may bypass the da-tabase lookup code and return the referenced DataSet If searching the cache turns up
a null object reference, go ahead and make the round-trip to the database When the database lookup fi nishes, you’ll have a good DataSet (provided the query succeeds) Cache it before returning the reference to the caller If you include the Trace state-ments, you’ll be able to see exactly how big an impact caching can make The changes you need to make are shown in bold:
protected DataTable GetInventory()
Trang 3Chapter 15 Application Data Caching 333
{
connection.ConnectionString = strConnection;
connection.Open();
DbCommand command = f.CreateCommand();
command.CommandText = "Select * from DotNetReferences";
Impact of Caching
If you included the Trace statements in the GetInventory method, then you can surf to the trace page to see the effect of caching The UseDataCaching application included here has the Trace attribute turned off in the page but has application tracing turned on That is, the web.confi g includes the following section:
You can see the trace information by surfi ng to the virtual directory with a fi le name of
Trace.axd Instead of surfi ng to the UseDataList.aspx fi le, surf to the Trace.axd fi le in the same
directory
Figure 15-1 shows the trace statements produced by accessing the page for the fi rst time The column farthest to the right indicates the time elapsed since the previous trace state-ment The trace statement shows that more than half a second has elapsed during the page loading time
Trang 4334 Part III Caching and State Management
FIGURE 15-1 Hitting the database takes more than half a second in this scenario
Make a few more posts to the page (for example, add some items from the inventory to the selected items grid) Then go back and look at the tracing information for the subsequent postbacks Figure 15-2 shows some examples of trace statements Fetching from the Cache
is dramatically faster than hitting the database—by several orders of magnitude! Again, you may not notice the difference with just one client surfi ng the page every once in a while However, when multiple clients are surfi ng to the same page simultaneously, they’ll get their responses much more quickly than if the page had to make a round-trip to the database
FIGURE 15-2 Fetching data from the cache takes 0.000040 seconds
Trang 5Chapter 15 Application Data Caching 335
Managing the Cache
The last example cached items in the most naive way possible They were simply placed in the cache and given an index However, at times you may need a bit more control over the items in the cache For example, what if the physical source backing one of the items you cache changes? If getting accurate information out to your users is important, you may want
to know about the change so you can handle it (perhaps by reloading the new information into the cache) As another example, what if you knew that the data in your cache would be-come invalid after a certain period of time or on a certain date? You’d want to make sure that the data in the cache are invalidated and the cache is appropriately refreshed with new data
In addition to placing items in the cache using the indexer, the Cache object implements
a parameterized method named Insert that allows you control over many aspects of the cached item The ways in which you may control cache entries include the following:
Setting up an absolute expiration time
Setting up a sliding expiration time
Setting up dependencies between cached items and their backing sources (for ample, database, fi le, or directory dependencies, or even dependencies on other cache entries)
Managing a relative invalidation priority of cached items
Setting up callback functions to be called when items are removed
The Cache’s insert method includes four overloads Table 15-1 enumerates them
TABLE 15-1 Overloads for the Cache.Insert Method
Insert (String, Object) Directly corresponds to the indexer version Blindly
places the object in the Cache using the string key in the fi rst parameter.
Insert (String, Object, CacheDependency) Inserts an object into the Cache and associates it with
Insert (String, Object, CacheDependency,
DateTime, TimeSpan, CacheItemPriority,
CacheItemRemovedCallback)
Inserts an object into the Cache Associates a dency and expiration and priority policies Also associ- ates the Cache entry with a delegate for a callback to notify the application when the item is removed from the cache
depen-The following example illustrates some of these settings and how they work In addition, the forthcoming examples illustrate another way to get DataTables and DataSets You may actu-ally create them programmatically The next few examples use a DataTable that is created
Trang 6336 Part III Caching and State Management
in memory rather than being fetched from a database Although the impact of caching isn’t quite as dramatic when using the in-memory DataTable, it is still appreciable—and you can see this other approach to managing data We’ll also see how the DataTable serializes as XML
as well (which will be useful for examining cached items with fi le dependencies)
DataSets in Memory
In Chapter 11, we looked at making a round-trip to the database to gather data suitable to bind to a control In the previous chapter we looked at maintaining data between requests
by using the Session object The Session object holds any serializable NET CLR object—even
a DataReader However, it’s not a good idea to hold on to a DataReader for long periods of time because that means holding a connection open Having too many open connections will ultimately slow your site to a crawl A better approach is to make single round-trips to the database and hold on to a DataTable or a DataSet
In addition to fetching them from databases, a DataTable may be synthesized matically (as we saw in Chapter 12) Doing so involves constructing a DataTable and adding
program-DataRows to describe the schema After constructing a DataTable, you may use it to create
columns with the correct “shape,” populate them, and then add them to the table’s columns collection Listing 15-2 shows an example of creating a DataTable in memory (note you also saw basic table creation in Listing 15-1 and in the previous chapter, but I didn’t call your at-tention to it at the time so I could save the discussion for this section) The table is a collec-tion of famous quotes and their originators that will be useful in the next examples
LISTING 15-2 The QuotesCollection Object
public class QuotesCollection : DataTable
Trang 7Chapter 15 Application Data Caching 337
dr[0] = @"A banker is a fellow who lends you his umbrella
when the sun is shining, but wants it back the
minute it begins to rain.";
dr[0] = @"Reality is merely an illusion, albeit a
very persistent one.";
dr[0] = @"Research is what I'm doing when I don't know
what I’m doing";
dr[1] = "Von Braun";
dr[2] = "Wernher";
this.Rows.Add(dr);
Trang 8338 Part III Caching and State Management
dr[0] = @"The illiterate of the 21st century will not be
those who cannot read and write, but
those who cannot learn,
unlearn, and relearn.";
Now let’s take a look at managing items within the cache
Cache Expirations
The fi rst way to manage cached items is to give them expiration thresholds In some cases, you may be aware of certain aspects of your cached data that allow you to place expiration times on it The Cache supports both absolute expirations and sliding expirations
Absolute expiration
1 To try out absolute expirations, add a new page to the UseDataCaching site named
CacheExpirations.aspx
2 Use the Website, Add Existing Item to bring the QuoteCollection.cs fi le from the CD
accompanying this book and make it part of this project
Trang 9Chapter 15 Application Data Caching 339
3 Drag a GridView onto the CacheExpirations page Don’t bind it to a data source yet
We’ll handle that in the Page_Load method
4 In the Page_Load method of the CacheExpirations page, check the cache to see if
there’s already an instance of the QuoteCollections object (just as in the previous example) If the data set is not available from the cache, create an instance of the
QuoteCollections class and call the Synthesize method to populate the table Finally, add
it to the cache using the overloaded Insert method You can use the DataTime class to generate an absolute expiration Bind the QuotesCollection object to the GridView The caching policy should be Cache.NoSlidingExpiration Set up some trace statements so you may see how the expiration times affect the lifetime of the cached object
protected void Page_Load(object sender, EventArgs e)
Trang 10340 Part III Caching and State Management
quotesCollection = new QuotesCollection();
5 Experiment with changing the dates and times to see how setting the expiration time
forces a reload of the cache
An absolute expiration time applied to the cached item tells ASP.NET to fl ush the item from the cache at a certain time Now let’s try using a different kind of expiration technique—the
sliding expiration Using a sliding expiration tells ASP.NET to keep the data in the cache as
long as it has been accessed within a certain period of time Items that have not been cessed within that time frame are subject to expiration
Sliding expirations
1 Now try setting a sliding expiration for the cached data Modify the Page_Load method
in the CacheExpirations page Getting a sliding expiration to work is simply a matter of changing the parameters of the Insert method Make up a time span after which you want the cached items to expire Pass DateTime.MaxValue as the absolute expiration date and the timespan as the fi nal parameter like so:
protected void Page_Load(object sender, EventArgs e)
Trang 11Chapter 15 Application Data Caching 341
2 Surf to the page You should see the cache reloading if you haven’t accessed the
cached item within the designated time frame
Cache dependencies represent another way to manage cached items Let’s take a look at how they work
Cache Dependencies
In addition to allowing objects in the cache to expire by duration, you may set up cies for the cached items For example, imagine our program loads some data from a fi le and places them into the cache The backing fi le (that is, the source of the cached information) may change, making the data in the cache invalid ASP.NET supports setting up a dependency be-tween the cached item and the fi le so that changing the fi le invalidates the cached item The conditions under which the cached items may be fl ushed include when a fi le changes, a di-rectory changes, another cache entry is removed, or data in a table in a SQL Server change (this is an often requested feature available since ASP.NET 2.0)
Trang 12dependen-342 Part III Caching and State Management
Here’s an example that illustrates setting up cache dependencies
Setting up cache dependencies
1 Add a new page to the UseDataCache site Name it CacheDependencies.aspx
2 Place a button on the page that you may use to post a request to the page to generate
an XML fi le from the QuotationsCollection Also, drag a GridView onto the page like so:
3 Double-click the button to generate a handler for the button that will save the XML
Schema and the XML from the DataTable to XML and XSD fi les in the App_Data directory
4 Within the handler, instantiate a QuotesCollection object and call Synthesize to generate
the data Within the page, you have a reference to the Server object Call the MapPath method in the Server object to get the physical path for saving the fi le Then use that path to create an XML fi le and a schema fi le The DataTable will do this for you auto-matically by calling the WriteXmlSchema and WriteXml methods, respectively
protected void ButtonSaveAsXML_Click(object sender, EventArgs e)
{
QuotesCollection quotesCollection = new QuotesCollection();
quotesCollection.Synthesize();
String strFilePathXml =
Trang 13Chapter 15 Application Data Caching 343
5 Now write a method to load the XML into the QuotationsCollection object and cache the
data You can use the fi le path to the XML fi le to create a dependency on the fi le When
it changes, ASP.NET will empty the cache Turn off the absolute expiration and the ing expiration by passing in Cache.NoAbsoluteExpiration and Cache.NoSlidingExpiration
slid-If you put trace statements in, you can see the effect of updating the fi le after it’s been loaded in the cache Finally, make sure to bind the GridView to the QuotationCollection protected void CacheWithFileDependency()
Trace.Warn("Page_Load", "Not found in cache");
quotesCollection = new QuotesCollection();
Trang 14344 Part III Caching and State Management
6 Call the CacheWithFileDependency() within the Page_Load method
protected void Page_Load(object sender, EventArgs e)
7 Now run the page It should load the XML and schema into the QuotesCollection, save
the QuotesCollection in the cache, and then show the data in the grid Clicking the
Save Table as XML button will refresh the XML fi le (on which a cache dependency was
made) Because the fi le on the disk changes, ASP.NET will fl ush the cache Next time you load the page, the cache will need to be reloaded
Now let’s look at the fi nal cache dependency: the SQL Server dependency
The SQL Server Dependency
ASP.NET 1.0 had a huge gap in its cache dependency functionality The most useful type of dependency was completely missing—that is, a dependency between a cached item coming from SQL Server and the physical database Because so many sites use data provided by SQL Server to back their DataGrids and other controls, establishing this dependency is defi nitely a most useful way to manage cached data
For the SQL Server dependency to work, you fi rst confi gure SQL Server using the program aspnet_regsql.exe The dependency is described in the confi guration fi le, whose name is passed into the SqlCacheDependency constructor The SqlCacheDependency class monitors the table When something causes the table to change, ASP.NET will remove the item from the Cache
Listing 15-3 shows a confi guration fi le with a dependency on SQL Server Listing 15-4 shows
an ASP.NET page that loads the data from the SQL Server database and establishes a dency between the database and the cached item
LISTING 15-3 Confi guration Settings for SQL Server Cache Dependency
Trang 15Chapter 15 Application Data Caching 345
LISTING 15-4 Page Using SqlCacheDependency
<form id="form1" runat="server">
<asp:GridView ID="GridView1" Runat="server">
Clearing the Cache
As you can see from the previous examples, ASP.NET clears the cache on several occasions, including:
removing items explicitly by calling Cache.Remove
removing low-priority items due to memory consumption
removing items that have expired
One of the parameters to one of the Insert overloaded methods is a callback delegate so that ASP.NET can tell you that something’s been removed from the cache To receive callbacks, you simply need to implement a method that matches the signature, wrap it in a delegate,