Caching Data as a String
A useful point to keep in mind whenever you store data that is not a string value, especially when you are dealing with more complex classes that persist their data internally (such as a DataSet), is that most support a method to export the content as a string. For example, with a DataSet (and, in version 2.0, a DataTable), you can use the WriteXml
method to write the data as an XML document, which is itself just a text string. You can also provide the writeMode
parameter (a value from the XmlWriteMode enumeration) that specifies if a schema is included, and whether you want a simple XML representation of the data or a diffgram that stores change information as well.
The following code shows how to serialize a DataSet as an XML document and store it in the ViewState of the page, and then re-instantiate the DataSet for each postback:
Imports System.IO ' needed to use StringWriter
Dim dsMyData As New DataSet()
If Page.IsPostBack Then
Dim reader As New _
'... code to fill DataSet from source data store here ...
Dim writer As New StringWriter()
ViewState("MyDataSet") = writer.ToString()
|Figure 1. DataSet XML Persistance: When you first load this page, it reads the DataSet data from a database, storing the DataSet as XML in the ViewState, but subsequent page refreshes reconstitute the DataSet from the XML representation in the ViewState rather than reloading it from the database.|
|Author's Note: See the .NET Framework SDK topic "StringWriter Members" in the "Reference | Class Library" section for more details.
The downloadable sample code for this article contains an example of caching the contents of a DataSet as an XML string value within the page ViewState. The write-ViewState.aspx
example uses the code listed above. The result looks like Figure 1
. When the server loads the first page, it fetches the data from the database; however, as you refresh the page, it rebuilds the DataSet each time from the XML cached in the ViewState.
Caching Data as a Disk File
The .NET Framework provides plenty of easy-to-use classes for reading and writing disk files. You can use these when you need to cache data required for more than one ASP.NET application. As an example, suppose you want to cache a DataSet containing data to which all the applications on your server require access. You could capitalize on the just-described technique for calling the WriteXml
methods, which work automatically with a disk fileall you have to do is provide the path and filename.
However, you can also use a BinaryFormatter to write data from objects that are serializable to a Stream. The code below demonstrates using a FileStream with a DataSet, but you could just as easily cache the data in memory using a MemoryStream if required. Why bother with binary serialization when the DataSet can serialize itself as XML? Well, the one extra feature here revolves around the fact that, in version 2.0, the DataSet gains a new property named RemotingFormat
. This can be set to a value from the SerializationFormat enumeration (the two options are Xml
). When set to Binary
, the volume of data generated from a DataSet is generally reduced by between 20 and 80 percent, which results in smaller disk files, faster loading, and ultimately better performance:
' create BinaryFormatter and Stream
Dim f As IFormatter = New BinaryFormatter()
Using s As New FileStream(datPath, _
FileMode.Create, FileAccess.Write, FileShare.None)
' specify Binary remoting format for the DataSet
' (new in v 2.0)
dsMyDataSet.RemotingFormat = SerializationFormat.Binary
' serialize the contents to the Stream as binary data
To rebuild the DataSet from the disk file, you can use this code:
' now de-serialize the data back into a new DataSet
Dim dsMyDataSet As DataSet
Dim f As IFormatter = New BinaryFormatter()
Using s As New FileStream(datPath, FileMode.Open, FileAccess.Read, FileShare.Read)
' de-serialize file contents into a DataSet
dsMyDataSet = CType(f.Deserialize(s), DataSet)
|Figure 2. DataSet Serialization: The figure illustrates the process of serializing a DataSet to a disk File and later deserializing it back into a DataSet.|
|Author's Note: The preceding code takes advantage of the Using construct, which was available in C# in v1.1 and is now available in VB.NET in v 2.0.
shows the provided example (serialize-to-disk.aspx
) that uses this code to serialize a DataSet to a disk file, and then de-serialize it back into a DataSet again. The page also displays some rows from the DataSet to prove that it worked.
Many objects support the ISerializable interface, and so can be used with a BinaryFormatter as shown above (though the actual format of the persisted data will vary with the object type). Suitable classes include collection types such as HashTable and NameValueCollection, the ADO.NET DataSet and DataTable, and the Image class that is used to create and store bitmap images.
ASP.NET Output Caching
|Author's Note: See the .NET Framework SDK topic "BinaryFormatter Members" in the "Reference | Class Library" section for more details.
One extremely useful way to boost performance in ASP.NET is to avoid running server-side script to provide a new version of the page for each request. To do that, you can use output caching
, which is useful when users regularly or repeatedly load the same page. ASP.NET implements output caching internally, so it's seamless for developers. By specifying a directive in the ASP.NET page, the server automatically caches the HTML generated by the page according to the conditions you specify.
The simplest output caching directive is:
<%@OutputCache Duration="#seconds" VaryByParam="None" />
The preceding line instructs ASP.NET to cache the HTML for the specified number of seconds, irrespective of the parameters sent with the page request from the client. "Parameters" in this sense means any values in the Request collections (Form, QueryString, Cookies, and ServerVariables). While the page is cached, every request will return the HTML generated the last time that the page was executed being sent to the client. In IIS 6.0, in Windows Server 2003, this detection of cached pages is actually done inside the kernel-level HTTP redirector module, and so it is blindingly fastquicker than in IIS 5.0 and, of course, a great deal faster that re-executing the page.
There are many alternative ways to configure output caching in ASP.NET. The OutputCache
directive provides a range of options, for example:
<%@ OutputCache Duration="#seconds"
Table 2 defines the attribute values used above.
Table 2. OutputCache Directive Attributes: The table lists the attributes you can use to fine-tune the OutputCache directive.
||Required in every ASP.NET OutputCache directive. The number of seconds that the generated HTML will be cached.
||Defines if page output can be cached on a proxy or downstream client. Often omitted, so that caching is performed in the most efficient way. However, it is a good idea to include Location="Any" where you do not actually need to control the location.
||Used only in a User Control. Defines if multiple instances of the control will be served from one cached instance. If False (the default) multiple instances will be cached.
||Used only in a User Control. Declares the ID of ASP.NET controls that the cache will be varied by. For example: VaryByControl="MyTextBox;MyListBox" will cause a new version of the page to be cached for each differing value in the specified controls within the user control.
||Required in every ASP.NET OutputCache directive. Defines the names of the parameters that will be used to determine if a cached version of the page will be delivered. For example: VaryByParam="txtName;lstSelection" will cause a new version of the page to be cached for each differing value in the QueryString, Form, Cookies or ServerVariables collections that has one of the specified names.
||The name(s) of any HTTP Header(s) that are used to determine if a cached version of the page will be delivered. For example: VaryByHeader="USER-AGENT;SERVER;REMOTE-HOST"
||The special value "Browser" specifies that the cached version of the page will only be delivered to browsers with the same name and major version: VaryByCustom="Browser". Alternatively, you can place a custom function in global.asax that returns a String value based on some criteria you require, and the page will be varied for each client that causes a different value to be returned. For example: VaryByCustom="MyGlobalFunction" (see the following note for an example of this).
|Author's Note: Remember that ASP.NET varies the HTML it generates based on the browser type ("uplevel"meaning Internet Explorer 5 and above; or "downlevel"meaning all the rest). Therefore you might want to consider including the VaryByCustom="Browser" attribute if your page uses ASP.NET controls that can generate browser-specific output. Examples are controls such as Menu and Panel. The way that ASP.NET specifies style information for most other Web Forms controls also changes depending on the browser type, and may affect the appearance. You can experiment with these effects using a sample page from the book "Pro ASP.NET 1.1 Web Forms Techniques", see: http://www.daveandal.net/books/7868/control-compatibility/default.aspx . The asp-cache-by-level option shows how you can implement custom "vary by" functions. I've included the function code within the downloadable code samples for this article.
|Figure 3. Output Caching: The figure illustrates how the VaryByParam="*" attribute affects output caching.|
You can use the example page named output-cache-params.aspx
(see Figure 3
) to experiment with output caching. As the VaryByParam
attribute is set to "*"
, all the controls on the page will invalidate the cached HTML copy, and so changing the value in any one will cause the page to be re-executed (the time it was last executed is shown in the page). However, refreshing the page within the 10-second cache duration period specified in the OutputCache
directive does not cause it to be re-executed.
One interesting point demonstrated in this example is that using the ASP.NET auto-postback feature causes the page to be re-executed more than once. For example, changing the value in the second drop-down list initiates a postback automatically. And, because the values in the controls have changed, ASP.NET invalidates the cache and re-executes the page. However, if you then click the Refresh button, it executes the page againrather than serving it from the output cache. This is because the name and caption of the button are included in the parameters posted back to this page when the button is clicked, whereas they are not included when the postback is caused by the drop-down list that has the AutoPostback="True"