HomeDev GuideAPI Reference
Dev GuideAPI ReferenceUser GuideGitHubNuGetDev CommunitySubmit a ticketLog In
GitHubNuGetDev CommunitySubmit a ticket

Initialize CMS

Describes the initialization system in the Optimizely platform, which is used by Optimizely Content Management System (CMS 11), Optimizely Customized Commerce version 13, and third-party and customized modules used with Optimizely products.

This topic describes the initialization system in the Optimizely platform, which is used by Optimizely Content Management System (CMS), Optimizely Customized Commerce, and third-party and customized modules used with Optimizely products. You can develop your own initialization module.

The CMS initialization system is designed and implemented with the purpose of being the sole initialization mechanism to use for CMS internal code and third-party and custom modules. The initialization system comprises the following:

  • A discovery mechanism to determine which modules should be part of the initialization process.
  • A dependency sorting algorithm that decides the order of execution.
  • An execution engine that executes the modules.
  • Handling of re-execution of initialization modules (by hooking into ASP.NET) in the occurence of exceptions during startup.
  • The  EPiServer.Framework.Initialization namespace, in the EPiServer.Framework assembly.

Discovery mechanism

You can locate the initialization modules in an CMS application through scanning assemblies.

The scanning is primarily using an attribute-based discovery system. CMS scans all assemblies loaded in the current AppDomain for initialization modules. This includes all assemblies in the bin folder, because by default the ASP.NET config section <system.web> / / contains the configuration , which causes the ASP.NET build system to pull-in all assemblies in the bin folder.

To have a discoverable initialization module, you need to add the \[InitializableModule\] or `[ModuleDependency(...)]` attribute to your class. The class also needs to implement the IInitializableModule interface.

The IInitializableHttpModule and IConfigurableModule interfaces extend the IInitializableModule. To speed up the initialization process, the assembly scanning process supports some filtering logic which reduces the number of assemblies that are scanned. See the filtering section in this topic for information.

Dependency sorting

If your application has a dependency on one or more existing initialization modules, then explicitly state this dependency by using the \[ModuleDependency(typeof(ModuleThatIDependOn))\] attribute. This is used by the dependency sorting to ensure that all modules are executed in the correct order. You can define multiple modules on which the application is dependent. For example, in CMS, you can take a dependency on EPiServer.Web.InitializationModule to make sure any CMS functionality has been initialized before your module.

The modules are then simply sorted, ensuring that the modules are executed in the correct dependency order.

If the application has defined circular dependencies or dependencies to non-existing modules, you receive an exception upon application startup.

Execution engine – InitializationEngine

InitializationEngine (resides in the namespace EPiServer.Framework.Initialization) is responsible for executing the list of modules created by the dependency sorting algorithm. As described in the IInitializableModule.Initialize method, it is guaranteed that the Initialize method gets called only once (unless the method error occurs).

The initialization engine is a simple state machine that logs information about all state changes and reports the initialization methods that were executed. See the Logging section in this document for information.

In case of an exception, the system stops executing initialization code and waits for the next incoming request, then retries the failing Initialization method. This behavior ensures that the system does not start up in a partially initialized state.

After all Initialization methods are executed successfully, an InitComplete event is raised, which lets you perform post-initialization actions. For information,  see the InitComplete event section.

How the initialization system is started

The initialization system should be called from the first point of entry in the application. In CMS, this is handled automatically in the EPiServer.Global base class, the base class for Global.asax.cs. The call to the initialization system is placed at that point because it is the first piece of code under CMS’s control that is invoked by ASP.NET. If errors occur from a startup module, CMS tries the initialization again at the next BeginRequest event.

The major advantage of using the earlier initialization point is that it is executed before Application_Start, which has the application fully initialized and usable from within Application_Start.

ASP.NET 4 comes with the ability to automatically startup and proactively initialize a web application without waiting for an external client to hit the web server. This provides a faster response time, and you do not need custom scripts to “warm up” the server and get data caches ready. This feature works with all types of ASP.NET applications – including both ASP.NET Web Forms and ASP.NET MVC-based applications. Find out more about this: ASP.NET feature.

Assembly scan and filter

When the initialization system looks for modules, it relies on a common type scanning system based on the standard .NET reflection APIs for loading and composition. The basic idea is to scan all assemblies that are part of the application, with the exception of .NET Framework assemblies. This assembly list is exposed through the EPiServer.Framework.Initialization.InitializationModule.Assemblies static property. You can attribute an assembly with \[PreventAssemblyScan\] to exclude it from the scanning process.

Optimizely products use the same scanning process as the initialization system when discovering plug-ins. This is important to remember when you decide which assemblies are scanned by the Optimizely Framework.

Because scanning assemblies for types can be time-consuming, a disk-based cache is used, and is stored in the per-site folder Temporary ASP.NET Files. The types that were discovered during scanning are then loaded into a lookup implementing the ITypeScannerLookup interface in EPiServer.Framework.TypeScanner namespace. Types that define plug-in systems, such as Optimizely CMS, uses an assembly attribute TypeScannerRegistration to register new plug-in types. Do not use the TypeScannerRegistration attribute to register plug-ins; only to register new plug-in types. By having a consolidated type scanning system, a single cached sweep over all assemblies is required for all products to initialize themselves.

Customize assembly scanning with configuration

You can configure optional settings in the <episerver.framework> section of web.config to customize the assembly scanning process. Under this section, you find the following element:

<scanAssembly forceBinFolderScan="true" />

Use this section to customize the assembly scanning process. It should be regarded as an additional filter on top of the assemblies normally loaded by ASP.NET as controlled by the <system.web> / / section. The bulk of the configuration usually resides in the systems web.config file.

If you want to exclude specific assemblies from the normal scanning process (as described in the previous section), see the following example:

<scanAssembly forceBinFolderScan="true">
       <add assembly="*" />
       <remove assembly="MyAssembly" />
       <remove assembly="MyAssembly2" />
</scanAssembly>

This includes all assemblies by virtue of the directive (except those filtered by attributes as described above) except for MyAssembly and MyAssembly2. The second mode of usage is to only scan specific assemblies by adding configuration similar to this as follows:

<scanAssembly forceBinFolderScan="true">
      <add assembly="EPiServer.Framework" />
      <add assembly="EPiServer.Data" />
      <add assembly="EPiServer.Events" />
      <add assembly="EPiServer.Shell" />
      <add assembly="EPiServer" />
      <add assembly="EPiServer.Enterprise" />
      <add assembly="EPiServer.Cms.Shell.UIscanAssembly">
</scanAssembly>

This excludes any other assemblies from being scanned. The selection of assemblies above represent all assemblies delivered with CMS that have initialization modules (these assemblies must be present for CMS to work properly).

InitComplete event

There are cases where you might want your own initialization module to be called again after the initialization process is complete. A typical use case is to attach event handlers to an instance property that may be overridden by third-party code.

To attach to the InitComplete event, write your Initialize method as follows:

public void Initialize(InitializationEngine context)
      {
        context.InitComplete += InitCompleteHandler;
        StartThisModule();
      }

When all initialization modules have executed, the InitComplete event is raised. The InitComplete event is handled in a slightly non-standard way. When an event handler executes without error, the initialization system removes it from the InitComplete event. So do not detach from the InitComplete event in your Uninitialize method.

Although this procedure in the initialization system may seem peculiar, it is simply to verify that if an InitComplete event handler has an error, CMS can re-execute the InitComplete event on the next request without re-invoking the already successfully executed event handlers.

Logging

Add the following configuration to episerverlog.config to enable logging of the initialization engine:

<logger name="EPiServer.Framework.Initialization">
      <level value="All" />
    </logger>