In my custom SiteMapProvider I use a file dependency and touch the file whenever I want to clear the cache.
However an alternate approach is needed in a web farm environment. In that case I might have a dependency check by looking up a record in the db and then touch the file if its expired in the db, that way each node of the web farm will touch its own copy of the dependency file.
Code like this works fine on a single server:
// using a text file dependency
public override SiteMapNode BuildSiteMap()
{
if (rootNode != null)
{
return rootNode;
}
lock (objLock)
{
menuPages = SiteUtils.GetMenuPages();
if (menuPages != null)
{
rootNode = CreateRootNode();
int i = 0;
foreach (PageSettings page in menuPages)
{
if (page.ParentID <= -1)
{
SiteMapNode node = CreateSiteMapNode(page, i);
AddNode(node, GetParentNode(page));
if ((page.UseUrl) && (page.Url.StartsWith("http")))
{
node.Url = page.Url;
}
}
i += 1;
}
}
}
CacheDependency cacheDependency = new CacheDependency(
SiteUtils.GetPathToSiteMapCacheDependencyFile());
HttpRuntime.Cache.Insert(
cacheDependencyName,
new object(),
cacheDependency,
Cache.NoAbsoluteExpiration,
Cache.NoSlidingExpiration,
CacheItemPriority.Normal,
new CacheItemRemovedCallback(OnSiteMapChanged));
return rootNode;
}
protected override void Clear()
{
lock (objLock)
{
this.rootNode = null;
this.nodes.Clear();
base.Clear();
}
}
public void OnSiteMapChanged(string key, object item, CacheItemRemovedReason reason)
{
Clear();
}
After changing some thing I call:
SiteUtils.ResetSiteMapCache();
public static void ResetSiteMapCache()
{
TouchMenuCacheDependencyFile();
String pathToCacheDependencyFile = GetPathToSiteMapCacheDependencyFile();
if (pathToCacheDependencyFile != null)
{
if (File.Exists(pathToCacheDependencyFile))
{
File.SetLastWriteTimeUtc(pathToCacheDependencyFile, DateTime.Now);
}
else
{
StreamWriter streamWriter = File.CreateText(pathToCacheDependencyFile);
streamWriter.Close();
}
}
}
Hope it helps,
Joe