Anwenden der Entity Framework Core-Migrationen in .NET Aspire
Da .NET.NET Aspire Projekte eine containerisierte Architektur verwenden, sind Datenbanken kurzlebig und können jederzeit neu erstellt werden. Entity Framework Core (EF Core) verwendet ein Feature namens Migrationen zum Erstellen und Aktualisieren von Datenbankschemas. Da Datenbanken beim Starten der App neu erstellt werden, müssen Sie Migrationen anwenden, um das Datenbankschema bei jedem Start der App zu initialisieren. Dies erfolgt durch Registrieren eines Migrationsdienstprojekts in Ihrer App, das Migrationen während des Starts ausführt.
In diesem Lernprogramm erfahren Sie, wie Sie .NET Aspire Projekte so konfigurieren, dass EF Core Migrationen während des App-Starts ausgeführt werden.
Voraussetzungen
Um mit .NET.NET Aspirezu arbeiten, benötigen Sie folgendes lokal installiert:
- .NET 8.0 oder .NET 9,0
- Eine OCI-kompatible Containerlaufzeit, z. B.:
- Docker Desktop oder Podman. Weitere Informationen finden Sie unter container runtime.
- Eine integrierte Entwicklerumgebung (Integrated Developer Environment, IDE) oder ein Code-Editor, z. B.:
- Visual Studio 2022 Version 17.9 oder höher (optional)
-
Visual Studio Code (optional)
- C# Dev Kit: Erweiterung (Optional)
- JetBrains Rider mit .NET.NET Aspire Plug-in (Optional)
Weitere Informationen finden Sie unter .NET.NET Aspire Setup und Toolsund .NET.NET Aspire SDK.
Startanwendung herunterladen
In diesem Lernprogramm wird eine Beispiel-App verwendet, die veranschaulicht, wie EF Core Migrationen in .NET Aspireangewendet werden. Verwenden Sie Visual Studio, um die Beispiel-App von GitHub zu klonen oder den folgenden Befehl zu verwenden:
git clone https://github.com/MicrosoftDocs/aspire-docs-samples/
Die Beispiel-App befindet sich im Ordner SupportTicketApi. Öffnen Sie die Lösung in Visual Studio oder VS Code, und nehmen Sie sich einen Moment Zeit, um die Beispiel-App zu überprüfen und sicherzustellen, dass sie ausgeführt wird, bevor Sie fortfahren. Die Beispiel-App ist eine rudimentäre Support-Ticket-API, und enthält die folgenden Projekte:
- SupportTicketApi.Api: Das ASP.NET Core Projekt, das die API hostet.
- SupportTicketApi.Data: Enthält die EF Core Kontexte und Modelle.
- SupportTicketApi.AppHost: Enthält den .NET.NET Aspire App-Host und die Konfiguration.
- SupportTicketApi.ServiceDefaults: Enthält die Standarddienstkonfigurationen.
Führen Sie die App aus, um sicherzustellen, dass sie erwartungsgemäß funktioniert. Wählen Sie im .NET.NET Aspire-Dashboard den https Swagger-Endpunkt aus, und testen Sie die GET /api/SupportTickets Endpunkt der API, indem Sie den Vorgang erweitern und Ausprobierenauswählen. Wählen Sie Ausführen aus, um die Anforderung zu senden und die Antwort anzuzeigen:
[
{
"id": 1,
"title": "Initial Ticket",
"description": "Test ticket, please ignore."
}
]
Migrationen erstellen
Beginnen Sie, indem Sie einige Migrationen erstellen, die angewendet werden sollen.
Öffnen Sie ein Terminal (STRG+` in Visual Studio).
Legen Sie SupportTicketApiSupportTicketApi.Api als aktuelles Verzeichnis fest.
Verwenden Sie das
dotnet ef
Befehlszeilentool, um eine neue Migration zu erstellen, um den Anfangszustand des Datenbankschemas zu erfassen:dotnet ef migrations add InitialCreate --project ..\SupportTicketApi.Data\SupportTicketApi.Data.csproj
Der Befehl "Fortfahren":
- Führt das Befehlszeilentool für die Migration EF Core im Verzeichnis SupportTicketApi.Api aus.
dotnet ef
wird an diesem Speicherort ausgeführt, da der API-Dienst den DB-Kontext verwendet. - Erstellt eine Migration mit dem Namen InitialCreate.
- Erstellt die Migration im Migrations- Ordner des Projekts SupportTicketApi.Data.
- Führt das Befehlszeilentool für die Migration EF Core im Verzeichnis SupportTicketApi.Api aus.
Ändern Sie das Modell so, dass es eine neue Eigenschaft enthält. Öffnen Sie SupportTicketApi.DataModelsSupportTicket.cs, und fügen Sie der klasse
SupportTicket
eine neue Eigenschaft hinzu:public sealed class SupportTicket { public int Id { get; set; } [Required] public string Title { get; set; } = string.Empty; [Required] public string Description { get; set; } = string.Empty; public bool Completed { get; set; } }
Erstellen Sie eine weitere neue Migration, um die Änderungen am Modell zu erfassen:
dotnet ef migrations add AddCompleted --project ..\SupportTicketApi.Data\SupportTicketApi.Data.csproj
Jetzt müssen Sie einige Migrationen anwenden. Als Nächstes erstellen Sie einen Migrationsdienst, der diese Migrationen während des App-Starts anwendet.
Erstellen Sie den Migrationsdienst
Um die Migrationen beim Start auszuführen, müssen Sie einen Dienst erstellen, der die Migrationen anwendet.
Fügen Sie der Projektmappe ein neues Worker Service-Projekt hinzu. Wenn Sie Visual Studioverwenden, klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf die Projektmappe, und wählen Sie Add>New Projectaus. Wählen Sie Worker Service aus, und benennen Sie das Projekt SupportTicketApi.MigrationService. Wenn Sie die Befehlszeile verwenden, verwenden Sie die folgenden Befehle aus dem Lösungsverzeichnis:
dotnet new worker -n SupportTicketApi.MigrationService dotnet sln add SupportTicketApi.MigrationService
Fügen Sie zum SupportTicketApi.Data-Projekt die SupportTicketApi.ServiceDefaults- und SupportTicketApi.MigrationService-Projektverweise mithilfe von Visual Studio oder der Befehlszeile hinzu.
dotnet add SupportTicketApi.MigrationService reference SupportTicketApi.Data dotnet add SupportTicketApi.MigrationService reference SupportTicketApi.ServiceDefaults
Fügen Sie die 📦Aspire.Microsoft.EntityFrameworkCore.SqlServer NuGet-Paketreferenz zum Projekt SupportTicketApi.MigrationService mithilfe von Visual Studio oder über die Befehlszeile hinzu.
dotnet add package Aspire.Microsoft.EntityFrameworkCore.SqlServer
Fügen Sie der Program.cs-Datei im SupportTicketApi.MigrationService Projekt die hervorgehobenen Zeilen hinzu:
using SupportTicketApi.Data.Contexts; using SupportTicketApi.MigrationService; var builder = Host.CreateApplicationBuilder(args); builder.AddServiceDefaults(); builder.Services.AddHostedService<Worker>(); builder.Services.AddOpenTelemetry() .WithTracing(tracing => tracing.AddSource(Worker.ActivitySourceName)); builder.AddSqlServerDbContext<TicketContext>("sqldata"); var host = builder.Build(); host.Run();
Im vorherigen Code:
- Mit der Erweiterungsmethode
wird die Dienststandardfunktionenzu hinzugefügt. - Die
AddOpenTelemetry
Erweiterungsmethode konfiguriert OpenTelemetry Funktionalität. - Die
AddSqlServerDbContext
-Erweiterungsmethode fügt der Dienstkollektion denTicketContext
-Dienst hinzu. Dieser Dienst wird zum Ausführen von Migrationen und zum Seeden der Datenbank verwendet.
- Mit der Erweiterungsmethode
Ersetzen Sie den Inhalt der datei Worker.cs im SupportTicketApi.MigrationService Projekt durch den folgenden Code:
using System.Diagnostics; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; using OpenTelemetry.Trace; using SupportTicketApi.Data.Contexts; using SupportTicketApi.Data.Models; namespace SupportTicketApi.MigrationService; public class Worker( IServiceProvider serviceProvider, IHostApplicationLifetime hostApplicationLifetime) : BackgroundService { public const string ActivitySourceName = "Migrations"; private static readonly ActivitySource s_activitySource = new(ActivitySourceName); protected override async Task ExecuteAsync(CancellationToken cancellationToken) { using var activity = s_activitySource.StartActivity("Migrating database", ActivityKind.Client); try { using var scope = serviceProvider.CreateScope(); var dbContext = scope.ServiceProvider.GetRequiredService<TicketContext>(); await EnsureDatabaseAsync(dbContext, cancellationToken); await RunMigrationAsync(dbContext, cancellationToken); await SeedDataAsync(dbContext, cancellationToken); } catch (Exception ex) { activity?.RecordException(ex); throw; } hostApplicationLifetime.StopApplication(); } private static async Task EnsureDatabaseAsync(TicketContext dbContext, CancellationToken cancellationToken) { var dbCreator = dbContext.GetService<IRelationalDatabaseCreator>(); var strategy = dbContext.Database.CreateExecutionStrategy(); await strategy.ExecuteAsync(async () => { // Create the database if it does not exist. // Do this first so there is then a database to start a transaction against. if (!await dbCreator.ExistsAsync(cancellationToken)) { await dbCreator.CreateAsync(cancellationToken); } }); } private static async Task RunMigrationAsync(TicketContext dbContext, CancellationToken cancellationToken) { var strategy = dbContext.Database.CreateExecutionStrategy(); await strategy.ExecuteAsync(async () => { // Run migration in a transaction to avoid partial migration if it fails. await using var transaction = await dbContext.Database.BeginTransactionAsync(cancellationToken); await dbContext.Database.MigrateAsync(cancellationToken); await transaction.CommitAsync(cancellationToken); }); } private static async Task SeedDataAsync(TicketContext dbContext, CancellationToken cancellationToken) { SupportTicket firstTicket = new() { Title = "Test Ticket", Description = "Default ticket, please ignore!", Completed = true }; var strategy = dbContext.Database.CreateExecutionStrategy(); await strategy.ExecuteAsync(async () => { // Seed the database await using var transaction = await dbContext.Database.BeginTransactionAsync(cancellationToken); await dbContext.Tickets.AddAsync(firstTicket, cancellationToken); await dbContext.SaveChangesAsync(cancellationToken); await transaction.CommitAsync(cancellationToken); }); } }
Im vorherigen Code:
- Die
ExecuteAsync
-Methode wird aufgerufen, wenn der Worker startet. Sie führt wiederum die folgenden Schritte aus:- Ruft eine Referenz auf den
TicketContext
-Dienst vom Dienstanbieter ab. - Ruft
EnsureDatabaseAsync
auf, um die Datenbank zu erstellen, wenn sie nicht vorhanden ist. - Ruft
RunMigrationAsync
auf, um ausstehende Migrationen anzuwenden. - Ruft
SeedDataAsync
auf, um die Datenbank mit Anfangsdaten zu versehen. - Beendet den Worker mit
StopApplication
.
- Ruft eine Referenz auf den
- Die Methoden
EnsureDatabaseAsync
,RunMigrationAsync
undSeedDataAsync
kapseln alle ihre jeweiligen Datenbankvorgänge mithilfe von Ausführungsstrategien, um vorübergehende Fehler zu behandeln, die bei der Interaktion mit der Datenbank auftreten können. Weitere Informationen zu Durchführungsstrategien finden Sie unter Verbindungszuverlässigkeit.
- Die
Hinzufügen des Migrationsdiensts zum Orchestrator
Der Migrationsdienst wird erstellt, muss jedoch dem .NET.NET Aspire App-Host hinzugefügt werden, damit er beim Starten der App ausgeführt wird.
Öffnen Sie im SupportTicketApi.AppHost Projekt die Program.cs Datei.
Fügen Sie der
ConfigureServices
-Methode den folgenden hervorgehobenen Code hinzu:var builder = DistributedApplication.CreateBuilder(args); var sql = builder.AddSqlServer("sql") .AddDatabase("sqldata"); builder.AddProject<Projects.SupportTicketApi_Api>("api") .WithReference(sql); builder.AddProject<Projects.SupportTicketApi_MigrationService>("migrations") .WithReference(sql); builder.Build().Run();
Dadurch wird das SupportTicketApi.MigrationService-Projekt als Dienst im .NET.NET Aspire App-Host aufgelistet.
Wichtig
Wenn Sie Visual Studioverwenden und beim Erstellen des Projekts Enlist in Aspire orchestration die Option Worker Service ausgewählt haben, wird automatisch ähnlicher Code mit dem Dienstnamen
supportticketapi-migrationservice
hinzugefügt. Ersetzen Sie diesen Code durch den vorherigen Code.
Vorhandenen Seedingcode entfernen
Da der Migrationsdienst die Datenbank besät, sollten Sie den vorhandenen Datensatzungscode aus dem API-Projekt entfernen.
Öffnen Sie im SupportTicketApi.Api Projekt die Program.cs Datei.
Löschen Sie die hervorgehobenen Zeilen.
if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); using (var scope = app.Services.CreateScope()) { var context = scope.ServiceProvider.GetRequiredService<TicketContext>(); context.Database.EnsureCreated(); if(!context.Tickets.Any()) { context.Tickets.Add(new SupportTicket { Title = "Initial Ticket", Description = "Test ticket, please ignore." }); context.SaveChanges(); } } }
Test des Migrationsdiensts
Nachdem der Migrationsdienst konfiguriert ist, führen Sie die App aus, um die Migrationen zu testen.
Führen Sie die App aus, und beobachten Sie das SupportTicketApi-Dashboard.
Nach einer kurzen Wartezeit wird der
migrations
Dienststatus Abgeschlossenangezeigt.Wählen Sie den Link View des Migrationsdiensts aus, um die Protokolle zu untersuchen, die die ausgeführten SQL-Befehle enthalten.
Code abrufen
Sie finden die abgeschlossene Beispiel-App auf GitHub.
Weitere Beispielcode
Die Aspire Shop Beispiel-App verwendet diesen Ansatz, um Migrationen anzuwenden. Informationen zur Implementierung des Migrationsdiensts finden Sie im AspireShop.CatalogDbManager
Projekt.