As
time passes, the information in your data source may change. If your
code uses caching, you may remain unaware of the changes and continue
using out-of-date information from the cache. To help mitigate this
problem, ASP.NET supports cache dependencies.
Cache dependencies allow you to make a cached item dependent on another
resource, so that when that resource changes, the cached item is
removed automatically.
ASP.NET includes three types of dependencies:
Dependencies on files or folders
Dependencies on other cached items
Dependencies on a database query
You'll see all these types of dependencies in the following section.
1. File Dependencies
To use a cache dependency, you
need to create a CacheDependency object. You then need to supply the
CacheDependency object when you add the dependent cached item.
For example, the following
code creates a CacheDependency that depends on an XML file named
ProductList.xml. When the XML file is changed, the CacheDependency will
be invalidated, and the dependent cached item will be evicted from the
cache immediately.
' Create a dependency for the ProductList.xml file.
Dim prodDependency As New CacheDependency( _
Server.MapPath("ProductList.xml"))
' Add a cache item that will be dependent on this file.
Cache.Insert("ProductInfo", prodInfo, prodDependency)
Monitoring
begins as soon as the CacheDependency object is created. If the XML file
changes before you have added the dependent item to the cache, the item
will expire immediately as soon as it's added.
Figure 1
shows a simple test page that is included with the samples for thisarticle . It sets up a dependency, modifies the file, and allows you to
verify that the cached item has been dropped from the cache.
The
CacheDependency object provides several constructors. You've already
seen how it can make a dependency based on a file by using the file name
constructor. You can also specify a directory that needs to be
monitored for changes, or you can use a constructor that accepts an
array of strings that represent multiple files and directories.
2. Cache Item Dependencies
The CacheDependency class provides another constructor that accepts an array of file names and
an array of cache keys. Using the array of cache keys, you can create a
cached item that's dependent on another item in the cache. (If you
don't want to use file dependencies at all, you simply supply a null
reference (Nothing) for the first parameter.)
Here's an example that makes one item dependent on another cached item, without using file dependencies:
Cache("Key1") = "Cache Item 1"
' Make Cache("Key2") dependent on Cache("Key1").
Dim dependencyKey(0) As String
dependencyKey(0) = "Key1"
Dim dependency As New CacheDependency(Nothing, dependencyKey)
Cache.Insert("Key2", "Cache Item 2", dependency)
Now, when the first cached
item changes or is removed from the cache, the second cached item will
automatically be dropped from the cache as well.
3. SQL Server Cache Dependencies
A more complex kind of cache
dependency is the SQL Server cache dependency. In a nutshell, SQL cache
dependencies provide the ability to automatically invalidate a cached
data object (such as a DataSet) when the related data is modified in the
database.
Although this
feature is technically supported in SQL Server 2000, it's a better idea
to use SQL Server 2005 or later. That's because these versions of SQL
Server have a built-in notification system, which makes tasks like these
much more efficient.
To understand how database
dependencies work, you first need to know a bit about SQL Server's
built-in messaging system, which is called the Service Broker. The Service Broker manages queues, which are database objects that have the same standing as tables, stored procedures, or views.
Thanks to queues, you can
instruct SQL Server to send notifications for specific events using the
CREATE EVENT NOTIFICATION command. But ASP.NET offers a more convenient,
higher-level model—you register a query, and ASP.NET automatically
instructs SQL Server to send notifications for any operations that would
affect the results of that query. Every time you perform an operation,
SQL Server determines whether your operation affects a registered
command. If it does, SQL Server sends a notification message and stops
the notification process. Figure 2 shows an overview of how this cache invalidation system works.
Notifications work with
SELECT queries and stored procedures. However, some restrictions exist
for the SELECT syntax you can use. To properly support notifications,
your command must adhere to the following rules:
You must fully qualify table names in the form [Owner].table, as in dbo.Employees (not just Employees).
Your query cannot use an aggregate function, such as COUNT(), MAX(), MIN(), or AVERAGE().
You
cannot select all columns with the wildcard * (as in SELECT * FROM
Employees). Instead, you must specifically name each column so that SQL
Server can properly track changes that do and do not affect the results
of your query.
Here's an acceptable command:
SELECT EmployeeID, FirstName, LastName, City FROM dbo.Employees
These are the most important
rules, but the SQL Server Books Online has a lengthy list of caveats and
exceptions. If you break one of these rules, you won't receive an
error. However, the notification message will be sent as soon as you
register the command, and the cached item will be invalidated
immediately.
3.1. Enabling the Service Broker
SQL Server is often
installed with carefully locked-down settings for optimum security. To
use SQL Server notifications, you may have to enable features that are
currently switched off.
First, you need to enable
the Service Broker, which watches for changes in the database and
delivers the notifications to the appropriate queue. The Service Broker
must be specifically enabled for each database that you want to use with
cache dependencies.
If the Service Broker
isn't currently enabled for your database (or if you're just not sure),
there's an easy solution. First, launch the Visual Studio 2010 Command
Prompt window (click the Start button and choose All Programs => Microsoft Visual Studio 2010 => Visual Studio Tools =>
Visual Studio Command Prompt). Then, run the SqlCmd.exe command-line
utility, specifying the –S parameter and the name of your server. Here's
an example:
SqlCmd -S localhost\SQLEXPRESS
This connects to SQL Server
Express on the current computer. If you're using the full version of SQL
Server, you won't need to supply the instance name (you can use just
localhost instead of localhost\SQLEXPRESS). If your database is
installed on another server, use its computer name instead of localhost.
The SqlCmd.exe utility provides a command prompt where you can enter SQL commands. Use it to enter the following SQL statements:
USE Northwind
ALTER DATABASE Northwind SET ENABLE_BROKER
GO
Of course, if you want to
enable the Service Broker for a different database (other than
Northwind), you can modify this SQL accordingly. You can enable the
Service Broker for as many databases as you'd like.
Once you're finished, type quit to exit the SqlCmd tool.
3.2. Initializing the Caching Service
Before you can use SQL cache
dependencies with SQL Server, you need to call the shared
SqlDependency.Start() method. This initializes the listening service on
the web server.
Dim connectionString As String = _
WebConfigurationManager.ConnectionStrings("Northwind").ConnectionString
SqlDependency.Start(connectionString)
You need to call the Start()
method only once over the lifetime of your web application, so it often
makes sense to place the call in the Application_Start() method of the
global.asax file so it's triggered automatically. It's safe to call the
Start() method even if the listener is already started, as this won't
cause an error. You can also use the Stop() method to halt the listener.
3.3. Creating the Cache Dependency
When you create the
dependency object, you need to supply the command that you're using to
retrieve your data. That way, SQL Server knows what range of records you
want to monitor.
To specify the command, you
create the SqlCacheDependency using the constructor that accepts a
SqlCommand object. Here's an example:
' Create the ADO.NET objects.
Dim con As New SqlConnection(connectionString)
Dim query As String = _
"SELECT EmployeeID, FirstName, LastName, City FROM dbo.Employees"
Dim cmd As New SqlCommand(query, con)
Dim adapter As New SqlDataAdapter(cmd)
' Fill the DataSet.
Dim ds As New DataSet()
adapter.Fill(ds, "Employees")
' Create the dependency.
Dim empDependency As New SqlCacheDependency(cmd)
' Add a cache item that will be invalidated if one of its records changes
' (or a new record is added in the same range).
Cache.Insert("Employees", ds, empDependency)
Now, when you change
the data in the table, the notification will be delivered, and the item
will be removed from the cache. The next time you create the DataSet,
you'll need to add it back to the cache with a new SqlCacheDependency.
If your cached item never
expires, the ASP.NET polling service is not receiving the invalidation
message. This has several possible causes. The most common is that your
database server doesn't have the common language runtime enabled. The
procedure that sends notification messages is a .NET procedure, so it
requires this support.
To enable CLR support, fire up
the Visual Studio Command Prompt window, and run the SqlCmd.exe
command-line utility. Here's how to do it for SQL Server Express:
SqlCmd -S localhost\SQLEXPRESS
Now enter the following SQL statements:
EXEC sp_configure 'show advanced options', '1' GO RECONFIGURE GO EXEC sp_configure 'clr enabled', 1 GO RECONFIGURE GO
Then type quit to exit the SqlCmd tool.
On the other hand, if your cached item expires immediately,
the most likely problem is that you've broken one of the rules for
writing commands that work with notifications, as described earlier.
|