Udostępnij za pośrednictwem


Zastosuj migracje Entity Framework Core w .NET Aspire

Ponieważ projekty .NET.NET Aspire używają architektury konteneryzowanej, bazy danych są efemeryczne i można je odtworzyć w dowolnym momencie. Entity Framework Core (EF Core) używa funkcji o nazwie migrations do tworzenia i aktualizowania schematów bazy danych. Ponieważ bazy danych są tworzone ponownie podczas uruchamiania aplikacji, należy zastosować migracje w celu zainicjowania schematu bazy danych przy każdym uruchomieniu aplikacji. Jest to realizowane przez zarejestrowanie projektu usługi migracji w aplikacji, który uruchamia migracje podczas uruchamiania.

Z tego samouczka dowiesz się, jak skonfigurować projekty .NET Aspire, aby uruchomić EF Core migracje podczas uruchamiania aplikacji.

Warunki wstępne

Aby pracować z .NET.NET Aspire, musisz mieć zainstalowane lokalnie następujące elementy:

Aby uzyskać więcej informacji, zobacz .NET.NET Aspire setup and toolingi .NET.NET Aspire SDK.

Pobierz aplikację startową

Ten samouczek wykorzystuje przykładową aplikację, która pokazuje, jak zastosować migracje EF Core w programie .NET Aspire. Użyj Visual Studio, aby sklonować aplikację przykładową z GitHub albo wprowadź następujące polecenie:

git clone https://github.com/MicrosoftDocs/aspire-docs-samples/

Przykładowa aplikacja znajduje się w folderze SupportTicketApi. Otwórz rozwiązanie w programie Visual Studio lub VS Code i pośmiń chwilę na przejrzenie przykładowej aplikacji i upewnij się, że działa przed kontynuowaniem. Przykładowa aplikacja to proste API do wsparcia zgłoszeń, i zawiera następujące projekty:

  • SupportTicketApi.Api: projekt ASP.NET Core hostujący interfejs API.
  • SupportTicketApi.AppHost: zawiera hosta i konfigurację aplikacji .NET.NET Aspire.
  • SupportTicketApi.Data: zawiera konteksty i modele EF Core.
  • SupportTicketApi.ServiceDefaults: zawiera konfiguracje usługi domyślnej.

Uruchom aplikację, aby upewnić się, że działa zgodnie z oczekiwaniami. Na panelu sterowania .NET.NET Aspire poczekaj, aż wszystkie zasoby będą działać i są w dobrym stanie. Następnie wybierz punkt końcowy https Swagger i przetestuj punkt końcowy GET /api/SupportTickets, otwierając operację i wybierając Wypróbuj. Wybierz opcję Wykonaj, aby wysłać żądanie i wyświetlić odpowiedź:

[
  {
    "id": 1,
    "title": "Initial Ticket",
    "description": "Test ticket, please ignore."
  }
]

Zamknij karty przeglądarki, na których jest wyświetlany punkt końcowy programu Swagger i pulpit nawigacyjny .NET.NET Aspire, a następnie zatrzymaj debugowanie.

Utwórz migracje

Zacznij od utworzenia niektórych migracji do zastosowania.

  1. Otwórz terminal (ctrl+` w Visual Studio).

  2. Ustaw SupportTicketApi\SupportTicketApi.Api jako bieżący katalog.

  3. Użyj narzędzia wiersza polecenia dotnet ef, aby utworzyć nową migrację w celu przechwycenia początkowego stanu schematu bazy danych:

    dotnet ef migrations add InitialCreate --project ..\SupportTicketApi.Data\SupportTicketApi.Data.csproj
    

    Następujące polecenie:

    • Uruchamia narzędzie linii poleceń migracji EF Core w katalogu SupportTicketApi.Api. dotnet ef jest uruchamiany w tej lokalizacji, ponieważ kontekst bazy danych jest używany w usłudze API.
    • Tworzy migrację o nazwie InitialCreate.
    • Tworzy migrację w folderze Migrations w projekcie SupportTicketApi.Data.
  4. Zmodyfikuj model tak, aby zawierał nową właściwość. Otwórz SupportTicketApi.Data\Models\SupportTicket.cs i dodaj nową właściwość do klasy SupportTicket:

    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; }
    }
    
  5. Utwórz kolejną nową migrację, aby przechwycić zmiany w modelu:

    dotnet ef migrations add AddCompleted --project ..\SupportTicketApi.Data\SupportTicketApi.Data.csproj
    

Teraz masz kilka migracji do zastosowania. Następnie utworzysz usługę migracji, która stosuje te migracje podczas uruchamiania aplikacji.

Tworzenie usługi migracji

Aby przy starcie uruchomić migracje, należy utworzyć usługę, która te migracje zastosuje.

  1. Dodaj nowy projekt Worker Service do rozwiązania. Jeśli używasz Visual Studio, kliknij rozwiązanie prawym przyciskiem myszy w Eksploratorze rozwiązań i wybierz pozycję Add>New Project. Wybierz Worker Service, nazwij projekt SupportTicketApi.MigrationService i ustaw jako cel .NET 8.0. W przypadku korzystania z wiersza polecenia użyj następujących poleceń z katalogu rozwiązania:

    dotnet new worker -n SupportTicketApi.MigrationService -f "net8.0"
    dotnet sln add SupportTicketApi.MigrationService
    
  2. Dodaj odwołania do projektu SupportTicketApi.Data i SupportTicketApi.ServiceDefaults do projektu SupportTicketApi.MigrationService przy użyciu Visual Studio lub wiersza polecenia:

    dotnet add SupportTicketApi.MigrationService reference SupportTicketApi.Data
    dotnet add SupportTicketApi.MigrationService reference SupportTicketApi.ServiceDefaults
    
  3. Dodaj 📦Aspire.Microsoft.EntityFrameworkCore.SqlServer jako odwołanie do pakietu NuGet do projektu SupportTicketApi.MigrationService przy użyciu Visual Studio lub wiersza polecenia.

    cd SupportTicketApi.MigrationService
    dotnet add package Aspire.Microsoft.EntityFrameworkCore.SqlServer -v "9.1.0"
    
  4. Dodaj wyróżnione wiersze do pliku Program.cs w projekcie SupportTicketApi.MigrationService:

    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();
    

    W poprzednim kodzie:

  5. Zastąp zawartość pliku Worker.cs w projekcie SupportTicketApi.MigrationService następującym kodem:

    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 RunMigrationAsync(dbContext, cancellationToken);
                await SeedDataAsync(dbContext, cancellationToken);
            }
            catch (Exception ex)
            {
                activity?.RecordException(ex);
                throw;
            }
    
            hostApplicationLifetime.StopApplication();
        }
    
        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 dbContext.Database.MigrateAsync(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);
            });
        }
    }
    

    W poprzednim kodzie:

    • Metoda ExecuteAsync jest wywoływana podczas rozpoczęcia pracy. Z kolei wykonuje następujące czynności:
      1. Uzyskuje odwołanie do usługi TicketContext od dostawcy usług.
      2. Wywołuje RunMigrationAsync, w celu zastosowania wszelkich oczekujących migracji.
      3. Wywołuje SeedDataAsync, aby wypełnić bazę danych początkowymi danymi.
      4. Zatrzymuje pracownika przy użyciu StopApplication.
    • Metody RunMigrationAsync i SeedDataAsync hermetyzują odpowiednie operacje bazy danych przy użyciu strategii wykonywania w celu obsługi błędów przejściowych, które mogą wystąpić podczas interakcji z bazą danych. Aby dowiedzieć się więcej na temat strategii wykonywania, zobacz Odporność Połączeń.

Dodaj usługę migracji do orchestratora

Usługa migracji jest tworzona, ale należy ją dodać do hosta aplikacji .NET.NET Aspire, aby była uruchamiana po uruchomieniu aplikacji.

  1. W projekcie SupportTicketApi.AppHost otwórz plik Program.cs.

  2. Dodaj następujący wyróżniony kod do metody ConfigureServices:

    var builder = DistributedApplication.CreateBuilder(args);
    
    var sql = builder.AddSqlServer("sql", port: 14329)
                     .WithEndpoint(name: "sqlEndpoint", targetPort: 14330)
                     .AddDatabase("sqldata");
    
    builder.AddProject<Projects.SupportTicketApi_Api>("api")
        .WithReference(sql)
        .WaitFor(sql);
    
    builder.AddProject<Projects.SupportTicketApi_MigrationService>("migrations")
        .WithReference(sql)
        .WaitFor(sql);
    
    builder.Build().Run();
    

    Spowoduje to rejestrację projektu SupportTicketApi.MigrationService jako usługi na hoście aplikacji .NET.NET Aspire.

  3. Jeśli kod nie może rozpoznać projektu usługi migracji, dodaj odwołanie do projektu usługi migracji w projekcie AppHost:

    dotnet add SupportTicketApi.AppHost reference SupportTicketApi.MigrationService
    

    Ważny

    Jeśli używasz Visual Studioi wybrano opcję Enlist in Aspire orchestration podczas tworzenia projektu Worker Service, podobny kod jest dodawany automatycznie z nazwą usługi supportticketapi-migrationservice. Zastąp ten kod poprzednim kodem.

Usuń istniejący kod inicjowania

Ponieważ usługa migracji inicjuje bazę danych, należy usunąć istniejący kod inicjowania danych z projektu interfejsu API.

  1. W projekcie SupportTicketApi.Api otwórz plik Program.cs.

  2. Usuń wyróżnione wiersze.

    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();
            }
        }
    }
    

Testowanie usługi migracji

Po skonfigurowaniu usługi migracji uruchom aplikację, aby przetestować migracje.

  1. Uruchom aplikację i obserwuj pulpit nawigacyjny SupportTicketApi.

  2. Po krótkim oczekiwaniu stan usługi migrations będzie widoczny jako Zakończono.

    Zrzut ekranu przedstawiający panel sterowania .NET.NET Aspire z usługą migracji w stanie Zakończono.

  3. Wybierz ikonę Console logs w usłudze migracji, aby zbadać dzienniki pokazujące wykonane polecenia SQL.

Pobieranie kodu

Ukończoną przykładową aplikację można znaleźć w witrynie GitHub.

Więcej przykładowego kodu

Przykładowa aplikacja Aspire Shop używa tego podejścia do stosowania migracji. Zobacz projekt AspireShop.CatalogDbManager dotyczący implementacji usługi migracji.