I'm using compiled Razor views in separate class libraries as a sort of plugin system for MVC3.
I've followed the guide by Chris Van De Steed here and only deviated primarily on the part about adding references, as I'm loading my assemblys at runtime.
Because I'm loading assemblies at runtime, I'm not using the VirtualPathProviderViewEngine in the BoC library, and have instead implemented my own ViewEngine based on RazorViewEngine. It works by rewriting the viewPath in CreateView to insert the appropriate namespace so that the view can be resolved.
So far so good... I can have different modules loaded, and their controllers won't collide if they share the same name.
The only problem I have now is that for compiled views, the _ViewStart is not called. The _ViewStart works for views in the host MVC3 project, but for any views loaded from the plugin assemblies it's not found.
I have a Route setup like this:-
RouteTable.Routes.MapRoute(
string.Format("Plugin{0}Route", pluginName),
string.Format(@"Plugin/{0}/{{controller}}/{{action}}", pluginName),
new { },
new string[] { string.Format("{0}.Controllers", pluginName) });
the ViewEngine looks like this:-
public class PluginRazorViewEngine : RazorViewEngine
{
public PluginRazorViewEngine() : base()
{
ViewLocationFormats = new[]
{
"~/Plugin/%1/Views/{1}/{0}.cshtml",
"~/Plugin/%1/Views/{1}/{0}.vbhtml",
"~/Plugin/%1/Views/Shared/{0}.cshtml",
"~/Plugin/%1/Views/Shared/{0}.vbhtml",
"~/Views/{1}/{0}.cshtml",
"~/Views/{1}/{0}.vbhtml",
"~/Views/Shared/{0}.cshtml",
"~/Views/Shared/{0}.vbhtml"
};
(the %1 is replaced with the name of the assembly)
and the assembly is registered with the BoC library like this:-
BoC.Web.Mvc.PrecompiledViews.ApplicationPartRegistry.Register(assembly, string.Format("/Plugin/{0}/", pluginName));
When the view is loaded from a plugin assembly (in this example "accounts"), the view is found and displayed OK. But then it looks in these locations for the _ViewStart:-
~/plugin/accounts/views/invoice/_viewstart.cshtml
~/plugin/accounts/views/invoice/_viewstart.vbhtml
~/plugin/accounts/views/_viewstart.cshtml
~/plugin/accounts/views/_viewstart.vbhtml
~/plugin/accounts/_viewstart.cshtml
~/plugin/accounts/_viewstart.vbhtml
~/plugin/_viewstart.cshtml
~/plugin/_viewstart.vbhtml
~/_viewstart.cshtml
~/_viewstart.vbhtml
But it doesn't look in ~/Views/Shared/_ViewStart.cshtml where the file lives.
I've tried changing all the location formats in my ViewEngine (AreaMasterLocationFormats, AreaPartialViewLocationFormats, AreaViewLocationFormats, MasterLocationFormats, PartialViewLocationFormats and ViewLocationFormats) but none of them seem to make a difference.
I've looked around and it seems that System.Web.WebPages.StartPage.GetStartPage is responsible for finding and returning the start page in a view, but I can't find any information on how to control where it looks.
I've tried moving the _ViewStart.cshtml to ~/_ViewStart.cshtml (one of the places it looks) however I immediately get:-
Unable to cast object of type 'ASP._Page__ViewStart_cshtml' to type 'System.Web.WebPages.StartPage'.
Which according to what I've read, is because the _ViewStart needs to live under /Views
Can I modify where MVC looks for a _ViewStart?
The BoC library implements it's own IView, and calls the following:-
startPage = this.StartPageLookup(page, VirtualPathFactoryManagerViewEngine.ViewStartFileName, this.ViewStartFileExtensions);
But in this case ViewStartFileName is just "_ViewStart" and ViewStartFileExtensions are just cshtml and vbhtml... nothing that would control where MVC should search for the file.