✨ Hands-On Tutorial

.NET 8 Essentials: Core Features & Getting Started

.NET 8 is the current LTS release (supported through November 2026) and a solid base for new or migrated apps. It brings full C# 12, faster startup and lower memory with Native AOT, simpler authentication, and broad performance improvements across the stack.

In this tutorial you’ll install the tooling, set up a clean project, and build a working Web API. You’ll learn practical project structure, configuration, dependency injection, and middleware, add logging and error handling, and understand when Native AOT is the right choice for production.

What's New in .NET 8

.NET 8 brings features that make your applications faster and your code cleaner. Here's what matters most for daily development work.

🚀 Native AOT Compilation: Compile your code directly to machine instructions for 50-70% faster startup and smaller memory use. Great for container apps and serverless functions.
Performance Improvements: Across-the-board speed boosts in HTTP handling, JSON serialization, and LINQ queries. Most apps see 10-20% faster response times without code changes.
🔒 Simplified Authentication: New identity APIs that reduce boilerplate. You can wire up JWT bearer tokens or cookie auth in just a few lines.
📝 C# 12 Integration: Primary constructors, collection expressions, and improved pattern matching. Your code gets shorter without losing clarity.
🌐 Cross-Platform Consistency: Linux, macOS, and Windows now behave more consistent, for file paths, networking, and threading. No more platform-specific workarounds.

Tip: If you're migrating from .NET Framework, focus on learning the new project structure first. The syntax stays mostly the same, but the tooling and file layout changed.

Installation & Setup

You'll need the .NET 8 SDK to build apps and the Runtime to run them. Most developers install just the SDK since it includes the Runtime.

Download Options

  • SDK: Install this if you're writing code. Includes compiler, templates, and tools.
  • Runtime: Install this only if you're running someone else's app on a server.

Download from dotnet.microsoft.com/download and follow the installer for your OS. After installing, open a terminal and verify:

Terminal
dotnet --version

You should see 8.0.x or higher. If not, restart your terminal and try again.

Quick Hello World

Test your setup with a simple console app. Run these commands in any directory:

Terminal
dotnet new console -n HelloDotNet
cd HelloDotNet
dotnet run

You'll see "Hello, World!" in the console. That confirms everything works.

IDE Options: Visual Studio 2022 (17.8+), VS Code with C# extension, or JetBrains Rider all support .NET 8. Pick whichever you're comfortable with.

Project Structure Basics

.NET 8 projects use a cleaner file structure than old .NET Framework apps. Here's what changed and why it matters.

Modern Project File

The .csproj file now uses SDK-style format. It's shorter and easier to read than the old XML format.

MyApp.csproj (Framework)
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Core" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Program.cs" />
  </ItemGroup>
</Project>
MyApp.csproj (.NET 8)
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>
</Project>

Notice how .NET 8 automatically includes all .cs files in your project directory. You don't list them manually anymore.

Program.cs with Top-Level Statements

New projects use top-level statements. You write code directly without wrapping it in a class and Main method.

Program.cs
// No class or Main method needed
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.Run();

This keeps simple programs short. For complex apps, you can still use the traditional class-based structure if you prefer.

Core Concepts

Three patterns you'll use in every .NET 8 app: dependency injection for managing services, configuration for app settings, and logging for tracking what happens at runtime.

Dependency Injection

.NET 8 includes a DI container out of the box. You register services at startup and the framework injects them where needed. This keeps your code testable and flexible.

Program.cs
var builder = WebApplication.CreateBuilder(args);

// Register services
builder.Services.AddSingleton<IWeatherService, WeatherService>();
builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddTransient<IEmailSender, EmailSender>();

var app = builder.Build();

// Services get injected automatically
app.MapGet("/weather", (IWeatherService weather) => 
{
    return weather.GetForecast();
});

app.Run();
  • Singleton: One instance for the app's lifetime. Use for stateless services or caches.
  • Scoped: One instance per HTTP request. Use for database contexts or unit-of-work patterns.
  • Transient: New instance every time it's requested. Use for lightweight, stateless helpers.

Configuration System

Settings come from multiple sources: JSON files, environment variables, command-line args, or user secrets for development. The framework merges them automatically with later sources overriding earlier ones.

appsettings.json
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Database=MyApp;"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information"
    }
  }
}
Program.cs
var builder = WebApplication.CreateBuilder(args);

// Read configuration
var connString = builder.Configuration
    .GetConnectionString("DefaultConnection");

// Or bind to a class
builder.Services.Configure<AppSettings>(
    builder.Configuration.GetSection("AppSettings")
);

Logging

Logging is built in and works with console, file, or third-party providers like Serilog. Inject ILogger<T> wherever you need it.

WeatherService.cs
public class WeatherService
{
    private readonly ILogger<WeatherService> _logger;
    
    public WeatherService(ILogger<WeatherService> logger)
    {
        _logger = logger;
    }
    
    public WeatherForecast GetForecast()
    {
        _logger.LogInformation("Fetching weather forecast");
        
        // Your logic here
        
        return new WeatherForecast();
    }
}

Building Your First Real API

Let's build a minimal API that handles to-do items. You'll create endpoints, add validation, and test everything in under 35 lines of code.

Program.cs
using System.ComponentModel.DataAnnotations;

var builder = WebApplication.CreateBuilder(args);

// Add validation support
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Enable Swagger in development
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

// In-memory storage (replace with database in production)
var todos = new List<Todo>();

// GET all todos
app.MapGet("/todos", () => todos);

// GET single todo
app.MapGet("/todos/{id}", (int id) => 
    todos.FirstOrDefault(t => t.Id == id) is Todo todo
        ? Results.Ok(todo)
        : Results.NotFound());

// POST new todo
app.MapPost("/todos", ([FromBody] Todo todo) => 
{
    todos.Add(todo);
    return Results.Created($"/todos/{todo.Id}", todo);
});

app.Run();

// Data model
record Todo(
    int Id, 
    [Required] string Title, 
    bool IsComplete = false
);

Test Your API

Run dotnet run and navigate to https://localhost:5001/swagger. You'll see an interactive UI for testing all your endpoints. Try creating a few to-do items and retrieving them.

Next Steps: Add a database with EF Core, implement authentication, and deploy to Azure App Service. We cover all of this in our ASP.NET Core tutorial.

Native AOT Deep Dive

Native Ahead-of-Time compilation turns your .NET code into native machine code before you ship it. This gives you faster startup and lower memory use, but with trade-offs.

When to Use Native AOT

  • CLI tools: Apps that start frequently benefit from instant launch.
  • Serverless functions: Cold start time matters in AWS Lambda or Azure Functions.
  • Microservices in containers: Smaller images and faster scale-up in Kubernetes.

When to Avoid It

  • Apps using reflection: AOT can't handle dynamic type discovery well.
  • Libraries with runtime code generation: Entity Framework, some serializers, and MEF struggle.
  • Traditional web apps: Most ASP.NET Core apps don't need it. The JIT compiler is fast enough.

Enable Native AOT

Add PublishAot to your project file and publish:

MyApp.csproj
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <PublishAot>true</PublishAot>
  </PropertyGroup>
</Project>
Terminal
dotnet publish -c Release

The compiler will warn you about incompatible code. Fix those issues before deploying.

Warning: Native AOT produces larger executables (10-50 MB) and slower first-time compilation. Only use it when startup time is your bottleneck.

Performance Tips

Small changes that make your .NET 8 apps faster without rewriting everything.

  • Use async/await correctly: Don't block async code with .Result or .Wait(). It causes thread pool starvation.
  • Span<T> for array operations: Avoid allocations when slicing or processing arrays. Use Span<T> or Memory<T>.
  • String interpolation: Use $"{value}" instead of string.Format(). The compiler optimizes it better.
  • ValueTask for hot paths: Replace Task<T> with ValueTask<T> in high-frequency methods to reduce allocations.
  • Profile before optimizing: Use dotnet-counters, PerfView, or Visual Studio profiler to find actual bottlenecks.

For deeper optimization, read Microsoft's performance profiling guide.

Common Migration Scenarios

Moving from .NET 6, 7, or Framework to .NET 8 is usually straightforward. Here's what to watch for.

Upgrading from .NET 6 or 7

Most code runs without changes. Update your TargetFramework and test:

MyApp.csproj (Before)
<PropertyGroup>
  <TargetFramework>net7.0</TargetFramework>
</PropertyGroup>
MyApp.csproj (After)
<PropertyGroup>
  <TargetFramework>net8.0</TargetFramework>
</PropertyGroup>

Run dotnet restore to update packages. Check Microsoft's breaking changes list for edge cases.

Migration Checklist

  • Update TargetFramework to net8.0
  • Update NuGet packages to compatible versions
  • Run all unit tests and integration tests
  • Check for compiler warnings (new analyzers catch mistakes)
  • Review authentication code (identity APIs changed)
  • Test on target OS (Linux, macOS, Windows)
  • Profile performance (look for regressions)

Framework Migration: Moving from .NET Framework 4.x to .NET 8 is a bigger lift. Check our dedicated migration guide for step-by-step instructions.

Next Steps & Resources

You've got the .NET 8 basics. Here's where to go next based on what you want to build.

  • Web APIs: Learn ASP.NET Core for building production REST APIs with authentication, validation, and database access.
  • Interactive UIs: Try Blazor to build web UIs with C# instead of JavaScript.
  • Data Access: Master Entity Framework Core for working with SQL Server, PostgreSQL, or SQLite.
  • Cloud Deployment: Deploy to Azure with App Service, Functions, or Container Apps.
  • Advanced C#: Deep dive into C# 12 features like primary constructors and collection expressions.

Official Resources

Frequently Asked Questions

What's the difference between .NET 8 and .NET Framework?

.NET 8 is cross-platform (Windows, Linux, macOS) and open-source, while .NET Framework runs on Windows only. .NET 8 is faster, supports modern development patterns, and receives active updates. If you're starting fresh, use .NET 8. If you have legacy Windows-only code, you may need Framework during migration.

Do I need to learn C# 12 to use .NET 8?

No. .NET 8 works with earlier C# versions. C# 12 adds convenience features like primary constructors and collection expressions that make code cleaner. You can stay on C# 10 or 11 and adopt C# 12 at your pace.

Should I use Native AOT for my application?

Use Native AOT for console tools, serverless functions, or small containerized services where startup time and memory matter most. Avoid it if you rely on heavy reflection, dynamic assembly loading, or runtime code generation. Many web apps run fine without AOT.

Can I upgrade from .NET 6 or .NET 7 to .NET 8?

Yes. Update TargetFramework to net8.0, update NuGet packages, and test. Most .NET 6 and 7 code runs on .NET 8 without changes. Review Microsoft's migration notes for any breaking changes in your dependencies.

What's the Long-Term Support timeline for .NET 8?

.NET 8 is an LTS release with support through November 2026. You'll receive security patches and critical fixes during this period. Plan your next major upgrade before support ends.

Back to Tutorials