We are trying to develop a corporate CMS application in ASP.NET MVC 5. The user needs to create a new page, change an existing page content or delete a page alltogether from an admin application. Another requirement states that some of the pages can include custom widgets which were written in Razor syntax.
These 2 requirements lead me to load razor views from the database. I googled and find a couple of examples.
The first one is to extend the VirtualPathProvider and register it with the expression below in the Application_Start method.
protected void Application_Start()
{
...
HostingEnvironment.RegisterVirtualPathProvider(new MyVirtualPathProvider());
}
When I try to run this, it throws the error below
[InvalidOperationException: The view at '~/Views/Home/Index.aspx' must derive from ViewPage, ViewPage, ViewUserControl, or ViewUserControl.] System.Web.Mvc.WebFormView.RenderView(ViewContext viewContext, TextWriter writer, Object instance) +180 System.Web.Mvc.BuildManagerCompiledView.Render(ViewContext viewContext, TextWriter writer) +107 System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context) +291 System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) +13 System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) +56 System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) +420 System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList`1 filters, ActionResult actionResult) +52 System.Web.Mvc.Async.c__DisplayClass2b.b__1c() +173 System.Web.Mvc.Async.c__DisplayClass21.b__1e(IAsyncResult asyncResult) +100 System.Web.Mvc.Async.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult) +10 System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49 System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult) +27 System.Web.Mvc.Controller.b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState) +13 System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +29 System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49 System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +36 System.Web.Mvc.Controller.b__15(IAsyncResult asyncResult, Controller controller) +12 System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +22 System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49 System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +26 System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult) +10 System.Web.Mvc.MvcHandler.b__5(IAsyncResult asyncResult, ProcessRequestState innerState) +21 System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +29 System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49 System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +28 System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9 System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +9644097 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155
Another example is
protected void Application_Start()
{
...
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new MyViewEngine());
}
private class MyViewEngine : RazorViewEngine
{
public MyViewEngine()
{
this.VirtualPathProvider = new MyVirtualPathProvider();
}
}
This one works when the specified file (e.g. /View/Home/Index.cshtml) is in the file system even it has nothing in it. When I delete the file from the file system, the error below is thrown
[InvalidOperationException: The view 'Index' or its master was not found or no view engine supports the searched locations. The following locations were searched: ~/Views/Home/Index.cshtml ~/Views/Home/Index.vbhtml ~/Views/Shared/Index.cshtml ~/Views/Shared/Index.vbhtml] System.Web.Mvc.ViewResult.FindView(ControllerContext context) +382 System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context) +116 System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) +13 System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) +56 System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) +420 System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList`1 filters, ActionResult actionResult) +52 System.Web.Mvc.Async.c__DisplayClass2b.b__1c() +173 System.Web.Mvc.Async.c__DisplayClass21.b__1e(IAsyncResult asyncResult) +100 System.Web.Mvc.Async.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult) +10 System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49 System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult) +27 System.Web.Mvc.Controller.b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState) +13 System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +29 System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49 System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +36 System.Web.Mvc.Controller.b__15(IAsyncResult asyncResult, Controller controller) +12 System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +22 System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49 System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +26 System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult) +10 System.Web.Mvc.MvcHandler.b__5(IAsyncResult asyncResult, ProcessRequestState innerState) +21 System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +29 System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49 System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +28 System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9 System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +9644097 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155
The RazorViewEngine tries to find the file in the file system even if you override the VirtualPathProvider to load view data from the database.
I guess the examples work in the previous versions of ASP.NET MVC (I think 2 or 3). The best solution that I got is to write a custom view engine from scratch, but there are too much work to be done to make it work like the razor view engine. Is there any other trick to make these examples work, or any other alternatives?
Have a look on these links. Hope this might help.
http://rebuildall.umbraworks.net/2009/11/17/ASP_NET_MVC_and_virtual_views
Using custom VirtualPathProvider to load embedded resource Partial Views
Storing ASP.Net MVC Views in the Database
ASP.NET MVC load Razor view from database