Using output cache substitutions

Output caching is a powerful tool for improving page performance. However, some pages have sections of content that need to be dynamic. Substitution macros allow you to add variables onto pages, which the system dynamically resolves even when loading the content from the output cache. By using substitutions, you can cache the output of pages, but still keep pieces of content that update according to the current time or other context information.

By default, the system does not provide any substitution macros. Developers need to define the substitutions according to the requirements of your website.

To insert output cache substitutions into page content, write expressions in format: {SubstitutionName}

Note: Substitution macros work even on pages that do not use output caching.

Tip: You do not need to create substitution macros for all types of dynamic content. By default, the output cache stores multiple versions of pages based on variables such as the user name, language and browser type. The system serves the appropriate cache version to visitors according to these variables.

See the Caching multiple output versions of pages section for more information.

Defining substitution macros

The system resolves substitution macros by calling the methods registered as handlers for the ResponseOutputFilter.OnResolveSubstitution event. To define substitutions, implement a handler method that converts individual substitution expressions into the appropriate results.

You need to register the event handlers at the beginning of the application’s life cycle. Choose one of the following options:

  • During the initialization process of the application itself — use the CMSModuleLoader partial class in the App_Code folder.
  • When initializing custom modules — override the OnInit method of the module class.

Example

The following example demonstrates how to create a substitution macro that displays the current time:

  1. Open your project in Visual Studio.

  2. Create a class in the App_Code folder (or CMSApp_AppCode -> Old_App_Code on web application projects).

  3. Edit the class and add the following references:

    
    
    
     using CMS.Base;
     using CMS.OutputFilter;
    
    
     
  4. Extend the CMSModuleLoader partial class.

  5. Create a new class inside CMSModuleLoader that inherits from CMSLoaderAttribute.

  6. Add the attribute defined by the internal class before the definition of the CMSModuleLoader partial class.

  7. Override the Init method inside the attribute class and assign a handler method to the ResponseOutputFilter.OnResolveSubstitution event.

    
    
    
     [SubstitutionLoader]
     public partial class CMSModuleLoader
     {
         /// <summary>
         /// Attribute class that registers substitution handlers.
         /// </summary>
         private class SubstitutionLoader : CMSLoaderAttribute
         {
             /// <summary>
             /// Called automatically when the application starts.
             /// </summary>
             public override void Init()
             {
                 // Assigns a handler method to the event that the system triggers when resolving substitution macros
                 ResponseOutputFilter.OnResolveSubstitution += ResolveCustomOutputSubstitutions;
             }
    
             /// <summary>
             /// Resolves output substitutions.
             /// </summary>
             /// <param name="sender">Sender object</param>
             /// <param name="e">Event argument object representing the substitution expression that is being resolved</param>
             private void ResolveCustomOutputSubstitutions(object sender, SubstitutionEventArgs e)
             {
                 // Checks that the substitution expression is not resolved yet
                 if (!e.Match)
                 {
                     // Branches according to the expression text of the substitution macro
                     // You can define any number of substitution macros by adding further cases
                     switch (e.Expression.ToLower())
                     {
                         // Handles the {~TimeStamp~} substitution macro
                         case "timestamp":
    
                             // Flags the substitution expression as resolved
                             e.Match = true;
    
                             // Returns the current time as the result of the substitution
                             e.Result = DateTime.Now.ToString();
                             break;
                     }
                 }
             }
    
         }
     }
    
    
     
  8. Save the class file. Build the solution on web application installations.

The system now recognizes the {TimeStamp} expression, and resolves it into the current time. You can place the expression directly into the text content of pages, or anywhere within the output code.

Output substitutions vs. Macro expressions

The functionality of output substitutions is similar to Kentico macro expressions. The main difference is that the system resolves substitutions even when loading pages from the output cache.

For example, the {% DateTime.Now %} macro also displays the current time. If you place both the macro and the sample {TimeStamp} substitution onto a page that uses output caching, you get the following results:

  • Macro - displays the time when the page was loaded for the first time and saved into the output cache.
  • Substitution - updates the time whenever the visitor refreshes the page, even if the system loads the content from the output cache.