Initialization System
Table of Contents
- Introduction
- The Discovery Mechanism
- Dependency Sorting
- Execution Engine – InitializationEngine
- Assembly Scanning and Filtering
- Customizing Assembly Scanning with Configuration
- The InitComplete Event
- Logging
Introduction
The EPiServer initialization system is designed and implemented with the purpose of being the sole initialization mechanism to use for both EPiServer internal code as well as 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 will execute the modules.
- Handling of re-execution of initialization modules (by hooking into ASP.NET) in the occurence of exceptions during startup.
- The namespace EPiServer.Framework.Initialization which resides in the assembly EPiServer.Framework.
The Discovery Mechanism
Locating the initialization modules in an EPiServer application is achieved through scanning assemblies.
The scanning is primarily using an attribute-based discovery system. EPiServer scans all assemblies loaded in the current AppDomain for initialization modules. This includes all assemblies in the bin folder, since by default the ASP.NET config section <system.web> / <compilation> / <assemblies> contains the configuration <add assembly="*"/>, this causes the ASP.NET build system to pull-in all assemblies in the bin folder.
In order 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.
To speed up the initialization process, the assembly scanning process supports some filtering logic which will reduce the number of assemblies that are scanned – see the filtering section in this document for further 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. Note that you can define multiple modules that the application is dependent upon.
For example in EPiServer CMS you can take a dependency on
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 then you will 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 throws an exception).
The initialization engine is a simple state machine that will log information about all state changes as well as reporting the initialization methods that have been executed (see the Logging section in this documentfor further information).
In the case of an exception the system will stop executing initialization code and simply wait for the next incoming request and then retry the failing Initialization method. This behavior will ensure that the system does not start up in a partially initialized state.
After all the Initialization methods have been executed successfully an InitComplete event will be raised. This is to allow you to perform post-initialization actions. For further information regarding this see the InitComplete event section in this document.
How the Initialization System Is Started
The initialization system should be called from the first point of entry in the application, in EPiServer CMS this is handled automatically in the EPiServer.Global base class, the base class for Global.asax.cs. The reason the call to the initialization system is placed at that specific point is due to the fact that it is the first piece of code under EPiServer CMS’s control that is invoked by ASP.NET. In the case of exceptions being thrown by a startup module EPiServer CMS will retry the initialization at the next BeginRequest event.
The major advantage of using the earlier initialization point is the fact that it is executed before Application_Start, which allows to have to have the application fully initialized and usable from within Application_Start (in most cases, see TerminateInitializationException).
ASP.NET 4 comes with the ability to automatically startup and proactively initialize a web application without having to wait for an external client to hit the web server. This provides a faster response time, and you will not need custom scripts to “warm up” the server and get any 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.
Early Initialization – TerminateInitializationException
When there are cases where EPiServer does not have access to an incoming request one question that may be posed is how the application manages to identify the correct EPiServer site section in the web.config file – in this instance the application cannot identify the correct EPiServer site section in the web.config – to meet this challenge a special technique is used: EPiServer utilizes the TerminateInitializationException. By throwing this exception from within the Initialization method the application will stop execution of the initialization and the InitializationEngine will catch the exception to prevent an error message being displayed.
In the case of the built-in SiteMappingConfiguration module this exception is being thrown the first time the EPiServer application executes (after installation). The execution will be resumed when the application reaches the BeginRequest event and the SiteMappingConfiguration will write the “metabase path-to-siteId” mapping into the EPiServerFramework.config file. This information can then be used on future startups to determine which site section should be used prior to having an active HTTP request.
Assembly Scanning and Filtering
When the initialization system looks for modules it relies on common type scanning system that is 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 what is exposed through the EPiServer.Framework.Initialization.InitializationModule.Assemblies static property. It is possible to attribute an assembly with [PreventAssemblyScan] to exclude it from the scanning process.
All EPiServer products uses the same scanning process as the initialization system when discovering plug-ins. This is important to remember when you decide which assemblies should be scanned by EPiServer Framework.
Since scanning assemblies for types can be time-consuming a disk-based cache is being used, it is stored in the per-site folder “Temporary ASP.NET Files”. All the types that was discovered during scanning are then loaded into a lookup implementing the interface ITypeScannerLookup in namespace EPiServer.Framework.TypeScanner. Types that define plug-in systems, such as EPiServer CMS, uses an assembly attribute TypeScannerRegistration to register new plug-in types. The TypeScannerRegistration attribute should not be used 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 to for all products to initialize themselves.
Customizing Assembly Scanning with Configuration
There are optional configuration settings that can be used to customize the assembly scanning process in the EPiServerFramework.config file. Note that the default configuration (see <configuration> / <configSections> in web.config) sets the restartOnExternalChanges attribute to false (changes to this file will not restart your web application).
In EPiServerFramework.config you will find the following section:
<scanAssembly forceBinFolderScan="true" />
This section can be used 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> / <compilation> / <assemblies> section. Note that the bulk of the configuration usually resides in the systems web.config file.
If you want to exclude some 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 will include all assemblies by virtue of the <add assembly="*" /> 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>
This will exclude any other assemblies from being scanned. Note that the selection of assemblies above represent all assemblies delivered with EPiServer CMS that have initialization modules (these assemblies must be present for EPiServer CMS to work properly).
The 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 (also featured in EPiServer Community) 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. Note that the InitComplete event will be handled in a slightly non-standard way. When an event handler has executed without throwing an exception the initialization system will remove it from the InitComplete event. This means that you should not detach from the InitComplete event in your Uninitialize method.
This procedure in the initialization system may seem somewhat peculiar though it is simply to make sure that if an InitComplete event handler throws an exception, EPiServer CMS can re-execute the InitComplete event on the next request without reinvoking the already successfully executed event handlers.
Logging
Add the configuration below to episerverlog.config to enable logging of the initialization engine as follows:
<logger name="EPiServer.Framework.Initialization">
<level value="All" />
</logger>
See Also
Refer to Creating an Initialization Module for more information on how to add custom initialization modules.
Last updated: Mar 21, 2013