Configure The Views Search Locations in ASP.NET MVC

This blog post explains how to change the default location of Views in an ASP.NET MVC project.

public class HomeController : Controller
{
    public ActionResult Index()
    {
        ViewBag.Message = "View Engine Demo!";

        return View();
    }
}

If we look at the above code snippet, we have a HomeController inside we have an Index action method which is returning a View. If an user request for the following URL http://localhost/Home/Index/ or http://localhost/Home/ ASP.NET MVC goes through the request pipeline, execute Index action method & return a view.

In this example we did not mention any view name & location of view. We know ASP.NET MVC follows conventions rather than configuration. It searches for a view named index (view name will same as action name, if we don’t explicitly mention) in Home folder under Views folder in root of the project.

Any ViewEngine searches for views in the following locations:

~/Views/Home/Index.cshtml
~/Views/Home/Index.vbhtml
~/Views/Shared/Index.cshtml
~/Views/Shared/Index.vbhtml 

~/Views/Home/Index.aspx
~/Views/Home/Index.ascx
~/Views/Shared/Index.aspx
~/Views/Shared/Index.ascx

As we know ASP.NET MVC ships with 2 View Engines (razor, web forms) out the box, so it searches both razor views (.cshtml, .vbhtml) & web forms views (.aspx, .ascx). Default convention for views location is either ~/Views/{0}/{1}.{2} or ~/Views/Shared/{1}.{2}.  In convention {0} – represents controller name, {1} – represents action method name, {2} – represents extension of file name.

viewengines

Above picture is the inheritance hierarchy of a view engine, here we are considering razor view engine. Any view engine should be inherited from IViewEngine interface, above classes shown in picture. In the above picture VirtualPathProviderViewEngine class has following properties to defines the location of views & views in areas in an application.

public string[] MasterLocationFormats { get; set; }
public string[] PartialViewLocationFormats { get; set; }
public string[] ViewLocationFormats { get; set; }

public string[] AreaMasterLocationFormats { get; set; }
public string[] AreaPartialViewLocationFormats { get; set; }
public string[] AreaViewLocationFormats { get; set; }

Let’s look at how razor view engine uses the above properties, to define location of views:

public class RazorViewEngine : BuildManagerViewEngine
{
    internal static readonly string ViewStartFileName = "_ViewStart";

    public RazorViewEngine() : this(null)
    {
    }

    public RazorViewEngine(IViewPageActivator viewPageActivator) : base(viewPageActivator)
    {
        AreaViewLocationFormats = new[]
        {
            "~/Areas/{2}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Views/{1}/{0}.vbhtml",
            "~/Areas/{2}/Views/Shared/{0}.cshtml", "~/Areas/{2}/Views/Shared/{0}.vbhtml"
        };
        AreaMasterLocationFormats = new[]
        {
            "~/Areas/{2}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Views/{1}/{0}.vbhtml",
            "~/Areas/{2}/Views/Shared/{0}.cshtml", "~/Areas/{2}/Views/Shared/{0}.vbhtml"
        };
        AreaPartialViewLocationFormats = new[]
        {
            "~/Areas/{2}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Views/{1}/{0}.vbhtml",
            "~/Areas/{2}/Views/Shared/{0}.cshtml", "~/Areas/{2}/Views/Shared/{0}.vbhtml"
        };

        ViewLocationFormats = new[]
        {
            "~/Views/{1}/{0}.cshtml", "~/Views/{1}/{0}.vbhtml",
            "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml"
        };
        MasterLocationFormats = new[]
        {
            "~/Views/{1}/{0}.cshtml", "~/Views/{1}/{0}.vbhtml",
            "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml"
        };
        PartialViewLocationFormats = new[]
        {
            "~/Views/{1}/{0}.cshtml", "~/Views/{1}/{0}.vbhtml",
            "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml"
        };

        FileExtensions = new[]
        {
            "cshtml",
            "vbhtml",
        };
    }

    protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
    {
        return new RazorView(controllerContext, partialPath,layoutPath: null, 
                                                            runViewStartPages: false, 
                                                            viewStartFileExtensions: FileExtensions, 
                                                            viewPageActivator: ViewPageActivator)
        {
            DisplayModeProvider = DisplayModeProvider
        };
    }

    protected override IView CreateView(ControllerContext controllerContext, 
                                                            string viewPath, 
                                                            string masterPath)
    {
        var view = new RazorView(controllerContext, viewPath,layoutPath: masterPath, 
                                                             runViewStartPages: true, 
                                                             viewStartFileExtensions: FileExtensions, 
                                                             viewPageActivator: ViewPageActivator)
        {
            DisplayModeProvider = DisplayModeProvider
        };
        return view;
    }
}

We can change the values of above properties in a view engine to change search location of views. Let’s write some code to change default search location of views of a view engine.

  • Create a class, derive it RazorViewEngine class (or) any view engine base class
  • Change the values of properties (ViewLocationFormats, MasterLocationFormats, PartialViewLocationFormats, AreaViewLocationFormats, AreaMasterLocationFormats, AreaPartialViewLocationFormats) with new locations
  • Clear all the existing View Engines using ViewEngines.Engines.Clear() method (so existing view engines don’t interfere with new locations)
  • Add the newly created class to view engine collection using ViewEngines.Engines.Add() in Application_Start() method in Global.asax
using System.Web.Mvc;

public class CustomViewLocationRazorViewEngine : RazorViewEngine
{
    public CustomViewLocationRazorViewEngine()
    {
        ViewLocationFormats = new[] 
        {
            "~/RazorViews/{1}/{0}.cshtml", "~/RazorViews/{1}/{0}.vbhtml",
            "~/RazorViews/Common/{0}.cshtml", "~/RazorViews/Common/{0}.vbhtml"
        };

        MasterLocationFormats = new[] 
        {
            "~/RazorViews/{1}/{0}.cshtml", "~/RazorViews/{1}/{0}.vbhtml",
            "~/RazorViews/Common/{0}.cshtml", "~/RazorViews/Common/{0}.vbhtml"
        };

        PartialViewLocationFormats = new[] 
        {
            "~/RazorViews/{1}/{0}.cshtml", "~/RazorViews/{1}/{0}.vbhtml",
            "~/RazorViews/Common/{0}.cshtml", "~/RazorViews/Common/{0}.vbhtml"
        };
    }
}
protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    ViewEngines.Engines.Clear();
    var viewEngine = new CustomViewLocationRazorViewEngine();
    ViewEngines.Engines.Add(viewEngine);

    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
}

In above code snippet we overriding the razor view engine default search location of views with our own paths, we can implement the same with any other view engine.

2 thoughts on “Configure The Views Search Locations in ASP.NET MVC

Leave a Reply