Initialize CMS
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.
An ASP.NET Core application controls the initialization of the application through the Startup
class. Optimizely Content Management System (CMS) is typically added to the application by calling the extension method AddCms()
on IServiceCollection
(from NuGet package EPiServer.Cms.Web
). In its registration, that method calls the extension method AddCmsHost()
on IServiceCollection
(from NuGet package EPiServer.Hosting
), which creates the InitializationEngine
and initialize modules implementing IInitializableModule
.
Custom initialization logic can preferably be done through standard ASP.NET Core components like, for example, IStartupFilter or IHostedService. The IInitializableModule
system (described below) within CMS that was built for ASP.NET – where there was no central Startup
class – is also available in the ASP.NET Core application and can be used also.
The Optimizely 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.
- TheÂ
EPiServer.Framework.Initialization
 namespace, in theEPiServer.Framework
assembly.
Discovery mechanism
The scanning of assemblies is primarily using an attribute-based discovery system. CMS uses the AssemblyLoadContext and the DependencyContext
to locate assemblies to locate which assemblies to scan for initialization modules.
To have a discoverable initialization module, you must add the [InitializableModule]
or [ModuleDependency(...)]
attribute to your class. The class also must implement the IInitializableModule
or IConfigurableModule
interfaces.
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. The dependency sorting uses this to ensure that 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 was initialized before your module.
The modules are then sorted, ensuring that they are executed in the correct dependency order.
If the application defines 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 the dependency sorting algorithm created. 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 state changes and reports the initialization methods that were executed.
After the Initialization
methods are executed, an InitComplete
event is raised, which lets you perform post-initialization actions. For information, see the InitComplete
event section.
The InitializationEngine
is created when calling AddCmsHost()
on IServiceCollection
(from NuGet package EPiServer.Hosting
). CMS adds an IHostedService that, during StartAsync()
, calls the initialization engine, which then calls Initialize on detected IInitializableModule
implementations. This means that IInitializableModules
are called after the DI configuration of the application is done but before any HTTP requests are executed.
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 assemblies that are part of the application, except for .NET 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 EPiServer Framework.
The types that were discovered during scanning are then available through an implementation of lookup ITypeScannerLookup
component in EPiServer.Framework.TypeScanner
 namespace. Types that define plug-in systems, such as CMS, use an assembly attribute TypeScannerRegistration
to register plug-in types. Do not use the TypeScannerRegistration
attribute to register plug-ins; only to register plug-in types. By having a consolidated type scanning system, a single cached sweep over assemblies is required for products to initialize themselves.
InitComplete event
Sometimes, you might want your initialization module to be called again after completing the initialization process. 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 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 verifies that if an InitComplete
event handler has an error, Optimizely CMS can re-execute the InitComplete
event on the next request without re-invoking the already executed event handlers.
Logging
To enable logging from initialization modules, configure logging for namespace EPiServer.Framework.Initialization
.
Updated 9 months ago