Project Structure
Opening an unfamiliar MVC 5 solution can feel like walking into someone else’s kitchen. Once you know what each folder is for, you can find anything in seconds.
On this page · 6 min · Beginner
On this page
- Learning objectives
- Explanation
- Syntax
- Code example
- Step by step
- Real-world use
- Common mistakes
- Best practices
- Remember this
- Practice exercise
- Mini quiz
- Related topics
- My notes
- Reading time
- 6 min
- Difficulty
- Beginner
- Updated
- Oct 29, 2026
Related tutorials
Learning objectives#
- Name the folders of an MVC 5 project and what goes in each
- Explain what Global.asax and App_Start do at startup
- Know the role of the two Web.config files
- Find where packages and bundles are configured
Explanation#
Controllers holds the controller classes; Models holds domain classes and view models; Views holds one subfolder per controller plus Shared for layouts, partials and the Error page.
App_Start contains small static classes that configure the app: RouteConfig (URLs), FilterConfig (global filters), BundleConfig (CSS/JS bundles) and, with Web API or Identity, WebApiConfig and Startup.Auth.
Global.asax.cs is the application class. Its Application_Start method runs once when IIS starts the app and calls the App_Start classes. It can also handle Application_Error and session events.
The root Web.config holds connection strings, app settings, authentication, compilation and assembly binding redirects. A second Web.config inside Views configures Razor and blocks direct requests to .cshtml files.
Content holds CSS and images, Scripts holds JavaScript (jQuery, validation), fonts holds web fonts, and packages.config lists NuGet packages. bin holds the compiled DLLs that IIS loads.
Syntax#
ShopSite/
├── App_Start/ BundleConfig.cs FilterConfig.cs RouteConfig.cs
├── Content/ Site.css bootstrap.css
├── Controllers/ HomeController.cs ProductsController.cs
├── Models/ Product.cs ProductViewModel.cs
├── Scripts/ jquery-3.7.1.js jquery.validate.js
├── Views/
│ ├── Home/ Index.cshtml
│ ├── Products/ Index.cshtml Details.cshtml
│ ├── Shared/ _Layout.cshtml Error.cshtml
│ ├── _ViewStart.cshtml
│ └── Web.config (Razor settings)
├── Global.asax(.cs)
├── packages.config
└── Web.config
Code example: What happens at startup#
// Global.asax.cs
using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace ShopSite
{
public class MvcApplication : HttpApplication
{
// Runs once, when IIS starts (or recycles) the application
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
Application["StartedAt"] = "application started";
}
}
}
// Controllers/HomeController.cs
using System.Web.Mvc;
namespace ShopSite.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Title = "Home";
ViewBag.Started = HttpContext.Application["StartedAt"];
ViewBag.Routes = System.Web.Routing.RouteTable.Routes.Count;
ViewBag.Filters = GlobalFilters.Filters.Count;
return View();
}
}
}
@* Views/Home/Index.cshtml *@
<p>Startup: @ViewBag.Started</p>
<p>Routes registered: @ViewBag.Routes</p>
<p>Global filters: @ViewBag.Filters</p>
GET / 200 OK <p>Startup: application started</p> <p>Routes registered: 2</p> <p>Global filters: 1</p>
This example is illustrative. Copy or download it and run it in your own environment; the expected output is shown above.
Step-by-step explanation#
- IIS loads the DLLs from bin and creates the MvcApplication class named in Global.asax.
- Application_Start registers areas, global filters (HandleErrorAttribute) and routes.
- The route table now holds 2 routes: the ignore rule for .axd files and the Default route. MapMvcAttributeRoutes adds entries only for controllers that use [Route].
- Each request then uses that configuration; Application_Start does not run again until the app pool recycles.
Real-world use case#
A developer is asked why a new page’s URL does not work. Knowing the structure, they go straight to App_Start/RouteConfig.cs, see a catch-all route registered before theirs, and fix the order in two minutes.
Common mistakes#
Without it Razor does not know its base class and namespaces, and every view fails with “The name "model" does not exist”. It also stops .cshtml files being downloadable.
Controllers are created for every request. One-time setup belongs in Application_Start or an App_Start class.
bin is rebuilt on every compile and publish. Change the source and rebuild instead.
Best practices#
- Keep App_Start classes small and focused
- Group views by controller and share layouts and partials in Views/Shared
- Put view models in a ViewModels folder once Models grows
- Use Web.Release.config transforms for production settings
Remember this#
Global.asax → Application_Start → App_Start classes.
Views/{Controller}/ and Views/Shared/.
Two Web.configs: app settings at the root, Razor settings in Views.
Practice exercise#
Your task: Add a BundleConfig that bundles Content/site.css into ~/Content/css and register it in Application_Start.
Need a hint?
Show solution
// App_Start/BundleConfig.cs
using System.Web.Optimization;
namespace ShopSite
{
public class BundleConfig
{
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/site.css"));
bundles.Add(new ScriptBundle("~/bundles/jquery").Include("~/Scripts/jquery-{version}.js"));
}
}
}
// Global.asax.cs
using System.Web;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace ShopSite
{
public class MvcApplication : HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
}
Mini quiz#
Which method runs once when the application starts?
Want more? Practise all ASP.NET MVC MCQs · ASP.NET MVC interview questions
My notes#
Saved in this browser.