Oskar, I've written a blog post that addresses your scenario. You can make things work intuitively by a single change in Global.asax (view engine registration). Read about
Areas and multiple view engines.
This technique should be helpful for those who want to transition their application from Aspx to Razor (or maybe between some other pair of view engines). However, once you are done with the transition you should probably stick to just having one view engine
registered to avoid unnecessary searches in the other view engines.
Do you think the only reason to mix view engines is when transitioning between one to another? Is the best practice to have one view engine only? I can understand this for Razor and web forms, since they are very similar in how they work. But suppose you
want to mix different view engines because one view engine solves a certain problem better than the other for some of the views? With the ability in MVC3 to easily choose view engine for each view, I can imagine people mixing view engines quite a bit which
would make this a more common issue. Any thoughts on this?
There is a cost associated with having multiple view engines looking for files on disk, unquestionably. It's a decision you make as a developer what the potential benefits vs. costs are.
Marked as answer by ricka6 on Nov 17, 2010 07:07 PM
To add to what Brad said, it's a best practice (in any sort of development) to remove unused things from your application (be it code, resources, view engines) to keep it as simple and straigthforward as possible. But if you need something then you need
it. Having multiple view engines will have a small initial perf hit, but should not have a signifficant impact throughout the whole life of your app domain. My comment was more about maintaining a clean application.
That's interesting ... but it doesn't seem to work, unless I'm missing something.
When trying to find views that are not part of an area, you get an exception when using the area-specific view engines. Looking in the source at VirtualPathProviderEngine.GetPath(), if there is no area defined, the set of view locations is only made up
of those in ViewLocationFormats ... and that's empty. So, GetPath() throws an exception.
Actually, the problem I had/have is slightly different. I do have a combination of Razor and WebForms as part of a migration ... with a preference for Razor. When looking for a view in an area (which is WebForms in part), it actually found a view of a
matching name in the default non-area locations which was Razor (rather than the WebForms one I expected) ... but then found the master in the area in Razor (because I'm partially migrated). Result: a nasty mess and a mismatch of view and master!
The problem (if problem it is) seems to be that, even when an area is defined, GetPath() falls back to looking in the non-area locations as well. Seems to me that a similar mechanism to the one in controllers to prevent that fallback (perhaps selectively)
would be nice. I don't see an easy way to override that, however.
That's interesting ... but it doesn't seem to work, unless I'm missing something.
Sorry about that, I've fixed the code in my blog post.
adrian pell
Actually, the problem I had/have is slightly different. I do have a combination of Razor and WebForms as part of a migration ... with a preference for Razor. When looking for a view in an area (which is WebForms in part), it actually found a view of a
matching name in the default non-area locations which was Razor (rather than the WebForms one I expected) ... but then found the master in the area in Razor (because I'm partially migrated). Result: a nasty mess and a mismatch of view and master!
The problem (if problem it is) seems to be that, even when an area is defined, GetPath() falls back to looking in the non-area locations as well. Seems to me that a similar mechanism to the one in controllers to prevent that fallback (perhaps selectively)
would be nice. I don't see an easy way to override that, however.
I'm having a hard time understanding how to repro your issue. Could you post a list of the relevenat files that exist in your project, what kind of request you are making, and which ones get chosen by the system (and indicate how that differs from what you
expect)?
Sorry about that, I've fixed the code in my blog post.
Cool - although I guess the presence of that non-existent file name would result in more failed lookups in the file system. Probably for every non-area view lookup.
marcind
I'm having a hard time understanding how to repro your issue. Could you post a list of the relevenat files that exist in your project, what kind of request you are making, and which ones get chosen by the system (and indicate how that differs from what you
expect)?
Sorry - let me try again! Here's the rough filesystem layout ...
Views
... Admin [this is the area name]
... ... Home
... ... ... Index.aspx <--- this is the view I expect to render
... ... Shared
... ... ... _Site.cshtml
... ... ... Site.Master
... Home [this is the controller that is not in a view]
... ... Index.cshtml
... Shared
... ... _Site.cshtml
The view engines are configured (1) Razor (2) WebForms.
When I try to fetch the view for action Admin/Home/Index, here's what I think happens:
Razor view engine looks for Home/Index.cshtml. It tries in the Admin area (fails) then falls back to try in the non-area views, and here it finds Index.cshtml. Of course, this is actually the
wrong view.
Now, Razor looks for the layout and finds _Site.cshtml in the Admin area (this is here because I'm gradually migrating)
Unfortunately, the Admin layout does not have some sections defined that the regular one does, so the view engine fails to render. It would have rendered the wrong thing anyway.
It seems to me that the fallback in GetPath() to look in the non-area view locations is the problem here. I suppose that makes sense when looking for masters and, perhaps, partials - but it seems wrong for the actual base view. I don't see a way to change
the behavior however without a trick similar to yours.
FWIW, the solution you updated does work in this case, although as I said I'd be concerned about the extra file system accesses for the non-existent file (especialy since this is for views in my normal site, as opposed to the area). I just ran Process Monitor
against that, and it does seem to do a bunch of lookups, although most of them are apparently for the compiled version of the non-existent page ... whcih doesn't exist either.
BTW - although this particular problem is expressed in terms of two view engines, the same thing could happen in the case of a single view engine where the appropriate view is missing inside the area (not written yet, etc.) Rather than giving a rendering
error (cannot find view), it could render the wrong thing (from the base, not the area) with potential for mis-matching layouts, models and who knows what else.
I know that avoiding duplicate names is best, but that's not always possible.
I don't know if this worked for other people, but it didn't work for me. I just downloaded MVC3 RC2 yesterday. In this release, neither Razor, nor webforms engine scan Areas for views at all.
I have Area called "Admin" with AdminController inside of that area. I right clicked on the "Index" method of the controller and added an Index view. I made the following change in Global.asax.cs
When I load Admin area in the browser, it seems that both engines are only searching the root view folder and completely ignoring the areas. Basicaly, it just assumes that Admin was just another controller added in the root and doesn't search anywhere else. See
below.
The view 'Index' or its master was not found or no view engine supports the searched locations. The following locations were searched:
~/Views/Admin/Index.cshtml
~/Views/Admin/Index.vbhtml
~/Views/Shared/Index.cshtml
~/Views/Shared/Index.vbhtml
~/Views/Admin/Index.aspx
~/Views/Admin/Index.ascx
~/Views/Shared/Index.aspx
~/Views/Shared/Index.ascx
The view 'Index' or its master was not found or no view engine supports the searched locations. The following locations were searched:
~/Views/Admin/Index.cshtml
~/Views/Admin/Index.vbhtml
~/Views/Shared/Index.cshtml
~/Views/Shared/Index.vbhtml
~/Views/Admin/Index.aspx
~/Views/Admin/Index.ascx
~/Views/Shared/Index.aspx
~/Views/Shared/Index.ascx
This is showing that your Area route is not matched. Try this
context.MapRoute(
"Admin_default",
"Admin/{controller}/{action}/{id}",
new { controller="Admin",action = "Index", id = UrlParameter.Optional }
);
"And whoever is removed away from the Fire and admitted to Paradise, he indeed is successful." (The Holy Quran)
Excellent Windows VPS Hosting Imran Baloch MVP, MVB, MCP, MCTS, MCPD
marcind
Contributor
3344 Points
609 Posts
Microsoft
Re: Locate view bug in MVC3 RC
Nov 15, 2010 06:52 AM|LINK
Oskar, I've written a blog post that addresses your scenario. You can make things work intuitively by a single change in Global.asax (view engine registration). Read about Areas and multiple view engines.
ASP.NET Team
@marcind
Blog
dillenmeiste...
Member
1 Points
6 Posts
Re: Locate view bug in MVC3 RC
Nov 16, 2010 06:56 AM|LINK
Very good and informative blog post!
Quote from the blog post:
Do you think the only reason to mix view engines is when transitioning between one to another? Is the best practice to have one view engine only? I can understand this for Razor and web forms, since they are very similar in how they work. But suppose you want to mix different view engines because one view engine solves a certain problem better than the other for some of the views? With the ability in MVC3 to easily choose view engine for each view, I can imagine people mixing view engines quite a bit which would make this a more common issue. Any thoughts on this?
bradwils
Contributor
5779 Points
691 Posts
Microsoft
Re: Locate view bug in MVC3 RC
Nov 16, 2010 02:24 PM|LINK
There is a cost associated with having multiple view engines looking for files on disk, unquestionably. It's a decision you make as a developer what the potential benefits vs. costs are.
marcind
Contributor
3344 Points
609 Posts
Microsoft
Re: Locate view bug in MVC3 RC
Nov 16, 2010 03:56 PM|LINK
To add to what Brad said, it's a best practice (in any sort of development) to remove unused things from your application (be it code, resources, view engines) to keep it as simple and straigthforward as possible. But if you need something then you need it. Having multiple view engines will have a small initial perf hit, but should not have a signifficant impact throughout the whole life of your app domain. My comment was more about maintaining a clean application.
ASP.NET Team
@marcind
Blog
Adrian Pell
Member
8 Points
4 Posts
Re: Locate view bug in MVC3 RC
Nov 17, 2010 04:50 PM|LINK
Marcin
That's interesting ... but it doesn't seem to work, unless I'm missing something.
When trying to find views that are not part of an area, you get an exception when using the area-specific view engines. Looking in the source at VirtualPathProviderEngine.GetPath(), if there is no area defined, the set of view locations is only made up of those in ViewLocationFormats ... and that's empty. So, GetPath() throws an exception.
Actually, the problem I had/have is slightly different. I do have a combination of Razor and WebForms as part of a migration ... with a preference for Razor. When looking for a view in an area (which is WebForms in part), it actually found a view of a matching name in the default non-area locations which was Razor (rather than the WebForms one I expected) ... but then found the master in the area in Razor (because I'm partially migrated). Result: a nasty mess and a mismatch of view and master!
The problem (if problem it is) seems to be that, even when an area is defined, GetPath() falls back to looking in the non-area locations as well. Seems to me that a similar mechanism to the one in controllers to prevent that fallback (perhaps selectively) would be nice. I don't see an easy way to override that, however.
Adrian
marcind
Contributor
3344 Points
609 Posts
Microsoft
Re: Locate view bug in MVC3 RC
Nov 17, 2010 06:37 PM|LINK
Sorry about that, I've fixed the code in my blog post.
I'm having a hard time understanding how to repro your issue. Could you post a list of the relevenat files that exist in your project, what kind of request you are making, and which ones get chosen by the system (and indicate how that differs from what you expect)?
ASP.NET Team
@marcind
Blog
Adrian Pell
Member
8 Points
4 Posts
Re: Locate view bug in MVC3 RC
Nov 17, 2010 07:13 PM|LINK
Cool - although I guess the presence of that non-existent file name would result in more failed lookups in the file system. Probably for every non-area view lookup.
Sorry - let me try again! Here's the rough filesystem layout ...
Views
... Admin [this is the area name]
... ... Home
... ... ... Index.aspx <--- this is the view I expect to render
... ... Shared
... ... ... _Site.cshtml
... ... ... Site.Master
... Home [this is the controller that is not in a view]
... ... Index.cshtml
... Shared
... ... _Site.cshtml
The view engines are configured (1) Razor (2) WebForms.
When I try to fetch the view for action Admin/Home/Index, here's what I think happens:
It seems to me that the fallback in GetPath() to look in the non-area view locations is the problem here. I suppose that makes sense when looking for masters and, perhaps, partials - but it seems wrong for the actual base view. I don't see a way to change the behavior however without a trick similar to yours.
FWIW, the solution you updated does work in this case, although as I said I'd be concerned about the extra file system accesses for the non-existent file (especialy since this is for views in my normal site, as opposed to the area). I just ran Process Monitor against that, and it does seem to do a bunch of lookups, although most of them are apparently for the compiled version of the non-existent page ... whcih doesn't exist either.
Adrian
Adrian Pell
Member
8 Points
4 Posts
Re: Locate view bug in MVC3 RC
Nov 17, 2010 07:21 PM|LINK
BTW - although this particular problem is expressed in terms of two view engines, the same thing could happen in the case of a single view engine where the appropriate view is missing inside the area (not written yet, etc.) Rather than giving a rendering error (cannot find view), it could render the wrong thing (from the base, not the area) with potential for mis-matching layouts, models and who knows what else.
I know that avoiding duplicate names is best, but that's not always possible.
Adrian
mlofny
Member
4 Points
2 Posts
Re: Locate view bug in MVC3 RC
Dec 28, 2010 03:58 PM|LINK
I don't know if this worked for other people, but it didn't work for me. I just downloaded MVC3 RC2 yesterday. In this release, neither Razor, nor webforms engine scan Areas for views at all.
I have Area called "Admin" with AdminController inside of that area. I right clicked on the "Index" method of the controller and added an Index view. I made the following change in Global.asax.cs
protected void Application_Start()
{
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new RazorViewEngine());
ViewEngines.Engines.Add(new WebFormViewEngine());
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
When I load Admin area in the browser, it seems that both engines are only searching the root view folder and completely ignoring the areas. Basicaly, it just assumes that Admin was just another controller added in the root and doesn't search anywhere else. See below.
The view 'Index' or its master was not found or no view engine supports the searched locations. The following locations were searched:
~/Views/Admin/Index.cshtml
~/Views/Admin/Index.vbhtml
~/Views/Shared/Index.cshtml
~/Views/Shared/Index.vbhtml
~/Views/Admin/Index.aspx
~/Views/Admin/Index.ascx
~/Views/Shared/Index.aspx
~/Views/Shared/Index.ascx
What am I missing here?
imran_ku07
All-Star
45815 Points
7698 Posts
MVP
Re: Locate view bug in MVC3 RC
Dec 28, 2010 04:50 PM|LINK
This is showing that your Area route is not matched. Try this
context.MapRoute(
"Admin_default",
"Admin/{controller}/{action}/{id}",
new { controller="Admin",action = "Index", id = UrlParameter.Optional }
);
Excellent Windows VPS Hosting
Imran Baloch MVP, MVB, MCP, MCTS, MCPD