Welcome to the navigation

Cupidatat pariatur, culpa ullamco ut laboris deserunt non dolor aliquip irure aliqua, nostrud ex do minim labore proident, eu velit veniam, nisi fugiat exercitation duis. Sed sunt ad do nulla quis qui in laboris sit cupidatat exercitation non lorem mollit commodo esse amet, aliqua, aute in sint est officia velit

Yeah, this will be replaced... But please enjoy the search!

Preloading EPiServer Content Providers

I am part of a project where we use lots and lots of EPiServer Content Providers to load data from miscellaneous datasources. Since we also are switching to EPiServer Find instead of SiteSeeker we discovered that the content providers doesn't get indexed unless you have preloaded them.

This may not be a common problem but since content providers are (out of the box) only indexed when running the scheduled job "EPiServer Find Content Indexing Job" the risk is the content never will show up in your EPiServer Find results.

Strategy

As always when preloading content in EPiServer you cannot preload content before everything has started, we have two options if we are to automate this

  1. Place the preload in an InitializableModule and add a timer that will wait until the HttpContext is loaded
  2. A scheduled job set to start on application start

Both of the above will solve the problem, in my scenario the InitializableModule will be perfect.

Code

ContentProvider registration

<episerver xmlns="http://EPiServer.Configuration.EPiServerSection">
  <contentProvider>
    <providers>
      <add entryPoint="34566" name="ImageProvider" type="Project.Classes.ContentProviders.ImageContentProvider, Project" />
      <add entryPoint="123" name="Press" filePath="http://rsssource.com/?p=pres" type="Project.Classes.ContentProviders.RSS.RSSPageProvider, Project" />
      <add entryPoint="553" name="Reports" filePath="http://rsssource.com/?p=reports" type="Project.Classes.ContentProviders.RSS.RSSPageProvider, Project" />
      <!-- 
      	More more more content providers
      	...
      -->
    </providers>
  </contentProvider>
</episerver>

 Nothing fancy, one specialized image provider and two rss providers.

InitializableModule

Since InitializableModules fire on application start and the EPiServer Context isn't available until after everything is started I've implemented Timers that are able to run the init async until they are loaded.

StartContentProviderLoadTimer<EpiPageTypes.ImageBank.ImageBankImage>("ImageProvider");

In order to start the loader I specify the pagetype to load and the providername.

The StartContentProviderLoadTimer method will start the timer and set the initial interval

private void StartContentProviderLoadTimer<T>(string providerName) where T : PageData
{
    var timer = new System.Timers.Timer();

    timer.Interval = 5000;
    timer.Elapsed += (sender, e) => Tick<T>(sender, e, providerName, timer);
    timer.Enabled = true;
}

Each timer is encapsulated in a Tick method

private void Tick<T>(object sender, ElapsedEventArgs e, string providerName, Timer timer) where T : PageData
{
    try
    {
        // prevent the timer from firing while we are fetching the data
        timer.Stop();

        var contentProviderManager = ServiceLocator.Current.GetInstance<IContentProviderManager>();
        var contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>();

        var provider = contentProviderManager.GetProvider(providerName);
        var children = contentLoader.GetChildren<T>(provider.EntryPoint.ToPageReference()).ToList();

        if (!children.Any())
        {
            // if no children was found let's try again
            timer.Start();
        }
        else
        {
            timer.Dispose();
        }
    }
    catch (Exception ex)
    {
        // oops we probably tried to fetch the data to early, lets try again
        timer.Start();
    }
}

This method will stop it self when everything is loaded.

The entire module

using System;
using System.Linq;
using EPiServer;
using EPiServer.Core;
using EPiServer.Framework;
using EPiServer.Framework.Initialization;
using log4net;
using System.Timers;

namespace Project.Business.Initialization
{
    /// <summary>
    /// Initialize ContentProviders
    /// </summary>
    [InitializableModule]
    public class ContentProviderInitialization : IInitializableModule
    {
        #region Local variables and members

        private bool _initialized;

        #endregion
 
        #region Methods

        /// <summary>
        /// Initialize
        /// </summary>
        /// <param name="context"></param>
        public void Initialize(InitializationEngine context)
        {
            if (this._initialized || context == null || context.HostType != HostType.WebApplication)
            {
                return;
            }

            // Start timers to load the content providers
            StartContentProviderLoadTimer<EpiPageTypes.ImageBank.ImageBankImage>("ImageProvider");
            StartContentProviderLoadTimer<EpiPageTypes.Press.PressRelease>("Press");
            StartContentProviderLoadTimer<EpiPageTypes.Press.Report>("Reports");

            this._initialized = true;
        }

        private void StartContentProviderLoadTimer<T>(string providerName) where T : PageData
        {
            var timer = new System.Timers.Timer();

            timer.Interval = 5000;
            timer.Elapsed += (sender, e) => Tick<T>(sender, e, providerName, timer);
            timer.Enabled = true;
        }

        private void Tick<T>(object sender, ElapsedEventArgs e, string providerName, Timer timer) where T : PageData
        {
            try
            {
                // prevent the timer from firing while we are fetching the data
                timer.Stop();

                var contentProviderManager = ServiceLocator.Current.GetInstance<IContentProviderManager>();
                var contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>();

                var provider = contentProviderManager.GetProvider(providerName);
                var children = contentLoader.GetChildren<T>(provider.EntryPoint.ToPageReference()).ToList();

                if (!children.Any())
                {
                    // if no children was found let's try again
                    timer.Start();
                }
                else
                {
                    timer.Dispose();
                }
            }
            catch (Exception ex)
            {
                // oops we probably tried to fetch the data to early, lets try again
                timer.Start();
            }
        }


        /// <summary>
        /// Uninitialize
        /// </summary>
        /// <param name="context"></param>
        public void Uninitialize(InitializationEngine context)
        {
            this._initialized = false;
        }

        public void Preload(string[] parameters)
        { }

        #endregion
    }
}

 

Cheers

Please note that this portal is a BETA site , I will continuously improve all functionality and performance during the coming weeks / the author