Примените миграции Entity Framework Core в .NET Aspire
Так как .NET.NET Aspire проекты используют контейнерную архитектуру, базы данных являются временными и могут быть воссозданы в любое время. Entity Framework Core (EF Core) использует возможность миграций для создания и обновления схем базы данных. Так как базы данных создаются при запуске приложения, необходимо применить миграции для инициализации схемы базы данных при каждом запуске приложения. Это достигается путем регистрации проекта службы миграции в приложении, которое выполняет миграцию во время запуска.
В этом руководстве вы узнаете, как настроить проекты .NET Aspire для выполнения миграций EF Core при запуске приложения.
Необходимые условия
Для работы с .NET.NET Aspire, вам потребуется следующее иметь установлено локально:
- .NET 8.0 или .NET 9.0
- Контейнерное время выполнения, совместимое с OCI, например:
- Docker настольный компьютер или Podman. Для получения дополнительной информации см. раздел Среда выполнения контейнеров.
- Интегрированная среда разработки (IDE) или редактор кода, например:
- Visual Studio 2022 версии 17.9 или более поздней (необязательно)
-
Visual Studio Code (необязательно)
- C# Dev Kit: расширение (необязательно)
- JetBrains Rider с плагином .NET.NET Aspire (необязательно)
Дополнительные сведения см. в разделе .NET.NET Aspire программы установки и инструментови .NET.NET Aspire пакета SDK.
Получение начального приложения
В этом руководстве используется пример приложения, демонстрирующего применение миграций EF Core в .NET Aspire. Используйте Visual Studio, чтобы клонировать пример приложения из GitHub или используйте следующую команду:
git clone https://github.com/MicrosoftDocs/aspire-docs-samples/
Пример приложения находится в папке SupportTicketApi. Откройте решение в Visual Studio или VS Code и просмотрите пример приложения и убедитесь, что оно выполняется перед продолжением. Образец приложения — это API для обработки заявок в службу поддержки, и он содержит следующие проекты:
- SupportTicketApi.Api: проект ASP.NET Core, на котором размещен API.
- SupportTicketApi.Data: содержит контексты и модели EF Core.
- SupportTicketApi.AppHost: содержит узел и конфигурацию приложения .NET.NET Aspire.
- SupportTicketApi.ServiceDefaults: содержит конфигурации служб по умолчанию.
Запустите приложение, чтобы убедиться, что оно работает должным образом. На панели мониторинга .NET.NET Aspire выберите конечную точку https Swagger и проверьте конечную точку API GET /api/SupportTickets, развернув операцию и выбрав Попробовать. Выберите Выполнить, чтобы отправить запрос и просмотреть ответ.
[
{
"id": 1,
"title": "Initial Ticket",
"description": "Test ticket, please ignore."
}
]
Создание миграций
Начните с создания некоторых миграций для применения.
Откройте терминал (CTRL+` в Visual Studio).
Задайте SupportTicketApiSupportTicketApi.Api в качестве текущего каталога.
Используйте средство командной строки
dotnet ef
для создания новой миграции для записи начального состояния схемы базы данных:dotnet ef migrations add InitialCreate --project ..\SupportTicketApi.Data\SupportTicketApi.Data.csproj
Следующая команда:
- Запускает инструмент командной строки EF Core миграции в каталоге supportTicketApi.Api.
dotnet ef
выполняется в этом расположении, так как служба API находится в том месте, где используется контекст базы данных. - Создает миграцию с именем InitialCreate.
- Создает миграцию в папке Migrations в проекте SupportTicketApi.Data.
- Запускает инструмент командной строки EF Core миграции в каталоге supportTicketApi.Api.
Измените модель таким образом, чтобы она включает новое свойство. Откройте SupportTicketApi.DataModelsSupportTicket.cs и добавьте новое свойство в класс
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; } }
Создайте еще одну миграцию для записи изменений в модели:
dotnet ef migrations add AddCompleted --project ..\SupportTicketApi.Data\SupportTicketApi.Data.csproj
Теперь нужно применить некоторые миграции. Затем вы создадите службу миграции, которая применяет эти миграции во время запуска приложения.
Создание службы миграции
Чтобы запустить миграции при запуске, необходимо создать службу, которая выполняет миграции.
Добавьте в решение новый проект Worker Service. При использовании Visual Studioщелкните правой кнопкой мыши решение в обозревателе решений и выберите Add>New Project. Выберите Worker Service и назовите проект SupportTicketApi.MigrationService. При использовании командной строки используйте следующие команды из каталога решения:
dotnet new worker -n SupportTicketApi.MigrationService dotnet sln add SupportTicketApi.MigrationService
Добавьте ссылки на SupportTicketApi.Data и SupportTicketApi.ServiceDefaults проекта в проект SupportTicketApi.MigrationService с помощью Visual Studio или командной строки:
dotnet add SupportTicketApi.MigrationService reference SupportTicketApi.Data dotnet add SupportTicketApi.MigrationService reference SupportTicketApi.ServiceDefaults
Добавьте 📦Aspire. Ссылка на пакет NuGet Microsoft.EntityFrameworkCore.SqlServer на проект SupportTicketApi.MigrationService с помощью Visual Studio или командной строки:
dotnet add package Aspire.Microsoft.EntityFrameworkCore.SqlServer
Добавьте выделенные строки в файл Program.cs в проекте 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();
В приведенном выше коде:
- Метод расширения
AddServiceDefaults
добавляет функциональные возможности службы по умолчанию. - Метод расширения
AddOpenTelemetry
настраивает OpenTelemetry функциональность. - Метод расширения
AddSqlServerDbContext
добавляет службуTicketContext
в коллекцию служб. Эта служба используется для выполнения миграций и заполнения базы данных.
- Метод расширения
Замените содержимое файла Worker.cs в проекте SupportTicketApi.MigrationService следующим кодом:
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); }); } }
В приведенном выше коде:
- Метод
ExecuteAsync
вызывается, когда запускается рабочий процесс. В свою очередь он выполняет следующие действия:- Получает ссылку на услугу
TicketContext
от поставщика услуг. - Вызывает
EnsureDatabaseAsync
для создания базы данных, если она не существует. - Функция вызова
RunMigrationAsync
для применения любых ожидающих миграций. - Вызывает
SeedDataAsync
для заполнения базы данных начальными данными. - Останавливает рабочий процесс с
StopApplication
.
- Получает ссылку на услугу
- Методы
EnsureDatabaseAsync
,RunMigrationAsync
иSeedDataAsync
все охватывают операции базы данных, используя стратегии выполнения для обработки переходящих ошибок, которые могут возникать при взаимодействии с базой данных. Дополнительные сведения о стратегиях выполнения см. в "Резилиентность подключений".
- Метод
Добавление службы миграции в оркестратор
Служба миграции создана, но ее необходимо добавить в хост приложения .NET.NET Aspire, чтобы она запускалась при старте приложения.
В проекте SupportTicketApi.AppHost откройте файл Program.cs.
Добавьте следующий выделенный код в метод
ConfigureServices
: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();
Это регистрирует проект SupportTicketApi.MigrationService в качестве службы в узле размещения приложения .NET.NET Aspire.
Важный
Если вы используете Visual Studioи выбрали параметр Enlist in Aspire orchestration при создании проекта Worker Service, аналогичный код добавляется автоматически с именем службы
supportticketapi-migrationservice
. Замените этот код предыдущим кодом.
Удалите существующий код сеядки
Так как служба миграции заполняет базу данных, необходимо удалить существующий код заполнения данных из проекта API.
В проекте SupportTicketApi.Api откройте файл Program.cs.
Удалите выделенные строки.
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(); } } }
Тестирование службы миграции
Теперь, когда служба миграции настроена, запустите приложение для тестирования миграций.
Запустите приложение и просмотрите панель мониторинга SupportTicketApi.
После короткого ожидания состояние службы
migrations
отобразится готово.Выберите ссылку View в службе миграции, чтобы исследовать журналы, показывающие команды SQL, которые были выполнены.
Получение кода
Пример приложения можно найти на GitHub.
Дополнительные примеры кода
Пример приложения Aspire Shop использует этот подход для применения миграций. См. проект AspireShop.CatalogDbManager
для реализации службы миграции.
.NET Aspire