3. Caching to Provide Multiple Views
The next example shows a more
interesting demonstration of caching, which includes retrieving
information from a database and storing it in a DataSet. This
information is then displayed in a GridView. However, the output for the
web page can't be efficiently cached because the user is given the
chance to customize the display by hiding any combination of columns.
Note that even with just ten columns, you can construct more than a
thousand different possible views by hiding and showing various columns.
These are far too many columns for successful output caching!
Figure 2 shows the page. To hide a column, you simply click the corresponding check box.
Instead of attempting to use
output caching, this page caches the DataSet object that holds the full
information. This DataSet is constructed in the dedicated
RetrieveData() function shown here. (In order to use this code as
written, you must import the System.Data, System.Data.SqlClient, and
System.Web.Configuration namespaces in the web page.)
Private Function RetrieveData() As DataSet
Dim connectionString As String = _
WebConfigurationManager.ConnectionStrings("Northwind").ConnectionString
Dim SQLSelect As String = "SELECT * FROM Customers"
Dim con As New SqlConnection(connectionString)
Dim cmd As New SqlCommand(SQLSelect, con)
Dim adapter As New SqlDataAdapter(cmd)
Dim ds As New DataSet()
Try
con.Open()
adapter.Fill(ds, "Customers")
Finally
con.Close()
End Try
Return ds
End Function
The RetrieveData()
method handles the work of contacting the database and creating the
DataSet. You need another level of code that checks to see whether the
DataSet is in the cache and adds it when needed. The best way to write
this code is to add another method. This method is called GetDataSet().
The GetDataSet() method
attempts to retrieve the DataSet from the cache. If it cannot retrieve
the DataSet, it calls the RetrieveData() method and then adds the
DataSet to the cache. It also reports on the page whether the DataSet
was retrieved from the cache or generated manually.
Private Function GetDataSet() As DataSet
Dim ds As DataSet = CType(Cache("Customers"), DataSet)
' Contact the database if necessary.
If ds Is Nothing Then
ds = RetrieveData()
Cache.Insert("Customers", ds, Nothing, _
DateTime.MaxValue, TimeSpan.FromMinutes(2))
lblCacheStatus.Text = "Created and added to cache."
Else
lblCacheStatus.Text = "Retrieved from cache."
End If
Return ds
End Function
The advantage of this
approach is that you can call GetDataSet() in any event handler in your
web page code to get the DataSet when you need it. You don't need to
worry about checking the cache first and calling RetrieveDataSet() when
needed—instead, GetDataSet() handles the whole process transparently.
This two-step approach
(with one method that creates the data object you need and another that
manages cache) is a common, time-tested design. It's always a good
strategy to ensure that you deal with the cache consistently. If you
want to use the same cached object in multiple web pages, you can take
this design one step further by moving the GetDataSet() and
RetrieveDataSet() methods into a separate class. In this case, you'd
probably make the RetrieveDataSet() method private and the GetDataSet()
method public—that way, web pages can request the DataSet whenever they
need it but don't determine when to contact the database or whether
you've implemented caching.
|
|
When the page is first loaded,
it calls GetDataSet() to retrieve the DataSet. It then gets the
DataTable with the customer records and binds the DataTable.Columns
collection to a CheckBoxList control named chkColumns:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) _
Handles Me.Load
If Not Me.IsPostBack Then
Dim ds As DataSet = GetDataSet()
chkColumns.DataSource = ds.Tables("Customers").Columns
chkColumns.DataTextField = "ColumnName"
chkColumns.DataBind()
End If
End Sub
The DataTable.Columns collection holds one DataColumn object for each
column in the DataTable. Each DataColumn specifies details such as data
type and column name. In this example, the DataColumn.ColumnName
property is used to display the name of each column (as configured by
the DataTextField property of the CheckBoxList control).
Every time the Filter button
is clicked, the page calls GetDataSet() to retrieve the DataSet. To
provide the configurable grid, the code loops through the DataTable,
removing all the columns that the user has selected to hide. The code
then binds the data by calling GridView.DataBind().
The full code for the Filter button is as follows:
Protected Sub cmdFilter_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles cmdFilter.Click
Dim ds As DataSet = GetDataSet()
' Copy the DataSet so you can remove columns without
' changing the cached item.
ds = ds.Copy()
For Each item As ListItem in chkColumns.Items
If item.Selected Then
ds.Tables(0).Columns.Remove(item.Text)
End If
Next
gridCustomers.DataSource = ds.Tables(0)
gridCustomers.DataBind()
End Sub
This example demonstrates an
important fact about the cache. When you retrieve an item, you actually
retrieve a reference to the cached object. If you modify that object,
you're actually modifying the cached item as well. For the page to be
able to delete columns without affecting the cached copy of the DataSet,
the code needs to create a duplicate copy before performing the
operations using the DataSet.Copy() method.