I have an MVC5 project with multiple areas. I have a default area (named Default
) and within it, a default controller (named DefaultController
). This is accessible at site route.
[RouteArea]
public class DefaultController : Controller
{
[Route]
public ActionResult Index()
{
return View("Index");
}
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.LowercaseUrls = true;
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Default", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "MyProject.Areas.Default.Controllers" }
);
}
The controller is correctly loaded, but the view (located at Areas/Default/Views/Default/Index.cshtml
) cannot be found. Why is MVC not looking in the right place?
The view 'Index' or its master was not found or no view engine supports the searched locations. The following locations were searched:
~/Areas/Controllers/Views/Default/Index.aspx
~/Areas/Controllers/Views/Default/Index.ascx
~/Areas/Controllers/Views/Shared/Index.aspx
~/Areas/Controllers/Views/Shared/Index.ascx
~/Views/Default/Index.aspx
~/Views/Default/Index.ascx
~/Views/Shared/Index.aspx
~/Views/Shared/Index.ascx
~/Areas/Controllers/Views/Default/Index.cshtml
~/Areas/Controllers/Views/Default/Index.vbhtml
~/Areas/Controllers/Views/Shared/Index.cshtml
~/Areas/Controllers/Views/Shared/Index.vbhtml
~/Views/Default/Index.cshtml
~/Views/Default/Index.vbhtml
~/Views/Shared/Index.cshtml
~/Views/Shared/Index.vbhtml
I see two options:
- Get explicit and specify the
areaName
with [RouteArea("Default", AreaPrefix = "")]
where "Default" is the name of your area and setting AreaPrefix = ""
gets the area in the root of your application.
- Change the namespace your controller is in so that the last part matches your area's name.
Note that if your area's prefix is the root ("") you need to be very explicit with your action [Route("...")]
attributes so that they don't greedily claim all of the routes in your application, and leave you scratching your head for a whole other slew of reasons.
Background
When the RouteAreaAttribute
is applied without supplying the areaName
or the areaName
supplied is null you may get this odd error, or your action may use an incorrect view (if for example you have created ~/Views/Shared/Index.cshtml
for some other purpose).
The RouteAreaAttribute.AreaName
docs are helpful:
(The AreaName property) gets the area name to set for all the routes defined in the controller. If the value is null, an attempt will be made to infer the area name from the target controller's namespace.
By applying [RouteArea]
to your controller the areaName
defaults to null which causes some logic to take over that will "infer the area name from the target controller's namespace." Apparently inferring involves grabbing the last part of the namespace, traditionally that part is "Controllers" because MVC developers have been taught to create their controllers in the Controllers folder, and by default Visual Studio determines a C# class' namespace based on folder structure.
To prove my point you can change the DefaultController's namespace from MyProject.Areas.Default.Controllers
to MyProject.Areas.Default
, chopping off ".Controllers". If you rebuild and visit that action it will look for views under the "Areas/Default" folder like we all expect:
~/Areas/Default/Views/Default/Index.aspx
~/Areas/Default/Views/Default/Index.ascx
...
Instead of under the "Areas/Controllers" folder like no one expects:
~/Areas/Controllers/Views/Default/Index.aspx
~/Areas/Controllers/Views/Default/Index.ascx
...
I get why the developers chose to do it this way, but it's unfortunate they ignored how we've been taught to organize ASP.NET MVC code and how Visual Studio adds areas and controllers to projects. It's not exactly a bug, instead it's something more subtle and frustrating than that.
There could be a number of issues here. 1st off when you declare an area there is a separate file that contains the route to that area. You need to look for DefaultAreaRegistration.cs file (should be in your Areas/Default folder) and check the area route in there. It should be something like this:
context.MapRoute(
"Default_default",
"Default/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
Notice 2 things here. There is a clear reference in the path to the Area - Default/{controller}... This is how MVC knows you are targeting a specific area in this case area named Default. 2nd thing to notice is the name of the route is Default_default. The reason for this is that site Default route is also named DEFAULT. Look in your RouteConfig.cs file and you will see the name of the route is default. So if you name two routes the same obviously they will overwrite each other.
I suspect one of these two things is the cause of your problem...