One of the things I like most when developing ASP.NET application using the MVC model (if you don't know yet what it is, read here) is the emphasis put to the project's folder structure: the separation between the three logical aspects is reflected to the directory tree, expecially - since the Model is often injected from external libraries and their abstraction layers - the /Controllers/ and /Views/ folders, hosting respectively the input handlers and the user interface of our application.
Among these, the /Views/ folder plays a peculiar role in relation with the filesystem because it is looked by the View Engine to retrieve the View .cshtml and/or .aspx files implicitly or expicitly referenced by each Controller. The search is handled using a set of predefined patterns that come by the name of default location scheme. Here's a portion of it, used by the Razor View Engine to lookup the standard Views:
1 2 3 4 5 6 7 |
ViewLocationFormats = new string[] { "~/Views/{1}/{0}.cshtml", "~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml" }; |
The full location scheme, including the default patterns for Partial Views, Area Views, Master Views et. al., can be seen by looking at this page.
Back to our example, we can notice two placeholders, {0} and {1}, referring to the Action and its Controller. What happens under the hood is the View Engine checking for the presence of a suitable View file in these folder paths, starting with the most specific ones and then looking in the directory hosting the shared views.
Question is: can we add other paths? Or, to be more specific: can we edit these lists, maybe adding one of more folders to reflect the folder structure we used to organize our Views, or are we forced to use the default locations to avoid errors like the following one?
The view 'YourView' or its master was not found or no view engine supports the searched locations. The following locations were searched:
Most answers that can be found on the web offer solutions relying to rather complex approaches, such as creating a new View Engine by extending the RazorViewEngine or the WebFormViewEngine, clearing & rewriting the ViewEngines.Engines collection, and other not so developer-friendly techniques.
Luckily enough, there's a simple yet effective method to alter the default location scheme collection with few lines of code and without having to create or extend any class: all we have to do is to add the following lines to the Global.asax's Application_Start method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
protected void Application_Start() { // ---------------------------------------------- // *** EXISTING CODE HERE - DO NOT DELETE IT! *** // ---------------------------------------------- // ... // Add /MyVeryOwn/ folder to the default location scheme for STANDARD Views var razorEngine = ViewEngines.Engines.OfType<RazorViewEngine>().FirstOrDefault(); razorEngine.ViewLocationFormats = razorEngine.ViewLocationFormats.Concat(new string[] { "~/MyVeryOwn/{1}/{0}.cshtml", "~/MyVeryOwn/{0}.cshtml" // add other folders here (if any) }).ToArray(); // Add /MyVeryOwnPartialFolder/ folder to the default location scheme for PARTIAL Views razorEngine.PartialViewLocationFormats = razorEngine.PartialViewLocationFormats.Concat(new string[] { "~/MyVeryOwnPartialFolder/{0}.cshtml" // add other folders here (if any) }).ToArray(); } |
The above example adds a folder reference (with or without Controller) to the default search pattern array for either the Views and Partial Views by using a rather inobtrusive, lightweight approach. You can also add other folders to one or both these arrays, and/or to any other Views collection referenced by the already mentioned page.
That's it fow now: happy coding!
Thanks very much for your article but when I try this
var razorEngine = ViewEngines.Engines.OfType().First();
gives
System.InvalidOperationException: Sequence contains no elements ??
Any ideas ??
Hello and sorry for the late reply: for MVC5 I guess you can try to replace the following 4 lines of the sample code with these:
var razorEngine = ViewEngines.Engines.OfType().FirstOrDefault();
if (razorEngine == null)
{
razorEngine = new RazorViewEngine();
ViewEngines.Engines.Add(razorEngine);
}
Please try it and let me know if it solves your issue, so I’ll update the post as well.