Layout Searching Not in Sync with the View Engine's order.

Oct 4, 2013 at 9:39 AM
Edited Oct 4, 2013 at 9:58 AM
Consider the following scenario: Say for instance, I have two projects containing precompiled views. Let's call the first project ThemesPrecompiledViews and the second project, OtherPrecompiledViews.

ThemesPrecompiledViews is registered with the following code:
//Registered on PostApplicationStartMethod
ViewEngines.Engines.Insert(1, engine);
VirtualPathFactoryManager.RegisterVirtualPathFactory(engine);
This is to ensure that the views from ThemesPrecompiledViews are searched first (After the host MVC application). Let's say we want the Host MVC to override the views from the Precompiled Views projects while also giving ThemesPrecompiledViews precedence over the other precompiled views assemblies.

Meanwhile, the view engine from OtherPrecompiledViews project is registered as:
//Registered on PostApplicationStartMethod
ViewEngines.Engines.Add(engine);
VirtualPathFactoryManager.RegisterVirtualPathFactory(engine);
This is to ensure that every other precompiled views project are searched after the Host MVC and ThemesPrecompiledViews project respectively.

Now here's the problem. Since we cannot always predict which of the two project's view engine gets registered first, we can end up with OtherPrecompiledViews getting registered first before the ThemesPrecompiledViews. (I think, with all else being equal, the assemblies are registered in alphabetical order)

This is not a problem with the Views from ThemesPrecompiledViews since we explicitly told it to always insert itself at index 1 so we can safely assume that it is always placed second in the views search sequence. However, the line:
VirtualPathFactoryManager.RegisterVirtualPathFactory(engine);
Gets called in the order of when the assembly containing that line of code was called. In this case, we can end up with ThemesPrecompiledViews getting registered at index 1 while its layout views gets registered by the VirtualPathFactoryManager as the last item. Thus:

View Engine Search Sequence:
Host MVC
ThemesPrecompiledViews
OtherPrecompiledViews

Layout Search Squence:
Host MVC
OtherPrecompiledViews
ThemesPrecompiledViews

So when a view from ThemesPrecompiledViews tries to get its associated layout, it would then invoke the _ViewStart of the OtherPrecompiledViews assembly instead since index 1 was the position that the view engine of OtherPrecompiledViews was inserted/added via RegisterVirtualPathFactory.

My question is, is there a way to specify the order of layout view searching like that of the views to make them in sync? Something like:
//Registered on PostApplicationStartMethod
ViewEngines.Engines.Insert(1, engine);
VirtualPathFactoryManager.VirtualPaths.Insert(1, engine);
Oct 4, 2013 at 11:33 AM
Edited Oct 4, 2013 at 11:42 AM
It turns out that the solution is staring at me right in the face. By specifiying the order in the WebActivator, you can control which gets registered first.

ThemesPrecompiledViews:
[assembly: WebActivatorEx.PostApplicationStartMethod(typeof(ThemesPrecompiledViews), "Start", Order=1)]
Every Other Precompiled Projects:
[assembly: WebActivatorEx.PostApplicationStartMethod(typeof(OtherPrecompiledViews), "Start", Order=2)]
[assembly: WebActivatorEx.PostApplicationStartMethod(typeof(AnotherPrecompiledViews), "Start", Order=2)]
By specifying the WebActivator order of every other projects as the same, their registration order would be random. They would, however, still respect any order that are higher than them and thus allow the ThemesPrecompiledViews' view engine registration to be executed first.

Though the problem remains in cases where you have no control over the other precompiled assemblies such as when developing web applications that allow plugins from third party vendors. In that case, you can just instruct plugin developers to always specify the required order number for their plugins to work.
Marked as answer by TheFurryMonk on 10/4/2013 at 4:40 AM
Coordinator
Oct 4, 2013 at 4:03 PM
Sounds good. Couple notes:
  1. The default order is 0, and negative numbers are allowed. So you could just set the Theme to -1 and leave the rest alone
  2. I only recently fixed WebActivator to support cross assembly ordering. So you'd have to make sure you use 2.0.3 or later.
Oct 7, 2013 at 4:28 AM
Edited Oct 7, 2013 at 4:29 AM
Just a further question on how the WebActivator works.

If we have a set of assemblies containing Order = 0, another set of assemblies containing Order = 1 and yet another set of assemblies containing Order = 2. Will the assemblies having Order = 0 be executed first, followed by those with Order = 1 and lastly those with Order = 2?
Coordinator
Oct 7, 2013 at 5:00 AM
With 2.0.3, the granularity of the ordering is not the assembly, but each PostStart attribute. So all the 0's will happens before all the 1's, etc... across all assemblies. See this code.
Oct 7, 2013 at 11:10 AM
Awesome! That's enlightening. Thank you.