Sdílet prostřednictvím


Použijte Entity Framework Core migrace v .NET Aspire

Vzhledem k tomu, že projekty .NET.NET Aspire používají kontejnerizovanou architekturu, databáze jsou dočasné a je možné je kdykoli znovu vytvořit. Entity Framework Core (EF Core) používá funkci označovanou jako migrace k vytváření a aktualizaci schémat databáze. Vzhledem k tomu, že se databáze po spuštění aplikace znovu vytvoří, musíte při každém spuštění aplikace použít migrace, abyste inicializovali schéma databáze. Toho dosáhnete tak, že ve své aplikaci zaregistrujete projekt služby migrace, který spouští migrace během spouštění.

V tomto kurzu se dozvíte, jak nakonfigurovat .NET Aspire projekty tak, aby spouštěly EF Core migrace během spouštění aplikace.

Požadavky

Pokud chcete pracovat s .NET.NET Aspire, potřebujete místně nainstalovat následující:

Další informace najdete v tématu .NET.NET Aspire nastavení a nástrojea .NET.NET Aspire SDK.

Získání úvodní aplikace

Tento kurz používá ukázkovou aplikaci, která předvádí, jak aplikovat EF Core migrace v .NET Aspire. Pomocí Visual Studio naklonujte ukázkovou aplikaci z GitHub nebo použijte následující příkaz:

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

Ukázková aplikace je ve složce SupportTicketApi. Otevřete řešení v Visual Studio nebo VS Code a chvíli si prohlédněte ukázkovou aplikaci a před pokračováním se ujistěte, že se spustí. Ukázková aplikace je základní API pro podpůrné tikety a obsahuje následující projekty:

  • SupportTicketApi.Api: Projekt ASP.NET Core, který je hostitelem rozhraní API.
  • SupportTicketApi.Data: Obsahuje kontexty a modely EF Core.
  • SupportTicketApi.AppHost: Obsahuje aplikační hostitel .NETa nastavení konfigurace.NET Aspire.
  • SupportTicketApi.ServiceDefaults: Obsahuje výchozí konfigurace služby.

Spusťte aplikaci, abyste měli jistotu, že funguje podle očekávání. Na řídicím panelu .NET.NET Aspire vyberte koncový bod https Swagger a otestujte ho pomocí GET /api/SupportTickets rozbalením operace a výběrem možnosti Vyzkoušet. Kliknutím na Spustit odešlete požadavek a zobrazte odpověď.

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

Vytvořte migrace

Začněte vytvořením některých migrací, které se mají použít.

  1. Otevřete terminál (Ctrl+` v Visual Studio).

  2. Nastavte SupportTicketApiSupportTicketApi.Api jako aktuální adresář.

  3. Pomocí nástroje příkazového řádku dotnet ef vytvořte novou migraci, která zachytí počáteční stav schématu databáze:

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

    Příkaz pro pokračování:

    • Spustí migrační nástroj příkazového řádku EF Core v adresáři SupportTicketApi.Api. dotnet ef se spouští v tomto umístění, protože služba API je místem, kde se používá kontext databáze.
    • Vytvoří migraci s názvem InitialCreate.
    • Vytvoří migraci ve složce Migrations v projektu SupportTicketApi.Data.
  4. Upravte model tak, aby zahrnoval novou vlastnost. Otevřete SupportTicketApi.DataModelsSupportTicket.cs a přidejte novou vlastnost do třídy 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. Vytvořte další novou migraci, která zachytí změny modelu:

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

Teď máte nějaké migrace, které musíte aplikovat. V dalším kroku vytvoříte službu migrace, která tyto migrace použije při spuštění aplikace.

Vytvoření služby migrace

Pokud chcete spustit migrace při spuštění, musíte vytvořit službu, která migraci použije.

  1. Přidejte do řešení nový projekt Worker Service. Pokud používáte Visual Studio, klikněte pravým tlačítkem na řešení v Průzkumníku řešení a vyberte Add>New Project. Vyberte Worker Service a pojmenujte projekt SupportTicketApi.MigrationService. Pokud používáte příkazový řádek, použijte následující příkazy z adresáře řešení:

    dotnet new worker -n SupportTicketApi.MigrationService
    dotnet sln add SupportTicketApi.MigrationService
    
  2. Pomocí SupportTicketApi.Data nebo příkazového řádku přidejte do projektu SupportTicketApi.ServiceDefaults odkazy na SupportTicketApi.MigrationService a Visual Studio projektu:

    dotnet add SupportTicketApi.MigrationService reference SupportTicketApi.Data
    dotnet add SupportTicketApi.MigrationService reference SupportTicketApi.ServiceDefaults
    
  3. Přidejte 📦Aspire. Odkaz na balíček NuGet microsoft.EntityFrameworkCore.SqlServer odkaz na projekt SupportTicketApi.MigrationService pomocí Visual Studio nebo příkazového řádku:

    dotnet add package Aspire.Microsoft.EntityFrameworkCore.SqlServer
    
  4. Přidejte zvýrazněné řádky do souboru Program.cs v projektu 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();
    

    V předchozím kódu:

  5. Obsah souboru Worker.cs v projektu SupportTicketApi.MigrationService nahraďte následujícím kódem:

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

    V předchozím kódu:

    • Metoda ExecuteAsync je volána při spuštění pracovního procesu. Následně provede následující kroky:
      1. Získá odkaz na službu TicketContext od poskytovatele služeb.
      2. Zavolá EnsureDatabaseAsync pro vytvoření databáze, pokud neexistuje.
      3. Volá RunMigrationAsync, aby se použily všechny čekající migrace.
      4. Aby databáze byla naplněna počátečními daty, je volána funkce SeedDataAsync.
      5. Zastaví pracovníka pomocí StopApplication.
    • Metody EnsureDatabaseAsync, RunMigrationAsynca SeedDataAsync všechny zapouzdřují příslušné databázové operace pomocí strategií provádění ke zpracování přechodných chyb, ke kterým může dojít při interakci s databází. Další informace o strategiích provádění najdete v tématu Odolnost připojení.

Přidání služby migrace do orchestrátoru

Služba migrace se vytvoří, ale je potřeba ji přidat do hostitele aplikace .NET.NET Aspire, aby se spustila při spuštění aplikace.

  1. V projektu SupportTicketApi.AppHost otevřete soubor Program.cs.

  2. Do metody ConfigureServices přidejte následující zvýrazněný kód:

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

    Tím se projekt SupportTicketApi.MigrationService začlení jako služba v aplikaci hostitele .NET.NET Aspire.

    Důležitý

    Pokud používáte Visual Studioa při vytváření projektu Enlist in Aspire orchestration jste vybrali možnost Worker Service, přidá se podobný kód automaticky s názvem služby supportticketapi-migrationservice. Nahraďte tento kód předchozím kódem.

Odebrání existujícího počátečního kódu

Protože služba migrace naplní databázi, měli byste z projektu rozhraní API odebrat stávající kód pro naplnění dat.

  1. V projektu SupportTicketApi.Api otevřete soubor Program.cs.

  2. Odstraňte zvýrazněné čáry.

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

Testování služby migrace

Teď, když je služba migrace nakonfigurovaná, spusťte aplikaci a otestujte migrace.

  1. Spusťte aplikaci a sledujte řídicí panel SupportTicketApi.

  2. Po krátkém čekání bude stav služby migrations zobrazen jako Dokončeno.

    snímek obrazovky řídicího panelu .NET.NET Aspire se službou migrace ve stavu Dokončeno

  3. Vyberte odkaz View ve službě migrace a prozkoumejte protokoly zobrazující provedené SQL příkazy.

Získání kódu

Na najdete dokončenou ukázkovou aplikaci .

Další ukázkový kód

Ukázková aplikace Aspire Shop používá tento přístup pro aplikaci migrací. Podívejte se na projekt AspireShop.CatalogDbManager pro implementaci služby migrace.