Lex2193 Lex2193 - 22 days ago 7
C# Question

MVC : The view found at '~/.../Index.cshtml' was not created

I have created a modular MVC project to load other special web project within the same runtime.

Other projects are located in a folder at the root of the website called "Modules".
I am using the attribute PreApplicationStartMethod to load the others assemblies inside the sub directories at boot.

I have added special routes to target each modules with the namespace constraints.

I have create a class who implements RazorViewEngine to override the viewPath when a call is made for the element in the module : ~/Views/Home/Index.cshtml -> ~/Modules/ModuleTest/Views/Home/Index.cshtml.

The Index() method inside the dynamically loaded library is successfully called but i got an error when the view is rendered :

See following image : http://i.imgur.com/KoTgxg2.png

The framework tell me basically that the view has been found but he will not render it.
Do anyone has any idea why the framework refuse to render it ?

Server Error in '/' Application.

The view found at '~/Modules/ModuleTest/Views/Home/Index.cshtml' was not created.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.InvalidOperationException: The view found at '~/Modules/ModuleTest/Views/Home/Index.cshtml' was not created.


  • Exception




Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace:

[InvalidOperationException: The view found at '~/Modules/ModuleTest/Views/Home/Index.cshtml' was not created.]
System.Web.Mvc.BuildManagerCompiledView.Render(ViewContext viewContext, TextWriter writer) +362
System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context) +431
System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) +39
System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) +116
System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) +529
System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList`1 filters, ActionResult actionResult) +106
System.Web.Mvc.Async.c__DisplayClass28.b__19() +321
System.Web.Mvc.Async.c__DisplayClass1e.b__1b(IAsyncResult asyncResult) +185
System.Web.Mvc.Async.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult) +42
System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +133
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +56
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult) +40
System.Web.Mvc.Controller.b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState) +34
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +70
System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +139
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +59
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +40
System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +44
System.Web.Mvc.Controller.b__15(IAsyncResult asyncResult, Controller controller) +39
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +62
System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +139
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +59
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +40
System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +39
System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult) +39
System.Web.Mvc.MvcHandler.b__4(IAsyncResult asyncResult, ProcessRequestState innerState) +39
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +70
System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +139
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +59
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +40
System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +40
System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +38
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +9514812
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155

Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.18408



  • Controller




public class HomeController : Controller
{
public ActionResult Index()
{
return View(new TestModel() { Value = "Bla" });
}
}



  • View





@using Easily.ModuleTest.Models;
@{ ViewBag.Title = "Test Index"; }
@model TestModel
@Model.Value




  • CustomRazorViewEngine




public class CustomRazorViewEngine : RazorViewEngine
{
public CustomRazorViewEngine()
{
List tmpViewLocationFormats = new List(ViewLocationFormats);
List tmpMasterLocationFormats = new List(MasterLocationFormats);
List tmpPartialViewLocationFormats = new List(PartialViewLocationFormats);
foreach (string moduleDirectory in EasilyModulesContainer.Modules.Select(x => x.Directory))
{
foreach (string viewLocationFormat in ViewLocationFormats)
tmpViewLocationFormats.Add(viewLocationFormat.Replace("~/", string.Format("~/{0}/{1}/", Constants.ModulesDirectory, moduleDirectory)));
foreach (string masterLocationFormat in MasterLocationFormats)
tmpMasterLocationFormats.Add(masterLocationFormat.Replace("~/", string.Format("~/{0}/{1}/", Constants.ModulesDirectory, moduleDirectory)));
foreach (string partialViewLocationFormat in PartialViewLocationFormats)
tmpPartialViewLocationFormats.Add(partialViewLocationFormat.Replace("~/", string.Format("~/{0}/{1}/", Constants.ModulesDirectory, moduleDirectory)));
}
ViewLocationFormats = tmpViewLocationFormats.ToArray();
MasterLocationFormats = tmpMasterLocationFormats.ToArray();
PartialViewLocationFormats = tmpPartialViewLocationFormats.ToArray();
}

protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
return base.CreateView(controllerContext, GetPath(controllerContext, viewPath), masterPath);
}

protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
return base.CreatePartialView(controllerContext, GetPath(controllerContext, partialPath));
}

private string GetPath(ControllerContext controllerContext, string path)
{
if (!controllerContext.RouteData.Values.ContainsKey("_module"))
return path;
Module module = ModulesContainer.Modules.SingleOrDefault(x => x.Name == controllerContext.RouteData.GetRequiredString("_module"));
return path.Replace("~/", string.Format("~/{0}/{1}/", Constants.ModulesDirectory, module.Directory));
}
}

Answer

I found the problem by debugging into http://aspnetwebstack.codeplex.com/.

I actually load the library who contain the controllers from another folder than bin in my ModulesContainer class (see question). But inside System.Web.Mvc.dll, one method try to find my controller type by doing a Assembly.Load() in the default directory, its why the BuildManager.GetCompiledType() return null.

I have found a simple way to override this method with :

AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainAssemblyResolve;

and

private static Assembly CurrentDomainAssemblyResolve(object sender, ResolveEventArgs args)
    {
        if (args.RequestingAssembly != null)
            return args.RequestingAssembly;
        Module module = _modules.SingleOrDefault(x => x.Assembly.FullName == args.Name);
        if (module != null)
            return module.Assembly;
        throw new Exception(string.Format("Unable to load assembly {0}", args.Name));
    }

I'm just looking inside my pre-built dll cache to find the already loaded assembly.