Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

Physical Directories vs. MVC View Paths


:P
On this page:

This post falls into the bucket of operator error on my part, but I want to share this anyway because it describes an issue that has bitten me a few times now and writing it down might keep it a little stronger in my mind.

I've been working on an MVC project the last few days, and at the end of a long day I accidentally moved one of my View folders from the MVC Root Folder to the project root. It must have been at the very end of the day before shutting down because tests and manual site navigation worked fine just before I quit for the night. I checked in changes and called it a night.

Next day I came back, started running the app and had a lot of breaks with certain views. Oddly custom routes to these controllers/views worked, but stock /{controller}/{action} routes would not. After a bit of spelunking I realized that "Hey one of my View Folders is missing", which made some sense given the error messages I got. I looked in the recycle bin - nothing there, so rather than try to figure out what the hell happened, just restored from my last SVN checkin. At this point the folders are back… but… view access  still ends up breaking for this set of views.

Specifically I'm getting the Yellow Screen of Death with:

CS0103: The name 'model' does not exist in the current context

Here's the full error:

Server Error in '/ClassifiedsWeb' Application.

Compilation Error

Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately.
Compiler Error Message: CS0103: The name 'model' does not exist in the current context
Source Error:

Line 1:  @model ClassifiedsWeb.EntryViewModel
Line 2:  @{
Line 3:      ViewBag.Title =  Model.Entry.Title + " - " +  ClassifiedsBusiness.App.Configuration.ApplicationName;

Source File: c:\Projects2010\Clients\GorgeNet\Classifieds\ClassifiedsWeb\Classifieds\Show.cshtml    Line: 1

Compiler Warning Messages:

Show Detailed Compiler Output:

Show Complete Compilation Source:


Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.272

Here's what's really odd about this error: The views now do exist in the /Views/Classifieds folder of the project, but it appears like MVC is trying to execute the views directly. This is getting pretty weird, man!

So I hook up some break points in my controllers to see if my controller actions are getting fired - and sure enough it turns out they are not - but only for those views that were previously 'lost' and then restored from SVN. WTF? At this point I'm thinking that I must have messed up one of the config files, but after some more spelunking and realizing that all the other Controller views work, I give up that idea. Config's gotta be OK if other controllers and views are working.

Root Folders and MVC Views don't mix

As I mentioned the problem was the fact that I inadvertantly managed to drag my View folder to the root folder of the project.

Here's what this looks like in my FUBAR'd project structure after I copied back /Views/Classifieds folder from SVN:

MovedCopy

There's the actual root folder in the /Views folder and the accidental copy that sits of the root. I of course did not notice the /Classifieds folder at the root because it was excluded and didn't show up in the project. Now, before you call me a complete idiot remember that this happened by accident - an accidental drag probably just before shutting down for the night. :-)

So why does this break? MVC should be happy with views in the /Views/Classifieds folder right?

While MVC might be happy, IIS is not. The fact that there is a physical folder on disk takes precedence over MVC's routing. In other words if a URL exists that matches a route the pysical path is accessed first. What happens here is that essentially IIS is trying to execute the .cshtml pages directly without ever routing to the Controller methods. In the error page I showed above my clue should have been that the view was served as:

c:\Projects2010\Clients\GorgeNet\Classifieds\ClassifiedsWeb\Classifieds\Show.cshtml

rather than

c:\Projects2010\Clients\GorgeNet\Classifieds\ClassifiedsWeb\Views\Classifieds\Show.cshtml

But of course I didn't notice that right away, just skimming to the end and looking at the file name.

The reason that

/classifieds/list

actually fires that file is that the ASP.NET Web Pages engine looks for physical files on disk that match a path. IOW, when calling Web Pages you drop the .cshtml off the Razor page and IIS will serve that just fine. So: /classifieds/list looks and tries to find /classifieds/list.cshtml and executes that script. And that is exactly what's happening. Web Pages is trying to execute the .cshtml file and it fails because Web Pages knows nothing about the @model tag which is an MVC specific template extension.

This is why my breakpoints in the controller methods didn't fire and it also explains why the error mentions that the @model key word is invalid (@model is an MVC provided template enhancement to the Razor Engine).

The solution of course is super simple: Delete the accidentally created root folder and the problem is solved.

Routing and Physical Paths

I've run into problems with this before actually. In the past I've had a number of applications that had a physical /Admin folder which also would conflict with an MVC Admin controller. More than once I ended up wondering why the index route (/Admin/) was not working properly. If a physical /Admin folder exists /Admin will not route to the Index action (or whatever default action you have set up, but instead try to list the directory or show the default document in the folder. The only way to force the index page through MVC is to explicitly use /Admin/Index. Makes perfect sense once you realize the physical folder is there, but that's easy to forget in an MVC application.

As you might imagine after a few times of running into this I gave up on the Admin folder and moved everything into MVC views to handle those operations. Still it's one of those things that can easily bite you, because the behavior and error messages seem to point at completely different  problems.

Moral of the story is: If you see routing problems where routes are not reaching obvious controller methods, always check to make sure there's isn't a physical path being mapped by IIS instead. That way you won't feel stupid like I did after trying a million things for about an hour before discovering my sloppy mousing behavior :-)

Posted in MVC  IIS7  

The Voices of Reason


 

Vitaly
April 06, 2012

# re: Physical Directories vs. MVC View Paths

Rick,
Setting RouteCollection.RouteExistingFiles to false should solve such problems.

Steven
April 06, 2012

# re: Physical Directories vs. MVC View Paths

That's one of the reasons I always enable 'show all files' for each and every project I work on.

Nathan
April 06, 2012

# re: Physical Directories vs. MVC View Paths

Nice write up.

Preventing accidental drag and drop is nice to have for large projects. Sometime you know you dragged a folder but you don't even know where you dropped it.

http://visualstudiogallery.msdn.microsoft.com/d491911d-97f3-4cf6-87b0-6a2882120acf/

-Nathan

Ryan
April 11, 2012

# re: Physical Directories vs. MVC View Paths

We're bound to mess up, but at least it wasn't published to production. ; ) I'm looking into using MVC so this is a great insight and one mishap I hope to avoid. Thanks for keeping it real.

Bruno Bertechini
August 14, 2013

# re: Physical Directories vs. MVC View Paths

Vitaly wrote:

Rick,
Setting RouteCollection.RouteExistingFiles to false should solve such problems.

The correct value is "TRUE" so MVC will Route even if a file/folder already exists!

Bruno

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024