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.
Related
I have read a nice article about how I can use feature folder structure in my ASP.NET Core MVC application. My plan is to use then a feature folder structure to organize my web application in a better way.
First of all lets see my folder structure:
...
wwwroot
Claims
Controllers
Services
Views
Shared
Map
...
I have followed the article and I implemented the IViewLocationExpander like the following:
public class MyViewLocationExpander : IViewLocationExpander
{
public void PopulateValues(ViewLocationExpanderContext context)
{
context.Values["customviewlocation"] = nameof(MyViewLocationExpander);
}
public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations)
{
var viewLocationFormats = new[]
{
"/Claims/Views/{0}.cshtml",
"/Claims/Views/Shared/{0}.cshtml"
};
return viewLocationFormats;
}
}
I placed my main Claims.cshtml view in the "/Claims/Views/" folder. At the beginning of my Claims.cshtml I have to following line to render my partial view:
#Html.Partial("_NewClaimPopup");
As for the _NewClaimPopup.cshtml, it placed it into the path "/Claims/Views/Shared". But unfortunately I got the following exception when trying to GET the following url: http://localhost:13078/Claims/Claims
InvalidOperationException: The partial view '_NewClaimPopup' was not found. The following locations were searched:
/Views/Claims/_NewClaimPopup.cshtml
/Views/Shared/_NewClaimPopup.cshtml
It seems that the custom paths are successfully added by the implementation of IViewLocationExpander.
Additional infos:
What I also tried is to use "~" sign in paths of the implementation of IViewLocationExpander, so: "~/Claims/Views/{0}.cshtml" and "~/Claims/Views/Shared/{0}.cshtml" but it does not help.
I tried to use absolute path for rendering my partial view, but still nothing
#Html.Partial(""~/Claims/Views/_NewClaimPopup.cshtml");
And of course, I registered my expander in the Startup.cs:
services.Configure<RazorViewEngineOptions>(options => options.ViewLocationExpanders.Add(new MyViewLocationExpander()));
Last but not least I attach a picture about my project structure:
Any other idea? Thanks in advance for any help!
I have made sample application. You can download from below link
Custom View Location
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();
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.
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";
}
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());