C# ASP.NET Comprehensive Guide

1 · What Is ASP.NET (Core)?

ASP.NET Core is the modern, cross‑platform web‑development stack for the .NET ecosystem (latest LTS: .NET 8, Nov 2024). It unifies MVC, Web API and Razor Pages under a single modular runtime that runs on Windows, Linux and macOS.

Note: Legacy “ASP.NET Framework” (WebForms / MVC 5) targets Windows‑only .NET Framework 4.x. New apps should use ASP.NET Core.

2 · Project Templates & CLI

// Create a Web API project targeting .NET 8 (LTS)
dotnet new webapi -n MyApi -f net8.0

// Razor Pages site
dotnet new razor -n MySite

// Minimal API
dotnet new web -n TodoMinimal

The dotnet CLI scaffolds opinionated folders:

FolderPurpose
Controllers/HTTP endpoints (MVC or API)
Pages/Razor Pages (.cshtml)
Data/Entity Framework Core DbContext & Migrations
Program.csHost builder & service registration

3 · Program.cs – Bootstrap & Middleware


var builder = WebApplication.CreateBuilder(args);

// 1 · Service registration
builder.Services.AddControllers();              // MVC & Web API
builder.Services.AddRazorPages();               // Razor
builder.Services.AddDbContext<AppDb>();        // Data

var app = builder.Build();

// 2 · HTTP pipeline
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();
app.MapRazorPages();

app.Run();

Two key orchestration methods: WebApplication.CreateBuilder (configures DI, logging, config) and app.Run (starts Kestrel).

4 · Controllers & Actions


[ApiController]
[Route("api/[controller]")]
public class TodoController : ControllerBase
{
    private readonly ITodoRepository _repo;
    public TodoController(ITodoRepository repo) => _repo = repo;

    // GET /​api/todo
    [HttpGet]
    public IEnumerable<TodoItem> GetAll() => _repo.All();

    // POST /​api/todo
    [HttpPost]
    public ActionResult<TodoItem> Create(TodoItem dto)
    {
        var created = _repo.Add(dto);
        return CreatedAtAction(nameof(GetById), new { id = created.Id }, created);
    }

    [HttpGet("{id:int}")]
    public ActionResult<TodoItem> GetById(int id) =>
        _repo.Find(id) is { } item ? item : NotFound();
}

Decorate methods with routing attributes such as [HttpGet], [HttpPost], and use ActionResult<T> for rich status codes.

5 · Endpoint Routing

Conventional vs Attribute Routing

Route Constraints Examples


// id must be GUID
[HttpGet("orders/{id:guid}")]
public IActionResult Get(Guid id) { … }

// slug must match regex
[HttpGet("blog/{slug:regex(^[a-z0-9-]+$)}")]
public IActionResult Post(string slug) { … }

6 · Razor Views & Razor Pages

Razor combines C# and HTML using @ syntax:


@page
@model IndexModel

<h2>Welcome @Model.UserName!</h2>

<ul>
@foreach (var todo in Model.Todos)
{
    <li>@todo.Title</li>
}
</ul>

Note: Razor Pages (~PageModel) are page‑centric; MVC Views rely on a Controller. Choose Pages for simple sites, MVC for complex domain‑driven apps.

7 · Models, DTOs & Validation


public class TodoItem
{
    public int Id { get; set; }

    [Required, StringLength(80)]
    public string Title { get; set; } = "";

    public bool IsDone { get; set; }
}

8 · Built‑in Dependency Injection


// Program.cs
builder.Services.AddScoped<ITodoRepository, TodoRepository>();
builder.Services.AddSingleton<ILoggerFactory, LoggerFactory>();

Lifetime options: Transient (new each request), Scoped (per HTTP request), Singleton (app wide).

9 · Custom Middleware


// LoggingMiddleware.cs
public class LoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<LoggingMiddleware> _log;

    public LoggingMiddleware(RequestDelegate next, ILogger<LoggingMiddleware> log)
        => (_next, _log) = (next, log);

    public async Task InvokeAsync(HttpContext ctx)
    {
        _log.LogInformation("⇒ {Path}", ctx.Request.Path);
        await _next(ctx);
        _log.LogInformation("⇐ {Status}", ctx.Response.StatusCode);
    }
}

// Register
app.UseMiddleware<LoggingMiddleware>();

10 · Entity Framework Core


public class AppDb : DbContext
{
    public DbSet<TodoItem> Todos => Set<TodoItem>();

    protected override void OnConfiguring(DbContextOptionsBuilder b)
        => b.UseSqlite("Data Source=todos.db");
}
  1. dotnet ef migrations add Init
  2. dotnet ef database update

Note: EF Core 8 brings bulk updates & improved JSON column mapping.

11 · Authentication & Authorization

Identity

Scaffold with dotnet new mvc --auth Individual for cookie auth.

JWT Bearer


builder.Services
    .AddAuthentication("Bearer")
    .AddJwtBearer("Bearer", opts =>
    {
        opts.Authority = "https://demo.identityserver.io";
        opts.Audience = "api";
    });

Protect endpoints via [Authorize].

12 · Minimal APIs


// Program.cs (no controllers)
app.MapGet("/api/todo", (ITodoRepository repo) => repo.All());
app.MapPost("/api/todo", (TodoItem dto, ITodoRepository repo) =>
{
    var created = repo.Add(dto);
    return Results.Created($"/api/todo/{created.Id}", created);
});

Uses route handlers and lambda‑style dependency injection for ultralight services.

13 · Testing with xUnit & Microsoft