SharePoint WCM: flushing publishing pages from the cache

SharePoint WCM does a lot of caching. One of the things that is cached are the publishing pages. These pages are cached in the object cache. Sometimes there is a situation where you want to flush a publishing page from the cache. In my case I had to flush a publishing page from the cache in a http module. The cache id for this page is the server relative url without any characters after the url. For example: /Pages/MyFirstLittleWCMPage.aspx. Therefore the path must be "normalized" so additional "stuff" is removed. The NormalizeUrl() function does this job.

What I want to do to flush the page from the cache was:

CacheManager contextCacheManager = CacheManager.GetManager(SPContext.Current.Site);
contextCacheManager.ObjectFactory.FlushItem(NormalizeUrl(HttpContext.Current.Request.Path);

Sadly enough many interesting and powerful API classes are internal, and you need some reflection to be able to call them. Below the code I needed to write to accomplish the above. I can tell you it was a hell of a job to get to this code. That is why I share it, to give you some insight in the required magic called reflection.

Interesting components:

  1. I know that the assembly containing the required class is already loaded. I can do GetAssembly(typeof(PublishingPage)) to get the assembly. Will work on any class in the assembly.
  2. To invoke a member of a class you need the type of the class. Assembly.GetType("full.name.of.type") returns the type, also on internal classes.
  3. Given the type you can invoke members, where members can be static functions, properties or methods. You specify what to search for the member using BindingFlags. For example for a static public method specify BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod.
  4. Arguments to methods must be passed in an object array.

I hope the code below will give some insight in how to make the impossible possible.

/// <summary>
/// Flush the current publishing page from the object cache
/// </summary>
/// <remarks>
/// Reflection is used to get access to internal classes of the SharePoint framework
/// </remarks>
private void FlushCurrentPublishingPageFromCache()
{
    // We need to get access to the Microsoft.SharePoint.Publishing.dll assembly, PublisingPage is in there for sure
    Assembly microsoftSharePointPublishingAssembly = Assembly.GetAssembly(typeof(PublishingPage));
    Type cacheManagerType = microsoftSharePointPublishingAssembly.GetType("Microsoft.SharePoint.Publishing.CacheManager", true);
    object contextCacheManager = cacheManagerType.InvokeMember("GetManager", 
        BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, 
        null, null, new object[] { SPContext.Current.Site });            

    string cacheId = NormalizeUrl(HttpContext.Current.Request.Path);
    if (contextCacheManager != null)
    {
        object cachedObjectFactory = contextCacheManager.GetType().InvokeMember("ObjectFactory", 
            BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty, 
            null, contextCacheManager, new object[] {});
        cachedObjectFactory.GetType().InvokeMember("FlushItem", BindingFlags.Instance | 
            BindingFlags.Public | BindingFlags.InvokeMethod, 
            null, cachedObjectFactory, new object[] { cacheId });
    }
    else
    {
        Microsoft.Office.Server.Diagnostics.PortalLog.LogString("Unexpected error: DualLayout " +
"FlushCurrentPublishingPageFromCache: No CacheManager for page {0}"
, cacheId); } } /// <summary> /// Normalize url for cachId usage /// </summary> /// <remarks> /// This code is copied from: /// private static string NormalizeUrl(string url); /// Declaring Type: Microsoft.SharePoint.Publishing.CachedObjectFactory /// Assembly: Microsoft.SharePoint.Publishing, Version=12.0.0.0 /// </remarks> /// <param name="url">Url to normalize</param> /// <returns>The normalized url</returns> private static string NormalizeUrl(string url) { url = SPHttpUtility.UrlPathDecode(url, false); if (!string.IsNullOrEmpty(url)) { int length = url.IndexOf('?'); if (length >= 0) { url = url.Substring(0, length); } } else { return ""; } int index = url.IndexOf('#'); if (index >= 0) { url = url.Substring(0, index); } return url; }

2 Comments

  • Thanks for this code.
    Actually, In my case I need to expire all Cached pages from current user Session.
    I would appricate if you have any idea how to get those pages?

  • Way cool! Some very valid points! I appreciate you writing this write-up plus the rest of the site is also really good.

Comments have been disabled for this content.