How do I stop MVC5 RenderAction looking for .aspx files - c#

I added MVC to my existing webforms project. All is going well except for RenderAction is looking for .aspx files
The view '_Mainmenu.cshtml' or its master was not found or no view engine supports the searched locations. The following locations were searched:
~/Areas/NewPages/Views/Shared/_Mainmenu.cshtml.ascx
The view is
~/Areas/NewPages/Views/Shared/_Mainmenu.cshtml
And it does exist in that folder. Can anybody help me sort this out.
Everything else MVC is working OK I even have PITA EntityFramework working too
Any help would be appreciated

The view '[viewname]' or its master was not found or no view engine supports the searched locations indicates that you're using default view engine which prioritizes web forms view engine (the path shown as ~/Areas/NewPages/Views/Shared/_Mainmenu.cshtml.ascx means that MVC view engine prioritizes searching ASPX & ASCX files instead of Razor cshtml files). To change this behavior which MVC uses Razor view engine by default, insert these lines into Application_Start method on Global.asax:
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new RazorViewEngine());
// ViewEngines.Engines.Add(new WebFormViewEngine()); => optional webforms engine registration
Additionally, if default Razor view engine still can't recognize cshtml files in areas properly, you need to create a custom view engine class which inherits RazorViewEngine and setting AreaViewLocationFormats in its constructor like this:
public class CustomViewEngine : RazorViewEngine
{
public CustomViewEngine()
{
// Route parsing convention for view engines:
// {0} means action method name
// {1} means controller class name
// {2} means area name
AreaMasterLocationFormats = new[]
{
"~/Areas/{2}/Views/Shared/{0}.cshtml"
};
AreaViewLocationFormats = new[]
{
"~/Areas/{2}/Views/{1}/{0}.cshtml",
// other view search locations here
};
AreaPartialViewLocationFormats = AreaViewLocationFormats;
}
}
Note that the custom view engine will search all view pages inside areas specified by controller action method depending on routes defined in AreaViewLocationFormats.
Then, register custom view engine class at the same place as RazorViewEngine, i.e. in Global.asax:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
// clear all view engines repository first
ViewEngines.Engines.Clear();
// register Razor view engine only
ViewEngines.Engines.Add(new RazorViewEngine());
// register custom view engine class here
ViewEngines.Engines.Add(new CustomViewEngine());
// other initialization codes here
}
Similar issues:
ASP.NET MVC: When should I create custom View Engine
How do I implement a custom RazorViewEngine to find views in non-standard locations?

In the "Global.asax" file, find "Application_Start()"
Then, Please input this code --
RemoveWebFormEngines();

Related

Setting Layout in ActionFilterAttribute.OnActionExecuted is problematic

I'm trying to set the layout path in a custom ActionFilterAttribute I have written as follow:
public class LayoutInjecterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
var result = filterContext.Result as ViewResult;
if (result != null)
{
result.MasterName = "~/Views/Layouts/Test.cshtml"
}
}
}
In here Test.cshtml is precompiled view (with the help of RazorGenerator) in a different project.
But it gives me the error:
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/Shared/Index.cshtml
~/Views/Home/Index.aspx
~/Views/Home/Index.ascx
~/Views/Shared/Index.aspx
~/Views/Shared/Index.ascx
~/Views/Layouts/Test.cshtml
and controller actually is simple:
[LayoutInjecter]
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
}
The error shows that LayoutInjecter is working fine. You said:
In here Test.cshtml is precompiled view in a different project.
But, Using razor views from a different (from outside the web project) is not supported out of the box. However there's a tool to precompile razor views and then you can put them in any DLL which called RazorGenerator.
The compiler can't find the specified master layout file and shows this error.
For more information look at
Precompiled Razor View Using RazorGenerator MVC and
PreCompiledViewEngine in MVC 4.
Compile your asp.net mvc Razor views into a seperate dll
Edit: How did the PrecompiledMvcViewEngine know which view to render?
PrecompiledMvcViewEngine still relies on the ASP.NET MVC Views folder convention, using relative file paths to locate the views. However, this is slightly misleading. The PrecompiledMvcViewEngine doesn’t look at physical files; it looks for the System.Web.WebPages.PageVirtualPathAttribute that the Razor Single File Generator adds to every view that it generates that includes the view’s relative file path.
Edit 2: I believe the guidance for your problem would be found in GitHub.
It works. Make sure the layout path "~/Views/Layouts/Test.cshtml" is correct.
Also, make sure that "Test.cshtml" is a layout page, and not a view / partial view.
Change result.MasterName = "~/Views/Layouts/Test.cshtml" to result.MasterName ="~/Views/Shared/Test.cshtml".
The Framework by convention looks in the ~/Views/Shared/ directory in your asp.net mvc solution for your layout pages.
It appears to me you are dynamically or at runtime selecting a master page.

Call external controller method and render the view

I am using a project called MVCForum and have created a new project in the solution which, for demo purposes, let's call "ExternalApp".
Now, I have added the ExternalApp reference to the MCVForum application, and can call the controller: http://mysite[.]com/TestController
where "TestController" is my external controller. That is, the controller lives in ExternalApp.
The problem is that when I try to return the view from "Test" in TestController, the view cannot be found.
The view 'Index' or its master was not found or no view engine supports the searched locations. The following locations were searched:
~/Themes/Metro/Views/Test/Index.cshtml
~/Themes/Metro/Views/Extensions/Test/Index.cshtml
~/Views/Extensions/Test/Index.cshtml
~/Views/Test/Index.cshtml
~/Views/Test/Index.vbhtml
~/Views/Shared/Index.cshtml
~/Views/Shared/Index.vbhtml
The application seems to be looking within it's own project for the view, and not in the ExternalApp/Views folder. How can I get my external app to render the correct view?
You can to create a custom view engine but as described hereyou have there are number of modifications you need to make:
In order for the views in our MVCExternalApp project to be available at runtime, they must be copied to the MVCForum output folder. Unless you wish to do this manually, you have to specifically tell each view to copy to output. This option forces the files to go into the bin folder.
For each view, right click and select properties. Change the 'Copy to Output Directory' option to 'Copy Always'. The will ensure that the files are always put in the output when the referencing project is built. You will also want to do this for the Web.config.
Create a custom view engine:
public class CustomViewEngine: RazorViewEngine
{
public CMSViewEngine()
{
ViewLocationFormats = new string[]
{
"~/Views/{1}/{0}.cshtml",
"~/Views/{1}/{0}.vbhtml",
"~/Views/Shared/{0}.cshtml",
"~/Views/Shared/{0}.vbhtml",
"~/bin/Views/{1}/{0}.cshtml",
"~/bin/Views/{1}/{0}.vbhtml",
"~/bin/Views/Shared/{0}.cshtml",
"~/bin/Views/Shared/{0}.vbhtml"
};
PartialViewLocationFormats = new[]
{
"~/Views/{1}/{0}.cshtml",
"~/Views/{1}/{0}.vbhtml",
"~/Views/Shared/{0}.cshtml",
"~/Views/Shared/{0}.vbhtml",
"~/bin/Views/{1}/{0}.cshtml",
"~/bin/Views/{1}/{0}.vbhtml",
"~/bin/Views/Shared/{0}.cshtml",
"~/bin/Views/Shared/{0}.vbhtml"
};
}
}
I override only PartialViewLocationFormats an ViewLocationFormats but you can override the rest of locations if needed;
Register view engine in Application_Start method in Global.asax:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
//Remove all view engine
ViewEngines.Engines.Clear();
//Add Custom view Engine Derived from Razor
ViewEngines.Engines.Add(new CustomViewEngine());
}
You can use something like Razor Generator to compile your views into your ExternalApp assembly, or you can just run the two apps separately under one site.

CSHTML files not loading outside of Views

I have a C# MVC Razor site. Typically, Controllers load views from the Views folder. However, I have a special circumstance where I need to render a view outside of the Views folder. How do I do that?
Controller will load /Views/Random/Index.cshtml
Can't load /Random/Index.cshtml
/Random/test.aspx loads with no issues, but can't change cshtml files to aspx files, they need to be built regularly.
I have tried return Redirect("/Random/Index.cshtml") in the Controller, and currently have no controller at all.
The weird thing is it works on my Production environment, but not in localhost. In localhost I get:
The type of page you have requested is not served because it has been explicitly forbidden. The extension '.cshtml' may be incorrect. Please review the URL below and make sure that it is spelled correctly.
Requested URL: /Random/Index.cshtml
You can definitely do this. For doing this you need to create one new custom view engine like
public class MyViewEngine : RazorViewEngine
{
private static string[] AdditionalViewLocations = new[]{
"~/Random/{0}.cshtml"
};
public MyViewEngine()
{
base.PartialViewLocationFormats = base.PartialViewLocationFormats.Union(AdditionalViewLocations).ToArray();
base.ViewLocationFormats = base.ViewLocationFormats.Union(AdditionalViewLocations).ToArray();
base.MasterLocationFormats = base.MasterLocationFormats.Union(AdditionalViewLocations).ToArray();
}
}
Then in you global.asax's Application_Start method register this view engine like this-
ViewEngines.Engines.Add(new MyViewEngine ());
If you want your viewengine to take precedence then insert this at 0th position. like this -
ViewEngines.Engines.Insert(0, new MyViewEngine());
return View("~/AnotherFolder/Index.cshtml")` should work for you.
Do not forget to indicate the Layout in your index view:
#{
Layout="~/Views/Shared/Layout.cshtml";
}

How to put .Mobile views in another folder with MVC4?

In MVC 4, you can just append .Mobile to any view and mobile devices will automatically get served that view from the same controller. Is there a way to store the .Mobile files in a different folder? I really want to store the desktop files in one "Area" and the mobile in another "Area". Anyone know of something like this?
This can easily be accomplished by creating a custom implementation of RazorViewEngine and adding the custom mappings to the ViewLocationFormats. It is important to remember to add the custom mappings to the beginning of the ViewLocationFormats array as they are more specific than the existing mappings.
namespace MobileViewsInMobileFolder.Utility {
public class MyCustomViewEngine : RazorViewEngine {
public MyCustomViewEngine() {
List<string> existingViewLocationFormats = ViewLocationFormats.ToList();
//Folder Structure: Views\Home\Desktop and Views\Home\Mobile
existingViewLocationFormats.Insert(0, "~/Views/{1}/Desktop/{0}.cshtml");
existingViewLocationFormats.Insert(0, "~/Views/{1}/Mobile/{0}.cshtml");
//Folder Structure: Views\Desktop\Home and Views\Mobile\Home
existingViewLocationFormats.Insert(0, "~/Views/Desktop/{1}/{0}.cshtml");
existingViewLocationFormats.Insert(0, "~/Views/Mobile/{1}/{0}.cshtml");
ViewLocationFormats = existingViewLocationFormats.ToArray();
}
}
}
And then make sure to add the custom view engine in the Application_Start
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new MyCustomViewEngine());

how does asp.net mvc relate a view to a controller action?

I have opened a sample ASP.NET MVC project.
In HomeController I have created a method (action) named MethodA
public ActionResult MethodA()
{
return View();
}
I have right clicked on MethodA and created a new view called MethodA1
Re-did it and created a new view called MethodA2.
How is this magical relationship done? I looked for the config to tell the compiler that views MethodAX are related to action MethodA, but found none.
What view will the controller return when MethodA is called?
The convention is that if you don't specify a view name, the corresponding view will be the name of the action. So:
public ActionResult MethodA()
{
return View();
}
will render ~/Views/ControllerName/MethodA.cshtml.
But you could also specify a view name:
public ActionResult MethodA()
{
return View("FooBar");
}
and now the ~/Views/ControllerName/FooBar.cshtml view will be rendered.
Or you could even specify a fully qualified view name which is not inside the views folder of the current controller:
public ActionResult MethodA()
{
return View("~/Views/Foo/Baz.cshtml");
}
Now obviously all this assumes Razor as view engine. If you are using WebForms, replace .cshtml with .aspx or .ascx (if you are working with partials).
For example if there is no view it will even tell you where and in what order is looking for views:
Remember: ASP.NET MVC is all about convention over configuration.
The MVC framework use convention over configuration. The framework calls the ExecuteResult on the ViewResult object (created by the return View();). The framework by convention then looks in a number of locations to find a view
If you are using areas the framework will look in the following locations for a view.
/Areas//Views/ControllerName/ViewName.aspx
/Areas//Views/ControllerName/ViewName.ascx
/Areas//Views/Shared/ViewName.aspx
/Areas//Views/Shared/ViewName.ascx
/Areas//Views/ControllerName/ViewName.cshtml
/Areas//Views/ControllerName/ViewName.vbhtml
/Areas//Views/Shared/ViewName.cshtml
/Areas//Views/Shared/ViewName.vbhtml
Without areas (or if you are using areas and no view has been found) the framework will look at the following locations
/Views/ControllerName/ViewName.aspx
/Views/ControllerName/ViewName.ascx
/Views/Shared/ViewName.aspx
/Views/Shared/ViewName.ascx
/Views/ControllerName/ViewName.cshtml
/Views/ControllerName/ViewName.vbhtml
/Views/Shared/ViewName.cshtml
/Views/Shared/ViewName.vbhtml
As soon as the Framework tests a location and finds a file, then the search stops,
and the view that has been found is used to render the response to the client.
There are a number of overriden versions of the View method. The most common one is to render a specific view, outside of the framework convention, by calling it by name. For example
return View("~/Views/AnotherIndex.cshtml");
As an interesting footnote, the framework looks for legacy ASP, C# and VB Razor views (aspx, ascx, cshtml and vbhtml) even though you have a specific view engine.
In MVC controller action is not bound to view.
It uses delegate mechanism to pickup the view.
Model Binding(Mapping)
I was looking for the same and I just made a couple of tests and figured out.
It doesn't save anywhere.
To understand how it works; just do these steps:
In your controller, right click, Add View
Then enter a different View Name
and Ctrl F5
you will get Server error in application.
For example if you right click , Add View in following Index action method and type "Index2" in View name, you will get the error.
public class TestController : Controller
{
// GET: Test
public ActionResult Index()
{
return View();
}
}
So basically there is a 1-1 matching between action name and View name. And you cannot add view for the same method so there is no need to save in a config file.
Now change the view file name in Visual Studio from Index2.cshtml to Index.cshtml then Ctrl+F5. You should see it is working.

Categories