Many large websites have huge amounts of data that are displayed to users frequently, but some elements do not change very often, such as the primary navigation/menu of a site. A site menu may contain hundreds of pages, and can take a significant amount of processing time to generate. If the content of the menu does not change often, this processing time can be drastically reduced by caching the data. However, when the content is changed (e.g. if a page is added or removed from the site menu), the change may not appear for hours or even days if the data is cached. A way to resolve this is to add custom handlers to the publishing pipelines in your CMS (e.g.: Sitecore) to clear the cache when changes are made to the content within it.
Sitecore provides the ability to add custom handlers to various events that are involved with managing content, such as publishing and saving items. In addition to clearing cache, custom Sitecore event handlers can be used to execute any desired function in response to a Sitecore event. However, it is important to understand the different types of EventArgs objects that are passed by different Sitecore events. If a site implements a CM-CD environment, the event that is triggered on publish in CM is “publish:end” whereas the event triggered in CD is “publish:end:remote.” The former passes SitecoreEventArgs while the latter passes PublishEndRemoteEventArgs, so these two events must be handled accordingly, accounting for the different properties that the EventArgs have in each case.
Sitecore has a useful feature where custom methods can be triggered on various events, such as item creation, saving, publishing, deletion, etc. These custom handlers are configured in the webconfig in Sitecore 7, or in App_Config\Sitecore.config in Sitecore 8, like so:
In the example above, a custom handler is being added to the publish:end event, which will trigger when an item has finished publishing. The “type” specifies the class (“Velir.Data.CustomSitecore.Handlers.ItemPublished”), and “method” specifies the method in the class (“RefreshCachedNavigation”).
In my case, I used the “publish:end” event to clear the cache whenever an item was published. My site had an extensive navigation menu that is cached to improve load time, but the caching resulted in changes to the navigation menu not showing up until the app pool was recycled. To resolve this, I added a custom handler to “publish:end” so that the cache will be cleared every time a navigation item is published.
I ran into a problem, though. If I was simply clearing the cache on every item publish, the method would be straightforward. However, I only wanted to clear the cache when the navigation menu was altered, and in order to determine that, I needed to check the item that had been published. This was the method I implemented:
This worked just fine for the local publishing event. However, because our production environment uses a CD/CM server configuration, I also needed to add my publishing handler to the “publish:end:remote” event.
This is where the problem occurred. When we tried to publish on the CM environment, we found that the cache wasn’t clearing on the CD environment. Some debugging uncovered the exception that was being thrown:
It turns out that “publish:end” and “publish:end:remote” pass different types of EventArgs; “publish:end” passes SitecoreEventArgs while “publish:end:remote” passes PublishEndRemoteEventArgs. This caused an error to occur in Event.ExtractParameter(eventArgs, 0), which attempts to cast eventArgs as type SitecoreEventArgs.
The question remained of how to retrieve the item in the remote case. SitecoreEventArgs has a parameter of type object named Parameters, which is what Event.ExtractParameter() pulls the item from. PublishEndRemoteEventArgs does not have Parameters. It does, however, have a Guid parameter named RootItemId. Using Sitecore.Data.Database.GetItem() we were then able to get the item that was published, and proceed through the rest of the method.
It’s tricky to work out problems with remote pipeline methods when you cannot debug locally (since my local environment is not CD/CM). We worked through it by using a ton of Logging, and were able to figure out what was going on through extensive trial and error of changing the code, publishing items, and then checking the logs to see what errors (if any) were returned. It is important to note that different sitecore events pass completely different types of EventArgs which cannot be used interchangeably, so a method that works for one handler may not necessarily work for another handler if it pulls information from the EventArgs. Event.ExtractParameter() only works on type SitecoreEventArgs. SitecoreEventArgs includes the parameters Parameters (object), EventName (string), and Result (EventResult); PublishEndRemoteEventArgs includes none of these, but does include RootItemId (Guid). Lesson learned: Always research what type of EventArgs any Sitecore event passes when adding a custom handler to that event.