This post is about 60% fact and 40% opinion. Not a bad ratio. I’ve been working a lot in ASP.NET MVC lately building up a set of classes to be reused across multiple sites in the near future. A few of these legos are small view classes that inherit from WebFormViewEngine
. They all do small things, like adding additional file search paths, loading views from resources and fetching views from remote storage. Since you can stack view engines in ViewEngines.Engines
, you can add and number of these engines together to get the functionality you want on a site per site basis. First engine to claim it can service a view wins. Nothing special there really.
I stumbled across something today that seems obvious in retrospect that leads me to the opinion that the default view/partial view search patterns are broken. If you crack open Reflector.NET and point it towards WebFormViewEngine
, you’ll see that ViewLocationFormats
and PartialViewLocationFormats
are declared the same:
~/Views/{1}/{0}.aspx
~/Views/{1}/{0}.ascx
~/Views/Shared/{0}.aspx
~/Views/Shared/{0}.ascx
In other words, for the
Index
action on the Home
controller, the following paths are searched:~/Views/Home/Index.aspx
~/Views/Home/Index.ascx
~/Views/Shared/Index.aspx
~/Views/Shared/Index.ascx
This all seems well and good until you happen to have a partial view that has the same name as an action that either happens to be shared, or the parti is being overridden in the local controllers folder. Here’s a somewhat plausible example: A
Search
controller with an Items
action:~/Views/Search/Items.aspx
(the view page for the Items action)
~/Views/Home/Index.ascx
(possibly an items partial view customized just for this controller/action)
~/Views/Shared/Items.ascx
(the shared partial view to render items in many controllers/actions/views)
This is where the problems start. By default, a call to
RenderPartial("Items")
within the Items.aspx
page is going to find the Items.aspx
file first and not the Items.ascx
. I’ve never ever seen anyone in the wild put partial views in an aspx file nor have I ever seen anyone put a full action view in an ascx file. So why on earth did they all them to be interchangeable in the location paths? Broken I say. Broken.
The good news is that it’s super easy to tweak these kinds of things in ASP.NET MVC. Inherit from WebFormViewEngine
and set your own ViewLocationFormats
and PartialViewLocationFormats
. I my case I decided to force the issue explicitly. ViewLocationFormats
only looks for aspx files and PartialViewLocationFormats
only look for ascx
files. Now we can override shared partial views locally in controller folders without worrying about name collisions.