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:
Markdown Monster - The Markdown Editor for Windows

Project Navigation and File Nesting in ASP.NET MVC Projects


:P
On this page:

More and more I’m finding myself getting lost in the files in some of my larger Web projects. There’s so much freaking content to deal with – HTML Views, several derived CSS pages, page level CSS, script libraries, application wide scripts and page specific script files etc. etc. Thankfully I use Resharper and the Ctrl-T Go to Anything which autocompletes you to any file, type, member rapidly. Awesome except when I forget – or when I’m not quite sure of the name of what I’m looking for. Project navigation is still important.

Sometimes while working on a project I seem to have 30 or more files open and trying to locate another new file to open in the solution often ends up being a mental exercise – “where did I put that thing?” It’s those little hesitations that tend to get in the way of workflow frequently.

To make things worse most NuGet packages for client side frameworks and scripts, dump stuff into folders that I generally don’t use. I’ve never been a fan of the ‘Content’ folder in MVC which is just an empty layer that doesn’t serve much of a purpose. It’s usually the first thing I nuke in every MVC project. To me the project root is where the actual content for a site goes – is there really a need to add another folder to force another path into every resource you use? It’s ugly and also inefficient as it adds additional bytes to every resource link you embed into a page.

Alternatives

I’ve been playing around with different folder layouts recently and found that moving my cheese around has actually made project navigation much easier. In this post I show a couple of things I’ve found useful and maybe you find some of these useful as well or at least get some ideas what can be changed to provide better project flow.

The first thing I’ve been doing is add a root Code folder and putting all server code into that. I’m a big fan of treating the Web project root folder as my Web root folder so all content comes from the root without unneeded nesting like the Content folder. By moving all server code out of the root tree (except for Code) the root tree becomes a lot cleaner immediately as you remove Controllers, App_Start, Models etc. and move them underneath Code. Yes this adds another folder level for server code, but it leaves only code related things in one place that’s easier to jump back and forth in. Additionally I find myself doing a lot less with server side code these days, more with client side code so I want the server code separated from that.

The root folder itself then serves as the root content folder. Specifically I have the Views folder below it, as well as the Css and Scripts folders which serve to hold only common libraries and global CSS and Scripts code. These days of building SPA style application, I also tend to have an App folder there where I keep my application specific JavaScript files, as well as HTML View templates for client SPA apps like Angular.

Here’s an example of what this looks like in a relatively small project:

Project Layout

The goal is to keep things that are related together, so I don’t end up jumping around so much in the solution to get to specific project items. The Code folder may irk some of you and hark back to the days of the App_Code folder in non Web-Application projects, but these days I find myself messing with a lot less server side code and much more with client side files – HTML, CSS and JavaScript. Generally I work on a single controller at a time – once that’s open it’s open that’s typically the only server code I work with regularily. Business logic lives in another project altogether, so other than the controller and maybe ViewModels there’s not a lot of code being accessed in the Code folder. So throwing that off the root and isolating seems like an easy win.

Nesting Page specific content

In a lot of my existing applications that are pure server side MVC application perhaps with some JavaScript associated with them , I tend to have page level javascript and css files. For these types of pages I actually prefer the local files stored in the same folder as the parent view. So typically I have a .css and .js files with the same name as the view in the same folder.

This looks something like this:

filenesting

In order for this to work you have to also make a configuration change inside of the /Views/web.config file, as the Views folder is blocked with the BlockViewHandler that prohibits access to content from that folder. It’s easy to fix by changing the path from * to *.cshtml or *.vbhtml so that view retrieval is blocked:

<system.webServer> <handlers> <remove name="BlockViewHandler"/> <add name="BlockViewHandler" path="*.cshtml" verb="*"
preCondition="integratedMode"
type="System.Web.HttpNotFoundHandler" /> </handlers> </system.webServer>

With this in place, from inside of your Views you can then reference those same resources like this:

<link href="~/Views/Admin/QuizPrognosisItems.css" rel="stylesheet" />

and

<script src="~/Views/Admin/QuizPrognosisItems.js"></script>

which works fine. JavaScript and CSS files in the Views folder deploy just like the .cshtml files do and can be referenced from this folder as well.

Making this happen is not really as straightforward as it should be with just Visual Studio unfortunately, as there’s no easy way to get the file nesting from the VS IDE directly (you have to modify the .csproj file).

However, Mads Kristensen has a nice Visual Studio Add-in that provides file nesting via a short cut menu option. Using this you can select each of the ‘child’ files and then nest them under a parent file. In the case above I select the .js and .css files and nest them underneath the .cshtml view.

FileNesting[1]

I was even toying with the idea of throwing the controller.cs files into the Views folder, but that’s maybe going a little too far :-) It would work however as Visual Studio doesn’t publish .cs files and the compiler doesn’t care where the files live. There are lots of options and if you think that would make life easier it’s another option to help group related things together.

Are there any downside to this? Possibly – if you’re using automated minification/packaging tools like ASP.NET Bundling or Grunt/Gulp with Uglify, it becomes a little harder to group script and css files for minification as you may end up looking in multiple folders instead of a single folder. But – again that’s a one time configuration step that’s easily handled and much less intrusive then constantly having to search for files in your project.

Client Side Folders

The particular project shown above in the screen shots above is a traditional server side ASP.NET MVC application with most content rendered into server side Razor pages. There’s a fair amount of client side stuff happening on these pages as well – specifically several of these pages are self contained single page Angular applications that deal with 1 or maybe 2 separate views and the layout I’ve shown above really focuses on the server side aspect where there are Razor views with related script and css resources.

For applications that are more client centric and have a lot more script and HTML template based content I tend to use the same layout for the server components, but the client side code can often be broken out differently.

In SPA type applications I tend to follow the App folder approach where all the application pieces that make the SPA applications end up below the App folder.

Here’s what that looks like for me – here this is an AngularJs project:

ScriptProject

In this case the App folder holds both the application specific js files, and the partial HTML views that get loaded into this single SPA page application.

In this particular Angular SPA application that has controllers linked to particular partial views, I prefer to keep the script files that are associated with the views – Angular Js Controllers in this case – with the actual partials. Again I like the proximity of the view with the main code associated with the view, because 90% of the UI application code that gets written is handled between these two files.

This approach works well, but only if controllers are fairly closely aligned with the partials. If you have many smaller sub-controllers or lots of directives where the alignment between views and code is more segmented this approach starts falling apart and you’ll probably be better off with separate folders in js folder. Following Angular conventions you’d have controllers/directives/services etc. folders.

Please note that I’m not saying any of these ways are right or wrong  – this is just what has worked for me and why!

Skipping Project Navigation altogether with Resharper

I’ve talked a bit about project navigation in the project tree, which is a common way to navigate and which we all use at least some of the time, but if you use a tool like Resharper – which has Ctrl-T to jump to anything, you can quickly navigate with a shortcut key and autocomplete search.

Here’s what Resharper’s jump to anything looks like:

ResharperGotoAnything

Resharper’s Goto Anything box lets you type and quick search over files, classes and members of the entire solution which is a very fast and powerful way to find what you’re looking for in your project, by passing the solution explorer altogether. As long as you remember to use (which I sometimes don’t) and you know what you’re looking for it’s by far the quickest way to find things in a project. It’s a shame that this sort of a simple search interface isn’t part of the native Visual Studio IDE.

Work how you like to work

Ultimately it all comes down to workflow and how you like to work, and what makes *you* more productive. Following pre-defined patterns is great for consistency, as long as they don’t get in the way you work. A lot of the default folder structures in Visual Studio for ASP.NET MVC were defined when things were done differently. These days we’re dealing with a lot more diverse project content than when ASP.NET MVC was originally introduced and project organization definitely is something that can get in the way if it doesn’t fit your workflow. So take a look and see what works well and what might benefit from organizing files differently.

As so many things with ASP.NET, as things evolve and tend to get more complex I’ve found that I end up fighting some of the conventions. The good news is that you don’t have to follow the conventions and you have the freedom to do just about anything that works for you.

Even though what I’ve shown here diverges from conventions, I don’t think anybody would stumble over these relatively minor changes and not immediately figure out where things live, even in larger projects. But nevertheless think long and hard before breaking those conventions – if there isn’t a good reason to break them or the changes don’t provide improved workflow then it’s not worth it. Break the rules, but only if there’s a quantifiable benefit.

You may not agree with how I’ve chosen to divert from the standard project structures in this article, but maybe it gives you some ideas of how you can mix things up to make your existing project flow a little nicer and make it easier to navigate for your environment.

Posted in ASP.NET  MVC  

The Voices of Reason


 

Ventsi
July 01, 2014

# re: Project Navigation and File Nesting in ASP.NET MVC Projects

Hi Rick,
What if we use Areas, I'm trying with
  <link href="~/Areas/Common/Views/ControllerName/viewName.min.css" rel="stylesheet" />

and
   <script src="~/Areas/Common/Views/ControllerName/viewName.min.js" type="text/javascript"></script>

with no success, maybe we should register some routes.

Rick Strahl
July 01, 2014

# re: Project Navigation and File Nesting in ASP.NET MVC Projects

@Ventsi - I rarely use areas so I didn't check but I think that should work. Can you access other content cross areas?

Chris Johnson
July 01, 2014

# re: Project Navigation and File Nesting in ASP.NET MVC Projects

Makes sense to me. I have a WPF application with similar pain, and I've found it helpful to create a top-level "MainClasses" folder with all the classes that I have to touch regularly (with subfolders as needed) and a top-level "UtilityClasses" folder that I can stick stuff in that don't normally need maintenance so that I can hide them until I need them. Unconventional, yes, but it helps me work faster. When I hand the project off to someone else at some point in the future, they can re-arrange the files to their heart's content.

Chris Hynes
July 01, 2014

# re: Project Navigation and File Nesting in ASP.NET MVC Projects

How do you handle NuGet package updates that include ~/Content files? Just move the files around?

One of these days NuGet's going to add a configuration for content file locations, but there doesn't seem to be much consensus on the best way to do it: http://nuget.codeplex.com/workitem/1914

Prabu
July 01, 2014

# re: Project Navigation and File Nesting in ASP.NET MVC Projects

Great post - it is something I've been thinking about as well.

A quick question that's slightly off topic regarding the client centric SPA apps that use pure HTML templates. How do you incorporate bundling and minification for the css/js files in them with VS.

I tend to use a MVC project to leverage these features OOTB but they require the use of .cshtml files.

Rick Strahl
July 01, 2014

# re: Project Navigation and File Nesting in ASP.NET MVC Projects

@Chris - even before changing to this particular layout I've not been using the content folder. I've always thought that a lame abstraction. For client side libraries and CSS etc. I tend just download the files from the source and replace them or as you say move them. It's easy enough to do especially if you don't use the content folder for other things.

Yup it'd be nice if you could specify where things go, but really this would have to be dynamic and would require some UI to choose where things go. I don't think there's really an easy one location fits all solution that really addresses all needs. You'd almost need to have a NuGet configuration for each package to make that work. I see the issue. That's why tools like Grunt and Gult exist for NPM/Node - they allow to really customize via a simple code flow where things go. NPM works, but it's also another set of tooling which kind of sucks as well. As it is it seems we're having to rely on too many components to make everything work. Too many diverse technologies just to get the build to go... sigh. I don't see this getting any easier either - it'll get more complex before it gets easier I think. Just look at the Node space to see where this is all headed at the moment. Looking at 1000 line Grunt files is not my idea of fun :-)

@Prabu - For minification I actually just rely on Web Essentials. Page specfic scripts I don't bundle because they're usually one off. Application level scripts I either use the MVC bundling (not a fan though because of all the dependencies that adds to a project), or external tooling like Grunt or in the future Gulp. There are a lot of choices.

I'm hoping the native story for this will get better in VS. Personally I wish there were better automation tooling that worked directly out of VS, but for now I'm firing off Grunt tasks from the build to do this. It works but is yet another toolset to add to everything. I would use bundling but I'm really unhappy with the proliferation of assemblies that adds to your project which impacts load size and startup time. A lot of the startup bloat for MVC comes from all the bundling mess.

PilotBob
July 01, 2014

# re: Project Navigation and File Nesting in ASP.NET MVC Projects

I never liked having a single folder for controllers either with the views way over in another folder. We have tried creating controller folders like we do with views, but this got pretty busy.

I just prefer to have the controller and all it's views in the same folder. But, I don't like that folder being called views or controllers. You can modify the view engine so you can specify the folder that it probes for views. But, leaving it as views works. So, drop the controllers folder and put them in the views folder. It is much cleaner.

Also, I leave script and content as they are. I don't have a problem with them, and NuGet expects them there. But, for our own js I prefer it to not be in scripts, so I create a js folder, but app could work as well. However, I think app is too general. But, whatever works.

Jonathan
July 01, 2014

# re: Project Navigation and File Nesting in ASP.NET MVC Projects

I've done something wrong I guess. I tried this out with my login.cshtml and a login.js in my Views/Account folder. When I run it I get a 404 on the login.js. I can move the login.js out of the Views/Account folder (first un-nesting) and it works fine. It feels like it has a problem referencing js out of the Views folder, but I thought that was what you were doing above.
<script src="~/Views/Account/Login.js"></script>


Interestingly enough, I use Areas and in each area I have a js folder and that works fine. I like the js folder inside each area for a specific areas js, but I would like to use the nesting and clean up some of the base code.

Jonathan
July 01, 2014

# re: Project Navigation and File Nesting in ASP.NET MVC Projects

I put my login.js into a bundle and it works. I did read that part above about a problem with this and bundling but I wanted the best of both worlds. I'm interested to know how you didn't have a problem, or if there is something you had to turn off or is it just because you don't use any bundling. I'll keep digging. Thanks for the article. I'm always looking for a better way to do things.

Ventsi
July 02, 2014

# re: Project Navigation and File Nesting in ASP.NET MVC Projects

@Rick I found the problem: in latest versions of ASP.NET MVC there is a blocker in web.config not to server any content from Views folder :
  <system.webServer>
    <handlers>
      <remove name="BlockViewHandler"/>
      <add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
    </handlers>
  </system.webServer>

I need to remove the handler to enable serving static files from Views folder.

Rick Strahl
July 02, 2014

# re: Project Navigation and File Nesting in ASP.NET MVC Projects

@Ventsi - you can leave the handler in there but just exclude .cshtml files (or .vbhtml if using VB).
<add name="BlockViewHandler" path="*.cshtml" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />

I totally spaced that I changed this in the project. Adding to the article. Thanks for catching that!

Note that you shouldn't remove the handler - otherwise ASP.NET will try to execute the .cshtml pages as ASP.NET WebPages which would cause unexpected behavior.

Rick Strahl
July 02, 2014

# re: Project Navigation and File Nesting in ASP.NET MVC Projects

@Bob - yup I mentioned in the post I've played with putting the controller into the Views folder for that controller. I've done that in a few projects as well - just dropped the controller into the Views folder. But I find the controller much less of a distraction even in a different folder because there's typically one controller for many views and usually I'm not opening controllers all the time as they just stay open. In a typical work scenario I'm working on one controller at a time, but views OTOH I often work on several at the same time frequently. Agree though - if that makes sense to you, by all means go for that.

I do like the Views folder though. If you want to not have to reference views explicitly by path then the Views folder makes sure MVC finds them by convention. You can change the base folders but the structure still has to stay kind of the same, and to me that more or less makes sense.

If you do something different what is the layout that you use that is different enough to make this better? I'm curious...

Finally if you do move/rename the Views folder to something else do make sure that you protect the .cshtml files. I believe IIS protects that anyway though because of ASP.NET WebPages using them in the first place.

Adrian
July 08, 2014

# re: Project Navigation and File Nesting in ASP.NET MVC Projects

Good stuff Rick. Especially like the nesting. Jimmy Bogard and his buddy Tim Thomas have written about the same issue and come up with the concept of 'feature folders':

http://timgthomas.com/2013/10/feature-folders-in-asp-net-mvc/

Natan
October 03, 2014

# re: Project Navigation and File Nesting in ASP.NET MVC Projects

I like the idea of removing the blockinghandler and using JS and CSS together in the views folder. But after a while, I noticed that I'm not even using MVC views anymore, as pure html files makes most of the templates nowadays, and they go to the "app" folder.

I leave MVC views for cases where I want to pre-process html views with server-generated content. It is not common, but sometimes makes your life easier and the app faster.

Also, I have been using bower instead of nuget for all javascript/css dependencies. This removes any need to care for Script/Contents folder mess and I can just delete those.

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