Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 30 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
30
Dung lượng
551,08 KB
Nội dung
Chapter 15 Application Data Caching 331 { DataTable dt = BindToInventory(); DataTable tableSelectedItems = this.CreateSelectedItemsTable(dt); Session["tableSelectedItems"] = tableSelectedItems; } } 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). 332 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() { DataTable dt = null; Trace.Warn("Page_Load", "looking in cache"); dt = (DataTable)Cache["InventoryDataTable"]; Trace.Warn("Page_Load", "done looking in cache"); if (dt == null) { Trace.Warn("Page_Load", "Performing DB lookup"); dt = new DataTable(); String strConnection = @"Provider=Microsoft.Jet.OLEDB.4.0; Data Source=|DataDirectory|ASPDotNetStepByStep.mdb"; DbProviderFactory f = DbProviderFactories.GetFactory("System.Data.OleDb"); using (DbConnection connection = f.CreateConnection()) Chapter 15 Application Data Caching 333 { connection.ConnectionString = strConnection; connection.Open(); DbCommand command = f.CreateCommand(); command.CommandText = "Select * from DotNetReferences"; command.Connection = connection; IDataReader reader = command.ExecuteReader(); dt.Load(reader); reader.Close(); connection.Close(); } Cache["InventoryDataTable"] = dt; Trace.Warn("Page_Load", "Done performing DB lookup"); } return dt; } This code reduces the cost of loading the page signifi cantly (after the data are loaded in the cache, of course). Next time the page is loaded, it’ll use the cached version—available through Cache at a tremendously reduced cost. How much is the cost savings? It’s huge—as you can see looking at the trace pages for the application. Let’s take a peek. 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: <configuration> <system.web> <trace enabled="true" /> <system.web> </configuration> 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. 334 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. Chapter 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 ex- 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 Overload Description 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 a dependency. Insert (String, Object, CacheDependency, DateTime, TimeSpan) Inserts an object into the Cache, associating it with a dependency and an expiration policy. Insert (String, Object, CacheDependency, DateTime, TimeSpan, CacheItemPriority, CacheItemRemovedCallback) Inserts an object into the Cache. Associates a depen- 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. 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 Insert Overload Descri p tio n 336 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 program- matically (as we saw in Chapter 12). Doing so involves constructing a DataTable and adding 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 { public QuotesCollection() { // // TODO: Add constructor logic here // } public void Synthesize() { // Be sure to give a name so that it will serialize as XML this.TableName = "Quotations"; DataRow dr; Columns.Add(new DataColumn("Quote", typeof(string))); Columns.Add(new DataColumn("OriginatorLastName", typeof(string))); Columns.Add(new DataColumn("OriginatorFirstName", typeof(string))); Chapter 15 Application Data Caching 337 dr = this.NewRow(); dr[0] = "Imagination is more important than knowledge."; dr[1] = "Einstein"; dr[2] = "Albert"; Rows.Add(dr); dr = this.NewRow(); dr[0] = "Assume a virtue, if you have it not"; dr[1] = "Shakespeare"; dr[2] = "William"; this.Rows.Add(dr); dr = this.NewRow(); 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[1] = "Twain"; dr[2] = "Mark"; this.Rows.Add(dr); dr = this.NewRow(); dr[0] = @"A man cannot be comfortable without his own approval."; dr[1] = "Twain"; dr[2] = "Mark"; this.Rows.Add(dr); dr = this.NewRow(); dr[0] = "Beware the young doctor and the old barber"; dr[1] = "Franklin"; dr[2] = "Benjamin"; this.Rows.Add(dr); dr = this.NewRow(); dr[0] = @"Reality is merely an illusion, albeit a very persistent one."; dr[1] = "Einstein"; dr[2] = "Albert"; this.Rows.Add(dr); dr = this.NewRow(); dr[0] = "Beer has food value, but food has no beer value"; dr[1] = "Sticker"; dr[2] = "Bumper"; this.Rows.Add(dr); dr = this.NewRow(); 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); 338 Part III Caching and State Management dr = this.NewRow(); dr[0] = "Whatever is begun in anger ends in shame"; dr[1] = "Franklin"; dr[2] = "Benjamin"; this.Rows.Add(dr); dr = this.NewRow(); dr[0] = "We think in generalities, but we live in details"; dr[1] = "Whitehead"; dr[2] = "Alfred North"; this.Rows.Add(dr); dr = this.NewRow(); dr[0] = "Every really new idea looks crazy at first."; dr[1] = "Whitehead"; dr[2] = "Alfred North"; this.Rows.Add(dr); dr = this.NewRow(); 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."; dr[1] = "Whitehead"; dr[2] = "Alfred North"; this.Rows.Add(dr); } } Building a DataTable in memory is straightforward—it’s mostly a matter of defi ning the col- umn schema and adding rows to the table. This class is available on the CD accompanying this book, so you don’t need to type the whole thing. You may just import it into the next examples. 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. Chapter 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) { QuotesCollection quotesCollection; DateTime dtCurrent = DateTime.Now; Trace.Warn("Page_Load", "Testing cache at: " + dtCurrent.ToString()); quotesCollection = (QuotesCollection)Cache["QuotesCollection"]; if (quotesCollection == null) { 340 Part III Caching and State Management quotesCollection = new QuotesCollection(); quotesCollection.Synthesize(); DateTime dtExpires = new DateTime(2008, 5, 31, 23, 59, 59); dtCurrent = DateTime.Now; Trace.Warn("Page_Load", "Caching at: " + dtCurrent.ToString()); Trace.Warn("Page_Load", "This entry will expire at: " + dtExpires); Cache.Insert("QuotesCollection", quotesCollection, null, dtExpires, System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Default, null); } this.GridView1.DataSource = quotesCollection; this.DataBind(); } 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 ac- 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) { QuotesCollection quotesCollection; DateTime dtCurrent = DateTime.Now; Trace.Warn("Page_Load", "Testing cache: " + dtCurrent.ToString()); [...]... control’s output cache depends VaryByContentEncoding encodings Specifies a list of encoding strings separated by commas used to vary the output cache VaryByCustom browser Tells ASP. NET to vary the output cache by browser name and version, or by a custom string; must be handled by an override of GetVaryByCustomString in Global.asax custom string VaryByHeader * header names VaryByParam None * param name A semicolon-delimited... SetValidUntilExpires Determines whether the ASP. NET cache should ignore HTTP CacheControl headers sent by the client for invalidating the cache SetVaryByCustom Specifies a custom text string for managing varying cached output responses VaryByHeaders Parameter list of all HTTP headers that will be used to vary cache output VaryByParam Parameter list received by a GET (query string) or POST (in the body... Page_Load method, meaning the page content was cached There are other ways to modify the VaryByParam attribute One way is to use the word “none,” which means ASP. NET will cache only one version of the page for each type of request (for example, GET, POST, and HEAD) Using an asterisk for VaryByParam (“*”) tells ASP. NET to cache as many different versions of the page as there are query string or POST body... versions of the page as there are unique names typed by users into the name text box Using VaryByHeader in the OutputCache directive tells ASP. NET to generate a separate cache entry for each new header string that comes down (for example, UserAgent and UserLanguage represent HTTP headers that may be sent by the client) We’ll cache a User control shortly The VaryByControl attribute lets you cache separate content... versions for each page that has a User control with unique properties Finally, VaryByCustom tells ASP. NET to manage separate cache entries dependent on a couple of factors The first factor is the browser types and versions Alternatively, you may provide a custom GetVaryByCustomString method in Global.asax that tells ASP. NET to create separate cached versions of a page based on a custom defined string 360... method runs and produces the following output: No matter now many times you refresh the browser (you may do this by pressing F5 while running Internet Explorer within 15 seconds of first accessing the page), ASP. NET will grab the cached content and display that As soon as 15 seconds has expired, ASP. NET runs the page in the normal way, calling Page_Load, regenerating the content, and caching it again The... complex controls like the DataList or the GridView whose rendering process is expensive All of these things usually take time to process Just as you can bypass recurring round-trips to a database by caching data in memory, you may configure ASP. NET to bypass the entire page-rendering process and send back content that’s already been rendered once This is called output caching Caching Page Content As you... 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 configuration file 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 dependency between the database and the cached item LISTING... cases, it’s enough to blindly cache the content of certain pages by simply putting the OutputCache directive in the page However, sometimes you need a bit more control over what’s happening in the output cache ASP. NET supports a number of parameters you may use to manage the way the cache functions You may control the output caching behavior by either changing the parameters in the OutputCache directive... < /asp: GridView> Once items are in the cache and their lifetimes are established through expirations and cached item dependencies, one other cache administrative task remains—reacting when items are removed Clearing the Cache As you can see from the previous examples, ASP. NET clears the cache on several . 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. null) { 34 0 Part III Caching and State Management quotesCollection = new QuotesCollection(); quotesCollection.Synthesize(); DateTime dtExpires = new DateTime(2008, 5, 31 , 23, 59 , 59 ); dtCurrent. dt = new DataTable(); String strConnection = @"Provider =Microsoft. Jet.OLEDB.4.0; Data Source=|DataDirectory|ASPDotNetStepByStep.mdb"; DbProviderFactory f = DbProviderFactories.GetFactory("System.Data.OleDb");