One feature that isn't availible in the source for the XmlSiteMapProvider we released is the ability to update when the web.sitemap file has been updated. The reason we couldn't provide this is that the mechanism we use inside XmlSiteMapProvider relies on some internal methods which map down to native calls. Now, this can easily be done with cache dependencies but we chose not to modify the code that way so that users would see how the providers were actually coded and not some version designed to mimic the providers.
However, since this is clearly a nice feature and relatively easy to implement, we elected to blog on it after-the-fact. Here you go, there's only a few line changes:
First, you need to hook up the handler. It's actually only a few lines of code. In the GetConfigDocument() function, there's a few lines like this:
The new version is:
However, the entire function needs to be reorganized slightly to ensure this code always runs. The normal behavior is to cache the results an exit immediately, in the first version of this post, this behavior caused this code to only get executed the first time. Here's the new version:
private XmlDocument GetConfigDocument() {
if (_document == null)
{
if (!_initialized)
throw new InvalidOperationException(
SR.GetString(SR.XmlSiteMapProvider_Not_Initialized));
}
// Do the error checking here
if (_virtualPath == null)
throw new ArgumentException(
SR.GetString(SR.XmlSiteMapProvider_missing_siteMapFile, _siteMapFileAttribute));
if (!Path.GetExtension(_virtualPath).Equals(_xmlSiteMapFileExtension, StringComparison.OrdinalIgnoreCase))
SR.GetString(SR.XmlSiteMapProvider_Invalid_Extension, _virtualPath));
// Ensure the appdomain virtualpath has proper trailing slash
_normalizedVirtualPath =
VirtualPathUtility.Combine(AppDomainAppVirtualPathWithTrailingSlash, _virtualPath);
// Make sure the file exists
CheckSiteMapFileExists();
_parentSiteMapFileCollection = new StringCollection();
XmlSiteMapProvider xmlParentProvider = ParentProvider as XmlSiteMapProvider;
if (xmlParentProvider != null && xmlParentProvider._parentSiteMapFileCollection != null)
if (xmlParentProvider._parentSiteMapFileCollection.Contains(_normalizedVirtualPath))
SR.GetString(SR.XmlSiteMapProvider_FileName_already_in_use, _virtualPath));
// Copy the sitemapfiles in used from parent provider to current provider.
foreach (string filename in xmlParentProvider._parentSiteMapFileCollection)
_parentSiteMapFileCollection.Add(filename);
// Add current sitemap file to the collection
_parentSiteMapFileCollection.Add(_normalizedVirtualPath);
_filename = HostingEnvironment.MapPath(_normalizedVirtualPath);
_document = new ConfigXmlDocument();
if (!String.IsNullOrEmpty(_filename))
CacheItemRemovedCallback _handler = new CacheItemRemovedCallback(OnConfigFileChange);
CacheDependency _dep = new CacheDependency(_filename);
HttpContext.Current.Cache.Add(GetHashCode().ToString(), "", _dep,
Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration,
CacheItemPriority.Normal, _handler);
ResourceKey = (new FileInfo(_filename)).Name;
return _document;
Next, look for the OnConfigFileChange() handler:
And change it to this (only the function signature and the recursive call changes):
And that's all there is to it. The provider will now update with file changes (or any other time the key is forced out of cache).