Displaying Cached XML Data in a DataGrid, and Intelligently Refreshing the Data
By Scott Mitchell
Introduction
One of the coolest and most powerful aspects of the .NET Framework is the built-in caching APIs.
These caching APIs make caching through ASP.NET Web pages painfully simple. When discussing caching
with regards to an ASP.NET Web page there are two classes of caching: output caching and data caching.
Output caching is the caching of the HTML output of an ASP.NET Web page. This cached HTML is stored
on disk and is cached for a specific period of time (say 30 seconds). With data caching, programming
objects (such as integers, DataSets, ArrayLists, etc.) are stored in an in-memory cache. Items stored in
the data cache can have their expiration specified by a sliding time scale, an absolute time scale, or
via a cache dependency.
A cache dependency can be a file or a directory in the file system, or a key in the cache.
When the dependency changes - the file is modified, the directory is altered, or the cache item is
altered or evicted - the item with the cache dependency is evicted from the cache.
In this article we will first look at caching a DataSet in the data cache, and binding this cached
DataSet to a DataGrid. Specifically, this cached DataSet will contain the data from an XML file
residing on the Web server's file system. A cache dependency will then be used so that the cached
DataSet is evicted from the data cache when the XML file's contents are altered. In essence we will
be examining how to create a DataGrid that displays cached data, but whose data is never stale! (That is,
whenever the data changes, the cached version of the data is updated as well.)
Displaying XML Data in a DataGrid
In a previous 4Guys article, XML, the DataSet, and a DataGrid,
I looked at how to display XML data in a DataGrid. The first step in accomplishing this is reading the
XML data into a DataSet. Once we have the data in a DataSet, we can simply bind the DataSet to the
DataGrid's DataSource and then call the DataGrid's DataBind() method.
For More Information on the DataGrid...
If you're not familiar with the DataGrid Web control I would encourage you to first read an earlier
article of mine: An Extensive Examination of the DataGrid Web Control.
It is a multi-part series that covers the ins and outs of the DataGrid Web control.
Reading XML data into a DataSet is incredibly simple thanks to the DataSet's ReadXml() method.
This method takes in a string parameter as the filename to the XML file.
'Create a DataSet
Dim myDataSet as New DataSet()
'Read the XML file using the ReadXml method
myDataSet.ReadXml(Server.MapPath("books.xml"))
In the above example, the Server.MapPath() method is used to obtain the full physical path
to the books.xml file. (For more information on Server.MapPath() be sure to
read: Using Server.MapPath().)
Once we have a DataSet populated with the results of an XML file, we can display the DataSet in a DataGrid
Web control using the following two lines of code:
The following code snippet shows the complete code for an ASP.NET Web page that displays the contents
of the file books.xml file.
<%@ Import Namespace="System.Data" %>
<script runat="server">
sub Page_Load(sender as Object, e as EventArgs)
Dim myDataSet as New DataSet()
myDataSet.ReadXml(Server.MapPath("books.xml"))
dgBooks.DataSource = myDataSet
dgBooks.DataBind()
end sub
</script>
<asp:datagrid id="dgBooks" runat="server" />
Caching the DataSet
Now that we have seen how to populate a DataSet with the contents of an XML file, let's turn our attention
to caching this DataSet in the data cache. This article does not intend to be a thorough examination of
the data cache, as there are already a number of articles already on this topic. If you are interested
in learning more about caching options available with ASP.NET, I'd highly recommend Scott McFarland's
Caching with ASP.NET article.
To bind the DataGrid to a cached DataSet, we first must check that the DataSet is in the cache. After
all, it may have never been added, or it may have been evicted from the cache for one of any number of
reasons. To determine if an item is in the cache, we can simply check to see if the item is Nothing.
(In C# you'd check to see if the cache item equaled null.) The following code checks to
see if the cache item authorsDS is in the cache or not.
If Cache("authorsDS") is Nothing then
'There is no authorsDS item in the Cache...
End If
In the case that there is no authorsDS item in the cache, we want to add it. This can be
accomplished using the Cache.Insert() method. Before adding the DataSet to the cache, though,
we must populate it from the XML file, as we saw earlier. This code, then, looks like:
If Cache("authorsDS") is Nothing then
'There is no authorsDS item in the Cache...
Dim myDataSet as New DataSet()
myDataSet.ReadXml(Server.MapPath("books.xml"))
'Add it to the Cache
Cache.Insert("authorsDS", myDataSet)
End If
When the end of this conditional statement is reached, we know for certain that there exists a cache item
named authorsDS (that should have a cached DataSet populated from the books.xml
file). If, when the page is visited, the cache item does not exist, it is created and added to the cache.
If, on the other hand, when the page is visited the cache item does exist, then we don't need to populate
the DataSet with the XML file contents, since the contents are already populated in a cached DataSet.
All that's left to do here is bind the cached DataSet to a DataGrid, which is accomplished using the
following complete code example:
<%@ Import Namespace="System.Data" %>
<script runat="server">
sub Page_Load(sender as Object, e as EventArgs)
If Cache("authorsDS") is Nothing then
'There is no authorsDS item in the Cache...
Dim myDataSet as New DataSet()
myDataSet.ReadXml(Server.MapPath("books.xml"))
'Add it to the Cache
Cache.Insert("authorsDS", myDataSet)
lblMessage.Text = "DataSet populated from XML file..."
End If
'Set the DataGrid's DataSource to the cached DataSet
dgBooks.DataSource = Cache("authorsDS")
dgBooks.DataBind()
end sub
</script>
<asp:datagrid id="dgBooks" runat="server" />
<asp:label id="lblMessage" runat="server" Font-Italic="True" />
Notice that a Label Web control was added (lblMessage) that is used to display a helpful
debugging message when the DataSet was not found in the cache. If you try this example on your own
computer, you'll find that the first time you visit the page it indicates that the DataSet was populated
from the XML file; however, if you reload the page, this message does not appear, since the DataSet
is cached, and the DataGrid is using the cached version.
Caching the Data in a More Intelligent Manner
One downside, though, is that when using cached data, the data may become stale. For example, if you
visit the page with the cached DataSet once, to load the DataSet into the cache, and then the contents
of the XML file are changed, the cached DataSet page will continue to show the cached data. That is,
your users will see the data prior to the change in the XML file.
Ideally we'd like to be able to have the cached data reside in the cache so long as the underlying XML
data doesn't change. To put it another way, as soon as the XML file's contents change, we want the
cached DataSet to be evicted from the cache. We can accomplish this goal using cache dependencies.
As mentioned in the Introduction, cache dependencies can be a file (or files) or a directory in the
file system, or a key in the cache. When the dependency changes - the file is modified, the
directory is altered, or the cache item is altered or evicted - the item with the cache dependency is
evicted from the cache. This sounds exactly like what we need! Essentially, the books.xml
file will become the cache dependency for the cached DataSet. This will cause the cached DataSet to be
evicted from the cache whenever the books.xml file is altered.
To add an item to the cache with a cache dependency, we simply need to use an alternate version of the
Cache.Insert() method. Specifically, we need to pass in a CacheDependency
object instance as the third parameter to Cache.Insert(). This requires just a small edit to
our earlier code sample, which now gives us:
<%@ Import Namespace="System.Data" %>
<script runat="server">
sub Page_Load(sender as Object, e as EventArgs)
If Cache("authorsDS") is Nothing then
'There is no authorsDS item in the Cache...
Dim myDataSet as New DataSet()
myDataSet.ReadXml(Server.MapPath("books.xml"))
'Add it to the Cache
Cache.Insert("authorsDS", myDataSet, _
New CacheDependency(Server.MapPath("books.xml")))
lblMessage.Text = "DataSet populated from XML file..."
End If
'Set the DataGrid's DataSource to the cached DataSet
dgBooks.DataSource = Cache("authorsDS")
dgBooks.DataBind()
end sub
</script>
<asp:datagrid id="dgBooks" runat="server" />
<asp:label id="lblMessage" runat="server" Font-Italic="True" />
And that's all there is to it! Now, when you try the above code on your computer you'll find that
when you first visit the page the message "DataSet populated from XML file..." appears, but on subsequent
visits, the message is no longer there. The cool part - if you edit the books.xml file (say
add a new book element), and then revisit the page, you'll note that the DataGrid is displaying
the new book and that the "DataSet populated from XML file..." message has appeared again. This is because
the cached DataSet is evicted from the cache whenever edits occur to the underlying XML file.
Conclusion
In this article we saw how to accomplish a number of things, which include: populating a DataSet with
data from an XML file; caching a DataSet in the data cache; and using cache dependencies to provide
on-demand cache eviction when the underlying XML data is altered. This approach has the performance
benefits inherent with caching, and removes one of the primary disadvantages of caching: stale data.