Allow custom view engines in K12 MVC
In MVC one of the key extensibility points is the abiltiy to create a custom View Engine.
This is most often achieved by implementing a new View Engine class that inherits from RazorViewEngine - thereby retaining all of Razor's functionality but allowing the MVC developer to specify new or additional locations for Views and Layouts.
This gives developers the ability to create multi-site MVC instances as well as sites that contain different themes.
Per support case 0087006336 it appears that Kentico has made the custom View Engine that they use in Kentico 12 MVC "internal", preventing the end developer from inheriting it to create custom view locations, etc.
This suggestion is to ask that the "internal" flag be removed from Kentico's custom view engine and its dependencies to reinstate this key extension point of MVC for Kentico projects.
The Kentico EmbeddedViewEngine class was not designed to be an extension point for Kentico MVC projects. We believe inheriting the native framework classes (System.Web.Mvc.RazorViewEngine or one of its predecessors) and registering the resulting custom view engine in the System.Web.Mvc.ViewEngines.Engines collection is the proper approach.
Also a custom view engine should be able to tell whether it can serve the view or not. Basically, there should not be a hardcoded filter to skip processing of Kentico views. Instead, the custom view engine should rather just skip the views it cannot serve because they are not defined in the folders maintained by it.
I've found a slightly simpler solution to this than was given in the ticket.
The key to the whole thing was this method, derived from the code Kentico sent me...
private static bool IsKenticoEmbeddedView(string viewName)
return (viewName.StartsWith("~/", StringComparison.Ordinal))
&& viewName.IndexOf("Kentico/", StringComparison.OrdinalIgnoreCase) >= 0;
Once I knew that the issue was with the "embedded" views and what constituted a Kentico "embedded" view I just needed to have my custom View Engine (which inherits directly from RazorViewEngine) return an empty result for those cases - which allows MVC to fall back to the next view engine in line - Kentico's - for those requests.
My working custom view engine can be seen here: https://pastebin.com/kXWqDYC7
To allow the fallback to Kentico's view engine to work properly, it must be registered in Global.asax after the Kentico features "Use" methods have been called. Possibly also after the CmsRegistrationSource has been registered with DI too, I've not tested that bit.
This method means that we're not fully overriding the Kentico view engine, just popping our own in front of it in the view resolution process. This should minimise the possibility of any future updates causing problems providing the above IsKenticoEmbeddedView rules don't change.