Eine API wird erst dauerhaft nützlich, wenn sie Daten sicher speichern und abrufen kann. Dieses Tutorial führt von einem EF-Core-Datenmodell über DbContext und Migrationen bis zu einer ASP.NET-Core-Minimal-API.
1. Architektur verstehen
Ein HTTP-Client sendet eine Anfrage an die ASP.NET-Core-API. Der Endpoint prüft Eingaben und ruft den Entity-Framework-Core-DbContext auf. EF Core übersetzt geeignete LINQ-Abfragen in SQL. SQL Server verwaltet die persistenten Datensätze. Ein DbContext ist eine kurzlebige Arbeitseinheit und darf nicht als globaler Singleton für gleichzeitige Requests verwendet werden.
2. Voraussetzungen
Du benötigst das aktuelle .NET SDK, eine lokale SQL-Server-Instanz oder einen geeigneten Testcontainer und einen Editor. Wir verwenden SQL Server als Provider. Erstelle das Projekt mit den folgenden Befehlen.
dotnet new web -n EfProductsApi
cd EfProductsApi
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet tool install --global dotnet-ef
3. Pakete installieren
EF Core benötigt einen Provider für die jeweilige Datenbank. Für Migrationen dient dotnet-ef als Werkzeug. Achte auf kompatible Major-Versionen der EF-Core-Pakete und des SDK.
4. Datenmodell erstellen
Das Product-Objekt repräsentiert eine Tabellenzeile. Der Schlüssel Id wird von der Datenbank erzeugt. Name und Price brauchen zusätzliche fachliche Validierung.
public sealed class Product
{
public int Id { get; set; }
public string Name { get; set; } = "";
public decimal Price { get; set; }
}
5. DbContext anlegen
Der DbContext definiert die Datenbanktabellen und deren Konfiguration. Mit einer Präzision für decimal verhinderst du implizite, möglicherweise ungeeignete Standardzuordnungen.
using Microsoft.EntityFrameworkCore;
public sealed class AppDbContext(DbContextOptions<AppDbContext> options)
: DbContext(options)
{
public DbSet<Product> Products => Set<Product>();
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Product>()
.Property(p => p.Price).HasPrecision(18, 2);
builder.Entity<Product>()
.Property(p => p.Name).HasMaxLength(120).IsRequired();
}
}
6. Verbindung konfigurieren
Speichere die Connection String-Konfiguration außerhalb des Codes. Für lokale Entwicklung sind User Secrets geeignet; in Azure sind Managed Identity und ein sicherer Konfigurationsspeicher vorzuziehen. Hinterlege keine produktiven Kennwörter im Repository.
dotnet user-secrets init
dotnet user-secrets set "ConnectionStrings:Sql" "Server=localhost;Database=EfProductsDemo;Trusted_Connection=True;TrustServerCertificate=True;"
7. API-Endpunkte erstellen
Nutze Dependency Injection für den DbContext. Die Beispiel-Endpoints lesen Daten, legen validierte Produkte an und geben 404 beziehungsweise 201 zurück. Für ein produktives Projekt benötigst du zusätzliche Authentifizierung und präzisere Eingabevalidierung.
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("Sql")));
var app = builder.Build();
app.MapGet("/api/products", async (AppDbContext db) =>
await db.Products.AsNoTracking().Take(100).ToListAsync());
app.MapPost("/api/products", async (Product input, AppDbContext db) =>
{
if (string.IsNullOrWhiteSpace(input.Name) || input.Price < 0)
return Results.BadRequest("Invalid product");
var product = new Product { Name = input.Name.Trim(), Price = input.Price };
db.Products.Add(product);
await db.SaveChangesAsync();
return Results.Created($"/api/products/{product.Id}", product);
});
app.Run();
8. Migrationen ausführen
Migrationen erzeugen Datenbankschemaänderungen aus dem Modell. Prüfe generierte Migrationen und die Zielumgebung vor der Ausführung. In Produktion werden Migrationen kontrolliert ausgerollt und nicht unkoordiniert bei jedem API-Start gestartet.
dotnet ef migrations add InitialCreate
dotnet ef database update
dotnet run
9. Warum async und AsNoTracking?
AsNoTracking reduziert bei lesenden Abfragen Tracking-Aufwand, wenn keine Änderungen an den geladenen Entitäten nötig sind. Async-Datenbankaufrufe geben Threads während der Wartezeit frei. Eine große Ergebnismenge sollte paginiert und durch geeignete Filter eingegrenzt werden.
10. Fehler und Sicherheit
Eine ungeprüfte Datenbankverbindung kann 500-Fehler auslösen. Stelle sinnvolle Health Checks, Logging ohne Zugangsdaten und begrenzte Retry-Strategien bereit. Verwende parametrisierte Abfragen, sichere Rollen, HTTPS und serverseitige Autorisierung. EF Core schützt nicht automatisch vor allen Geschäftslogikfehlern.
11. Übungen
Füge ein ProductDescription-Feld hinzu und erzeuge eine neue Migration. Ergänze einen Endpoint zum Abruf eines Produkts per ID. Prüfe ungültige Preise und teste, wie ein Datensatz nach einem API-Neustart erhalten bleibt.
Kommentare (0)
Noch keine Kommentare. Schreibe den ersten Beitrag zur Diskussion.