|Number of votes:||1|
In this post I will go into more detail about how the mirroring service works and how that affects modules and custom code.
As I mentioned in my previous post the mirroring job is executed in a separate application from the site. This has some implications when it comes to modules and external code. For example if you use custom properties on your site then the assembly containing the custom properties must be loadable from the mirroring service. So you have to deploy the assemblies to the bin folder for the mirroring service.
Another thing to be aware of is how to attach custom event handlers, e.g. to hookup to DataFactory events. Before I give any recommendations a short description of the internals of MirroringService is needed.
Each time the mirroring service starts a mirroring job (either acting as a source or as a target) it will create a new AppDomain and initialize a “CMS runtime” inside the new AppDomain. With “CMS runtime” I mean that functions such as DataFactory, DynamicDataStore, ObjectStore, VirtualPathProviders etc are initialized and functional. The initialization works in the way that the service reads the site configuration and initializes the functions according to the configuration. After the mirroring job is finished the AppDomain is unloaded.
One reason a new AppDomain is created for a mirroring job is that then one mirroring service can serve several sites and handle concurrent jobs. That means for example that when there are several sites in an enterprise scenario they can share the same mirroring service (they can though be configured to use separate mirroring services if that is preferred). Another reason is that we want the service to be “stateless”, that is it holds no internal state. It gets a job request and then initializes the runtime, executes the job and when the job is finished everything is cleared and no state is saved inside the service.
During the initialization of the new AppDomain the mirroring service will scan the bin folder for assemblies that want to be part of initialization. The scanning will be performed both for PlugInAttribute and for new MEF initialization as described in New initialization.
So what about hooking up event handlers? The way to go is to either use the new MEF initialization (recommended) or use the PlugInAttribute with a static Start method. Using HttpModule will however not work. The reason for that is because even though the Init method of your HttpModule will be called, that code will not execute in the correct AppDomain meaning there is no way to access e.g. the initialized DataFactory.
Another thing to be aware of is that the code executed as part of the mirroring job executes without web context. That means that event handlers and other code need to be implemented so they can execute without web context. There are helper classes like EPiServer.Web.VirtualPathUtilityEx (works like System.Web.VirtualPathUtility) and EPiServer.Web.Hosting.GenericHostingEnvironment (works like System.Web.Hosting.HostingEnvironment) that can be used instead of their counterparts in .NET framework. They are designed to work as the .NET classes but they also works without web context. For example:
The installer will install the mirroring service in a folder called MirroringService under the site root. It will be configured as a seperate site in IIS with a separate AppPool which means that it runs in its own process. It is possible to manually copy the MirroringService to another server and set up an IIS site there pointing to the service folder. One thing to remember if the mirroring service is relocated to another machine is that the VirtualPathProviders for the site must be configured so they can accessed from both site and service. This typically means that you would configure them to use a file share. Another thing that needs to be changed if the mirroring service is relocated to another machine is that the WCF endpoints and protocol used for communication between service and site (event replication and source service endpoint) has to be reconfigured to the new location and the binding has to be changed from from net.pipe to something (e.g. net.tcp) that works between machines.