ASP.NET MVC · Foundations

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.

Beginner 6 min Lesson 4 of 37Covers ASP.NET MVC 5.2 on .NET Framework 4.8.
0/37 in ASP.NET MVC
On this page · 6 min · Beginner

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#

Text Illustrative
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#

C#Global.asax.cs Illustrative
// 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>
Expected output
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#

  1. IIS loads the DLLs from bin and creates the MvcApplication class named in Global.asax.
  2. Application_Start registers areas, global filters (HandleErrorAttribute) and routes.
  3. 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].
  4. 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#

Deleting Views/Web.config

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.

Putting startup code in a controller constructor

Controllers are created for every request. One-time setup belongs in Application_Start or an App_Start class.

Editing files under bin

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?
Use bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/site.css")) and call BundleConfig.RegisterBundles(BundleTable.Bundles).
Show solution
C# Illustrative
// 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#

Project Structure quizMultiple choice Question 1 of 3

Which method runs once when the application starts?

Score so far: 0/0

Want more? Practise all ASP.NET MVC MCQs · ASP.NET MVC interview questions

My notes#

Saved in this browser.

Progress is saved in this browser. Sign in to sync.