Phase 0: Orientierung — Was hat sich seit .NET 3.5 geändert?
Bevor es mit den praktischen Übungen losgeht, lohnt sich ein Überblick über die wichtigsten Veränderungen. Nicht alles davon muss sofort verstanden werden — dieser Abschnitt dient als Nachschlagewerk.
Die .NET-Landschaft heute
Typischer Ausgangspunkt (.NET 3.5) Heute (2025/2026)
────────────────────── ─────────────────
.NET Framework 3.5 .NET 9 / .NET 10 (Preview)
Windows-only Cross-platform (Win/Linux/macOS)
Visual Studio Pflicht VS Code + CLI reicht völlig
MSBuild + .sln komplex dotnet CLI für alles
NuGet war ganz neu NuGet ist das zentrale Ökosystem
Web: ASP.NET WebForms Web: ASP.NET Core (komplett neu)
ORM: LINQ to SQL / EF 1.0 ORM: Entity Framework Core 9
Kein DI eingebaut DI ist Kernkonzept
Kein async/await async/await ist überall
GUI: WinForms / WPF GUI: MAUI / Blazor / Avalonia
Die wichtigsten neuen C#-Features (nach Version)
| Version | Highlight | Warum es wichtig ist |
|---|---|---|
| C# 5 (2012) | async / await |
Asynchrone Programmierung ohne Callback-Hölle. Das wichtigste Feature für ASP.NET Core. |
| C# 6 (2015) | String Interpolation $"Hallo {name}", Null-Conditional ?. |
Weniger Boilerplate, sichererer Null-Zugriff |
| C# 7 (2017) | Pattern Matching, Tuples (int x, string y), out var |
Expressiverer Code, weniger temporäre Variablen |
| C# 8 (2019) | Nullable Reference Types string?, Switch Expressions, Default Interface Methods |
Null-Sicherheit zur Compile-Zeit, funktionalerer Stil |
| C# 9 (2020) | record Typen, Top-Level Statements, init-only Properties |
Immutable Datentypen, weniger Boilerplate |
| C# 10 (2021) | Global Usings, File-Scoped Namespaces | Deutlich weniger Code pro Datei |
| C# 11 (2022) | Raw String Literals """...""", Required Members |
Multiline-Strings ohne Escaping |
| C# 12 (2023) | Primary Constructors, Collection Expressions [1, 2, 3] |
Noch kompakterer Code |
| C# 13 (2024) | params Collections, Lock Typ |
Modernisierte Grundbausteine |
Schneller Vergleich: Alter vs. neuer Stil
// ══════════════════════════════════════════════════
// KLASSISCHER STIL (.NET 3.5, C# 3.0)
// ══════════════════════════════════════════════════
using System;
using System.Collections.Generic;
namespace MeineApp
{
public class Benutzer
{
private string _name;
private string _email;
public Benutzer(string name, string email)
{
_name = name;
_email = email;
}
public string Name { get { return _name; } set { _name = value; } }
public string Email { get { return _email; } set { _email = value; } }
public string GetDisplayName()
{
if (_name != null)
return _name;
else
return "Unbekannt";
}
}
class Program
{
static void Main(string[] args)
{
var benutzer = new Benutzer("Max", "[email protected]");
Console.WriteLine(string.Format("Hallo {0}!", benutzer.Name));
}
}
}
// ══════════════════════════════════════════════════
// MODERNER STIL (.NET 9, C# 13)
// ══════════════════════════════════════════════════
// File-Scoped Namespace (C# 10) — kein Extra-Indent mehr
namespace MeineApp;
// Record statt Klasse für Daten (C# 9) — immutable, mit Equals/GetHashCode
public record Benutzer(string Name, string Email)
{
// Expression-bodied Member + Null-Conditional (C# 6) + Pattern Matching (C# 8)
public string DisplayName => Name ?? "Unbekannt";
}
// Top-Level Statement (C# 9) — kein Main() mehr nötig (für Konsolen-Apps)
var benutzer = new Benutzer("Max", "[email protected]");
Console.WriteLine($"Hallo {benutzer.Name}!"); // String Interpolation (C# 6)
Phase 1: Modernes C# auffrischen (1–2 Wochen)
Ziel: Die Sprache wiedererkennen und die neuen Features verstehen.
C# muss nicht von Grund auf neu gelernt werden — die Grundlagen sind bekannt. Ein paar Features sind jedoch fundamental neu und werden täglich in ASP.NET Core benötigt.
1.1 Übungsprojekt anlegen
mkdir ~/lernpfad && cd ~/lernpfad
dotnet new console -n CSharpAuffrischung
cd CSharpAuffrischung
code . # VS Code öffnen
1.2 Die drei wichtigsten Features zum Üben
Feature 1: async/await (PFLICHT — ohne das geht nichts in ASP.NET Core)
// Jeder API-Request in ASP.NET Core ist async.
// Jede Datenbankabfrage ist async. Jeder HTTP-Call ist async.
// Synchron (klassischer Stil):
public string HoleDaten()
{
Thread.Sleep(2000); // Blockiert den Thread 2 Sekunden
return "Daten";
}
// Asynchron (moderner Stil):
public async Task<string> HoleDatenAsync()
{
await Task.Delay(2000); // Thread ist frei für andere Arbeit!
return "Daten";
}
// Aufruf:
var daten = await HoleDatenAsync();
// GOLDENE REGEL: Wenn await benutzt wird, muss die Methode async sein.
// Wenn die Methode async ist, gibt sie Task<T> statt T zurück.
// Und: async void ist VERBOTEN (außer bei Event-Handlern).
Übung 1: Schreibe ein Konsolenprogramm, das gleichzeitig 3 „API-Calls" simuliert (je 1–3 Sekunden Task.Delay) und die Ergebnisse sammelt. Nutze Task.WhenAll().
Feature 2: Nullable Reference Types
// In der .csproj steht standardmäßig bei neuen Projekten:
// <Nullable>enable</Nullable>
string name = "Max"; // Darf NICHT null sein
string? nickname = null; // Darf null sein (das ? erlaubt es)
// Compiler warnt:
Console.WriteLine(nickname.Length); // ⚠️ Warning: mögliche NullReferenceException
// Korrekte Verwendung:
if (nickname is not null)
{
Console.WriteLine(nickname.Length); // ✅ Sicher
}
// Oder kürzer mit Pattern Matching:
Console.WriteLine(nickname?.Length ?? 0); // ✅ Null-safe
Übung 2: Erstelle eine Klasse UserProfile mit nullable und non-nullable Properties und schreibe eine Methode, die null-safe darauf zugreift.
Feature 3: Records und Pattern Matching
// Record: Immutable Datentyp mit automatischem Equals, GetHashCode, ToString
public record Note(int Id, string Title, string Content, DateTime CreatedAt);
// Erstellen:
var note = new Note(1, "Einkaufsliste", "Milch, Brot", DateTime.UtcNow);
// Kopie mit Änderung (with-Expression):
var updated = note with { Title = "Neue Einkaufsliste" };
// Pattern Matching in switch:
string Beschreibung(Note n) => n switch
{
{ Title.Length: > 50 } => "Langer Titel",
{ Content.Length: 0 } => "Leere Notiz",
_ => "Normale Notiz"
};
Übung 3: Modelliere die Kernentitäten eines typischen Webprojekts als Records: UserProfile, Post, FriendRequest. Schreibe Switch Expressions, die unterschiedliche Aktionen je nach Status ausführen.
1.3 Empfohlene Lernressourcen
- Microsoft Learn: "What's new in C#" — eine Seite pro Version, ideal zum Überfliegen https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/
- Microsoft Learn: "Asynchronous programming" — das Deep-Dive-Tutorial https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/
- "C# in a Nutshell" von Joseph Albahari — das Referenzbuch für C# (deckt alle Versionen ab)
Phase 2: ASP.NET Core Grundlagen (2–3 Wochen)
Ziel: Einen REST-API-Server bauen können.
2.1 Erstes Web-API-Projekt
cd ~/lernpfad
dotnet new webapi -n MeineApi
cd MeineApi
dotnet run
# Öffne http://localhost:5000/weatherforecast im Browser
2.2 Die Kernkonzepte (in dieser Reihenfolge lernen)
Konzept 1: Dependency Injection (DI)
In .NET 3.5 wurden Objekte mit new erstellt. In ASP.NET Core werden Services im DI-Container registriert und automatisch injiziert:
// Service registrieren (in Program.cs):
builder.Services.AddScoped<INoteService, NoteService>();
// Service nutzen (im Controller — wird automatisch injiziert):
public class NotesController : ControllerBase
{
private readonly INoteService _noteService;
public NotesController(INoteService noteService) // DI!
{
_noteService = noteService;
}
}
Drei Lifetimes verstehen: AddTransient (jedes Mal neu), AddScoped (einmal pro Request), AddSingleton (einmal für die App).
Konzept 2: Middleware-Pipeline
Jeder HTTP-Request durchläuft eine Kette von Middleware-Komponenten:
Request → Logging → Auth → Routing → Controller → Response
↓
Datenbank
// Program.cs — die Reihenfolge ist wichtig!
var app = builder.Build();
app.UseHttpsRedirection();
app.UseAuthentication(); // Erst Auth prüfen
app.UseAuthorization(); // Dann Berechtigung prüfen
app.MapControllers(); // Dann Request verarbeiten
app.Run();
Konzept 3: Controller + Routing
[ApiController]
[Route("api/[controller]")] // → /api/notes
public class NotesController : ControllerBase
{
[HttpGet] // GET /api/notes
public async Task<ActionResult<List<Note>>> GetAll() { ... }
[HttpGet("{id}")] // GET /api/notes/42
public async Task<ActionResult<Note>> GetById(int id) { ... }
[HttpPost] // POST /api/notes
public async Task<ActionResult<Note>> Create(CreateNoteDto dto) { ... }
[HttpPut("{id}")] // PUT /api/notes/42
public async Task<ActionResult> Update(int id, UpdateNoteDto dto) { ... }
[HttpDelete("{id}")] // DELETE /api/notes/42
public async Task<ActionResult> Delete(int id) { ... }
}
Konzept 4: Entity Framework Core + PostgreSQL
# Pakete installieren
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL
dotnet add package Microsoft.EntityFrameworkCore.Design
// DbContext — Tor zur Datenbank
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
public DbSet<Note> Notes => Set<Note>();
public DbSet<User> Users => Set<User>();
}
// Entity
public class Note
{
public int Id { get; set; }
public required string Title { get; set; }
public string? Content { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}
// In Program.cs registrieren:
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("Default")));
// Migration erstellen + ausführen:
// dotnet ef migrations add InitialCreate
// dotnet ef database update
2.3 Übungsprojekt: Notizen-API
Checkliste für eine vollständige CRUD-API:
- [ ]
dotnet new webapiProjekt erstellt - [ ] Note-Entity + AppDbContext definiert
- [ ] PostgreSQL-Connection in
appsettings.json - [ ] Migration erstellt und ausgeführt
- [ ] NotesController mit allen 5 CRUD-Endpunkten
- [ ] Alle Endpunkte sind
async - [ ] DTOs für Create und Update (nicht die Entity direkt exponieren!)
- [ ] Swagger UI funktioniert unter
/swagger - [ ] Mit
curloder Swagger alle Endpunkte getestet
2.4 Empfohlene Lernressourcen
- Microsoft Learn: "Tutorial: Create a web API with ASP.NET Core" https://learn.microsoft.com/en-us/aspnet/core/tutorials/first-web-api
- Microsoft Learn: "Entity Framework Core — Getting Started" https://learn.microsoft.com/en-us/ef/core/get-started/overview/first-app
Phase 3: Authentifizierung & Sicherheit (1–2 Wochen)
Ziel: JWT-basierte Authentifizierung implementieren — unverzichtbar für abgesicherte APIs.
3.1 JWT-Authentifizierung einrichten
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
// Program.cs
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidAudience = builder.Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]!))
};
});
// Controller schützen:
[Authorize] // Nur mit gültigem JWT-Token erreichbar
[HttpGet("profile")]
public async Task<ActionResult<UserProfile>> GetProfile()
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
// ...
}
3.2 Übungsprojekt erweitern
- [ ] User-Entity mit Passwort-Hash (BCrypt oder ASP.NET Core Identity)
- [ ] Login-Endpunkt, der JWT-Token zurückgibt
- [ ] Register-Endpunkt
- [ ] Notizen-Endpunkte mit
[Authorize]geschützt - [ ] Jeder User sieht nur seine eigenen Notizen
- [ ] Refresh-Token-Mechanismus (optional, aber empfohlen)
Phase 4: Echtzeit-Kommunikation mit SignalR (1 Woche)
Ziel: WebSocket-basierte Echtzeit-Features verstehen — für Chat, Notifications, Live-Feed.
4.1 SignalR einrichten
// Hub definieren:
public class NotificationHub : Hub
{
public async Task SendNotification(string userId, string message)
{
await Clients.User(userId).SendAsync("ReceiveNotification", message);
}
}
// In Program.cs:
builder.Services.AddSignalR();
app.MapHub<NotificationHub>("/hubs/notifications");
4.2 Übung
- [ ] NotificationHub erstellt
- [ ] Einfachen HTML/JS-Client, der Notifications empfängt
- [ ] Wenn ein Datensatz erstellt wird, bekommen alle verbundenen Clients eine Echtzeit-Nachricht
Phase 5: Message Queue mit RabbitMQ (1 Woche)
Ziel: Asynchrone Hintergrund-Jobs verstehen — für Bildkomprimierung, E-Mail-Versand, Feed-Updates.
5.1 MassTransit + RabbitMQ
dotnet add package MassTransit.RabbitMQ
// Message definieren:
public record ImageUploadedEvent(Guid ImageId, string UserId, string FilePath);
// Consumer (Worker):
public class ImageCompressionConsumer : IConsumer<ImageUploadedEvent>
{
public async Task Consume(ConsumeContext<ImageUploadedEvent> context)
{
var msg = context.Message;
// Bild komprimieren...
Console.WriteLine($"Komprimiere Bild {msg.ImageId} für User {msg.UserId}");
}
}
// In Program.cs registrieren:
builder.Services.AddMassTransit(x =>
{
x.AddConsumer<ImageCompressionConsumer>();
x.UsingRabbitMq((context, cfg) =>
{
cfg.Host("localhost", "/", h =>
{
h.Username("guest");
h.Password("guest");
});
cfg.ConfigureEndpoints(context);
});
});
5.2 Übung
- [ ] RabbitMQ im Docker-Container gestartet
- [ ] Wenn ein Bild hochgeladen wird → Event in die Queue
- [ ] Consumer verarbeitet das Event asynchron
- [ ] Ergebnis: Upload-Endpunkt antwortet sofort, Verarbeitung läuft im Hintergrund
Phase 6: Neo4j-Integration (1 Woche)
Ziel: Die Graph-Datenbank anbinden — für Beziehungsdaten, Empfehlungen, Graphabfragen.
6.1 Neo4j .NET Driver
dotnet add package Neo4j.Driver
// Service für Neo4j:
public class GraphService
{
private readonly IDriver _driver;
public GraphService(IConfiguration config)
{
_driver = GraphDatabase.Driver(
config["Neo4j:Uri"],
AuthTokens.Basic(config["Neo4j:User"], config["Neo4j:Password"]));
}
public async Task<List<string>> GetRelatedUsers(string userId)
{
await using var session = _driver.AsyncSession();
var result = await session.RunAsync(
@"MATCH (me:User {id: $userId})-[:FRIEND]->()-[:FRIEND]->(fof:User)
WHERE NOT (me)-[:FRIEND]->(fof) AND fof.id <> $userId
RETURN DISTINCT fof.name AS name
LIMIT 10",
new { userId });
return await result.ToListAsync(r => r["name"].As<string>());
}
}
// In Program.cs:
builder.Services.AddSingleton<GraphService>();
6.2 Übung
- [ ] Neo4j im Docker-Container gestartet
- [ ] Notiz-Nodes und Tag-Relationships erstellt
- [ ] "Verwandte Notizen"-Abfragen funktionieren (z.B.
MATCH (n:Note)-[:TAGGED]->(t:Tag)<-[:TAGGED]-(related:Note) RETURN related) - [ ] "Gemeinsame Tags"-Abfrage implementiert
Phase 7: Docker + Zusammenführung (1 Woche)
Ziel: Den gesamten Stack in Docker Compose zusammenführen.
7.1 docker-compose.yml für den gesamten Stack
services:
api:
build: .
ports: ["5000:8080"]
depends_on: [postgres, neo4j, rabbitmq, redis]
environment:
- ConnectionStrings__Default=Host=postgres;Database=appdb;Username=app;Password=secret
- Neo4j__Uri=bolt://neo4j:7687
postgres:
image: postgres:16
environment:
POSTGRES_DB: appdb
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
volumes: [pgdata:/var/lib/postgresql/data]
neo4j:
image: neo4j:5
environment:
NEO4J_AUTH: neo4j/secret123
ports: ["7474:7474", "7687:7687"]
volumes: [neo4jdata:/data]
rabbitmq:
image: rabbitmq:3-management
ports: ["5672:5672", "15672:15672"]
redis:
image: redis:7-alpine
ports: ["6379:6379"]
volumes:
pgdata:
neo4jdata:
7.2 Abschluss-Checkliste
- [ ]
docker compose upstartet den gesamten Stack - [ ] API antwortet auf
http://localhost:5000/swagger - [ ] Benutzerregistrierung + Login → JWT-Token
- [ ] CRUD für Hauptentitäten funktioniert (PostgreSQL)
- [ ] Beziehungen anlegen + abfragen (Neo4j)
- [ ] Datei-Upload löst asynchrone Verarbeitung aus (RabbitMQ)
- [ ] Echtzeit-Benachrichtigungen funktionieren (SignalR)
- [ ] Redis wird für Session/Cache genutzt
Zeitplan-Übersicht
| Phase | Thema | Dauer | Priorität |
|---|---|---|---|
| 0 | Orientierung: Was hat sich geändert? | 1–2 Tage | Einmalig lesen |
| 1 | Modernes C# (async/await, Records, Nullable) | 1–2 Wochen | ⭐⭐⭐⭐⭐ |
| 2 | ASP.NET Core Grundlagen (API, EF Core, DI) | 2–3 Wochen | ⭐⭐⭐⭐⭐ |
| 3 | JWT-Authentifizierung | 1–2 Wochen | ⭐⭐⭐⭐⭐ |
| 4 | SignalR (Echtzeit) | 1 Woche | ⭐⭐⭐⭐ |
| 5 | RabbitMQ + MassTransit | 1 Woche | ⭐⭐⭐⭐ |
| 6 | Neo4j-Integration | 1 Woche | ⭐⭐⭐⭐ |
| 7 | Docker + Zusammenführung | 1 Woche | ⭐⭐⭐ |
Geschätzte Gesamtdauer: 8–12 Wochen bei abendlichem/Wochenend-Lernen.
VS Code Extensions
Empfohlene Extensions für die .NET-Entwicklung:
code --install-extension ms-dotnettools.csdevkit # C# Dev Kit
code --install-extension ms-dotnettools.csharp # C# Sprachunterstützung
code --install-extension ms-dotnettools.dotnet-interactive-vscode # .NET Interactive
code --install-extension ms-azuretools.vscode-docker # Docker
code --install-extension humao.rest-client # REST-Client (statt Postman)
Kommentare
Kommentare werden von Remark42 bereitgestellt. Beim Laden werden Daten an unseren Kommentar-Server übertragen.