Programming Microsoft ASP.NET 4 - Dino Esposito [386]
var dep = sender as XmlDataCacheDependency;
// Check for changes and notify the base class if any are found
var value = CheckFile();
if (!String.Equals(_currentValue, value))
dep.NotifyDependencyChanged(dep, EventArgs.Empty);
}
public String CheckFile()
{
// Evaluates the XPath expression in the file
var doc = new XmlDocument();
doc.Load(_fileName);
var node = doc.SelectSingleNode(_xpathExpression);
return node.InnerText;
}
protected override void DependencyDispose()
{
// Kill the timer and then proceed as usual
_timer.Dispose();
_timer = null;
base.DependencyDispose();
}
}
When the cache dependency is created, the file is parsed and the value of the XPath expression is stored in an internal member. At the same time, a timer is started to repeat the operation at regular intervals. The return value is compared to the value stored in the constructor code. If the two are different, the NotifyDependencyChanged method is invoked on the base CacheDependency class to invalidate the linked content in the ASP.NET Cache.
Testing the Custom Dependency
How can you use this dependency class in a Web application? It’s as easy as it seems—you just use it in any scenario where a CacheDependency object is acceptable. For example, you create an instance of the class in the Page_Load event and pass it to the Cache.Insert method:
protected const String CacheKeyName = "MyData";
protected void Page_Load(Object sender, EventArgs e)
{
if (!IsPostBack)
{
// Create a new entry with a custom dependency (and poll every 10 seconds)
var dependency = new XmlDataCacheDependency(
Server.MapPath("employees.xml"),
"MyDataSet/NorthwindEmployees/Employee[employeeid=3]/lastname",
10);
Cache.Insert(CacheKeyName, dependency.CurrentValue, dependency);
}
// Refresh the UI
Msg.Text = Display();
}
You write the rest of the page as usual, paying close attention to accessing the specified Cache key. The reason for this is that because of the dependency, the key could be null. Here’s an example:
protected String Display()
{
var o = Cache[CacheKeyName];
return o ?? "[No data available--dependency broken]";
}
The XmlDataCacheDependency object allows you to control changes that occur on a file and decide which are relevant and might require you to invalidate the cache. The sample dependency uses XPath expressions to identify a subset of nodes to monitor for changes.
Note
I decided to implement polling in this sample custom dependency because polling is a pretty common, often mandatory, approach for custom dependencies. However, in this particular case polling is not the best option. You could set a FileSystemWatcher object and watch for changes to the XML file. When a change is detected, you execute the XPath expression to see whether the change is relevant for the dependency. Using an asynchronous notifier, if one is available, results in much better performance.
SQL Server Cache Dependency
Many ASP.NET applications query some data out of a database, cache it, and then manage to serve a report to the user. Binding the report to the data in the cache will both reduce the time required to load each report and minimize traffic to and from the database. What’s the problem, then? With a report built from the cache, if the data displayed is modified in the database, users will get an out-of-date report. If updates occur at a known or predictable rate, you can set an appropriate duration for the cached data so that the report gets automatically refreshed at regular intervals. However, this contrivance just doesn’t work if serving live data is critical for the application or if changes occur rarely and, worse yet, randomly. In the latter case, whatever duration you set might hit the application in one way or the other. A too-long duration creates the risk of serving outdated reports to end users which, in some cases, could undermine the business; a too-short duration burdens the application with unnecessary queries.
A database dependency is a special case of custom dependency that consists of the