All articles

Getting Started with ASP.NET Core MVC

Create your first ASP.NET Core MVC application on .NET 10 — project structure explained, Program.cs walkthrough, default routing, and your own model, controller, and view.

0 · log in to like, save & follow Share on LinkedIn Share on X

This tutorial creates your first ASP.NET Core MVC application on .NET 10, walks through every folder and file the template gives you, and follows one request through the MVC pattern — so the project structure stops being a mystery and becomes a map. Everything here works the same on .NET 8 and 9; only the target framework differs.

cover

What MVC means in ASP.NET Core

MVC separates an application into three responsibilities:

  • Model — your data and business logic: entity classes, view models, services.
  • View — Razor templates (.cshtml) that render HTML from model data.
  • Controller — receives the HTTP request, works with models, and picks the view (or other action result) to return.

A request for /Products/Details/5 flows: routing → ProductsController.Details(5) → the controller loads the model → returns View(model) → Razor renders Views/Products/Details.cshtml → HTML goes back to the browser.

Create the project

From the terminal:

dotnet new mvc -n GeekStore.Mvc
cd GeekStore.Mvc
dotnet run

In Visual Studio: File → New → Project → ASP.NET Core Web App (Model-View-Controller), name it GeekStore.Mvc, pick the latest framework.

dotnet run prints the URL it is listening on — open it and the template home page renders. That is a complete, working MVC app before you have written a line.

The project structure

GeekStore.Mvc/
├── Controllers/
│   └── HomeController.cs
├── Models/
│   └── ErrorViewModel.cs
├── Views/
│   ├── Home/            Index.cshtml, Privacy.cshtml
│   ├── Shared/          _Layout.cshtml, Error.cshtml
│   ├── _ViewImports.cshtml
│   └── _ViewStart.cshtml
├── wwwroot/             css/, js/, lib/ — static files
├── appsettings.json
└── Program.cs
  • Controllers/ — one class per resource; MVC finds them by the Controller suffix.
  • Views/ — one folder per controller; a view file per action. Shared/ holds layouts and partials every page can use.
  • _Layout.cshtml — the page chrome (header, nav, footer); each view's output is injected where the layout calls @RenderBody().
  • _ViewStart.cshtml — runs before every view; this is where the default layout is assigned.
  • _ViewImports.cshtml@using directives and tag helpers shared by all views.
  • wwwroot/ — the only folder served directly to browsers: CSS, JavaScript, images.
  • appsettings.json — configuration (connection strings, logging), with appsettings.Development.json overriding it during development.

Program.cs — the application in one file

The template's entire startup:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.MapStaticAssets();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}")
    .WithStaticAssets();

app.Run();

Two halves: services (everything before Build()) register what the app can use — MVC itself, and later your own services via dependency injection. Middleware (everything after) is the request pipeline, in order: error handling, HTTPS redirect, routing, authorization, static files.

The route pattern {controller=Home}/{action=Index}/{id?} is why / runs HomeController.Index() and /Home/Privacy runs Privacy() — controller and action default when the URL omits them, and id is optional. More in ASP.NET Core MVC routing.

MapStaticAssets (new since .NET 9) replaces the old UseStaticFiles for app assets — it fingerprints and compresses everything in wwwroot at build time, so browsers cache aggressively and still pick up new versions on deploy.

Add your own page — model, controller, view

ModelModels/Product.cs:

namespace GeekStore.Mvc.Models;

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public decimal Price { get; set; }
}

ControllerControllers/ProductsController.cs:

using GeekStore.Mvc.Models;
using Microsoft.AspNetCore.Mvc;

namespace GeekStore.Mvc.Controllers;

public class ProductsController : Controller
{
    public IActionResult Index()
    {
        List<Product> products =
        [
            new() { Id = 1, Name = "Galaxy A15 Mobile", Price = 390 },
            new() { Id = 2, Name = "Air Pods", Price = 200 },
        ];
        return View(products);
    }
}

ViewViews/Products/Index.cshtml:

@model List<Product>

<h1>Products</h1>
<table class="table">
    <thead><tr><th>Name</th><th>Price</th></tr></thead>
    <tbody>
    @foreach (var p in Model)
    {
        <tr><td>@p.Name</td><td>@p.Price.ToString("0.00")</td></tr>
    }
    </tbody>
</table>

Run again and browse to /Products — the default route maps it to ProductsController.Index(), which hands the list to the view. That is the whole MVC loop, and every page you ever build in this framework is a variation of these three files.

Where to go next

Enjoyed this article? Get the best GeeksArray articles in your inbox — once a week, no spam, unsubscribe anytime.

Comments (0)

Log in to join the conversation.

No comments yet — be the first to share your thoughts.