From 90118beb428087fe0ae7909f9112239b58a4afbd Mon Sep 17 00:00:00 2001 From: "aspire-repo-bot[bot]" <268009190+aspire-repo-bot[bot]@users.noreply.github.com> Date: Wed, 29 Apr 2026 04:39:12 +0000 Subject: [PATCH 1/2] docs: document AddEFMigrations and PublishAsMigrationBundle with publishContainer Document the AddEFMigrations AppHost API and the new publishContainer: true option for PublishAsMigrationBundle introduced in microsoft/aspire#16289. - Add 'Automated EF migrations with AddEFMigrations' section covering: - Package installation - AppHost configuration via AddEFMigrations - RunDatabaseUpdateOnStart for local run mode - PublishAsMigrationScript and PublishAsMigrationBundle for publish pipeline - New publishContainer: true option to wrap bundle in a container image - Per-environment configuration (ACA Job, Docker Compose, Kubernetes) - Artifact-only (no container) usage pattern Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../databases/efcore/migrations.mdx | 147 +++++++++++++++++- 1 file changed, 146 insertions(+), 1 deletion(-) diff --git a/src/frontend/src/content/docs/integrations/databases/efcore/migrations.mdx b/src/frontend/src/content/docs/integrations/databases/efcore/migrations.mdx index 49626b0b9..70bec587b 100644 --- a/src/frontend/src/content/docs/integrations/databases/efcore/migrations.mdx +++ b/src/frontend/src/content/docs/integrations/databases/efcore/migrations.mdx @@ -1,6 +1,6 @@ --- title: Apply EF Core migrations in Aspire -description: Learn about how to to apply Entity Framework Core migrations in Aspire. +description: Learn about how to apply Entity Framework Core migrations in Aspire, including running migrations at startup and publishing migration bundles as container images for deployment. --- import { Code, Steps, Tabs, TabItem } from '@astrojs/starlight/components'; @@ -526,3 +526,148 @@ You can find the [completed sample app on GitHub](https://github.com/MicrosoftDo ## More sample code The [Aspire Shop](https://learn.microsoft.com/samples/microsoft/aspire-samples/aspire-shop/) sample app uses this approach to apply migrations. See the `AspireShop.CatalogDbManager` project for the migration service implementation. + +## Automated EF migrations with `AddEFMigrations` + +In addition to creating a dedicated worker service as described above, Aspire provides a first-class AppHost API for managing EF Core migrations: `AddEFMigrations`. This API integrates migration execution directly into the AppHost orchestration and the Aspire publishing pipeline, without requiring a separate migration service project. + +### Add the package + +Add the [📦 Aspire.Hosting.EntityFrameworkCore](https://www.nuget.org/packages/Aspire.Hosting.EntityFrameworkCore) NuGet package to your AppHost project: + +```bash title=".NET CLI" +dotnet add package Aspire.Hosting.EntityFrameworkCore +``` + +### Configure migrations in the AppHost + +Call `AddEFMigrations` on any project resource that hosts an EF Core `DbContext`: + +```csharp title="C# — AppHost.cs" +var db = builder.AddPostgres("pg").AddDatabase("appdb"); + +var api = builder.AddProject("api") + .WithReference(db); + +var apiMigrations = api.AddEFMigrations("api-migrations", "MyApp.Data.MyDbContext"); +``` + +The first argument is a resource name for the migration resource; the second is the fully qualified name of the `DbContext` type. + +### Run migrations on startup + +To apply migrations automatically when running locally with `aspire run`, chain `RunDatabaseUpdateOnStart()`: + +```csharp title="C# — AppHost.cs" +var apiMigrations = api.AddEFMigrations("api-migrations", "MyApp.Data.MyDbContext") + .RunDatabaseUpdateOnStart(); + +// Other resources can wait for migrations to complete before starting +builder.AddProject("worker") + .WaitForCompletion(apiMigrations); +``` + +:::note +`RunDatabaseUpdateOnStart` only affects local run mode. The migration resource is not deployed with the app, so this call has no effect during `aspire publish` or deployment. +::: + +When `RunDatabaseUpdateOnStart()` is called, a health check is registered for the migration resource. The resource transitions through the following states: + +| State | Description | +|---|---| +| **Pending** | Waiting for database resource to become healthy | +| **Running** | Executing the `dotnet ef database update` command | +| **Finished** | Migrations applied successfully | +| **FailedToStart** | An error occurred during migration | + +### Publish migration artifacts + +To generate migration artifacts during `aspire publish`, use `PublishAsMigrationScript` or `PublishAsMigrationBundle`: + +```csharp title="C# — AppHost.cs" +// Generate a SQL migration script during publish +var apiMigrations = api.AddEFMigrations("api-migrations", "MyApp.Data.MyDbContext") + .PublishAsMigrationScript(); + +// Generate a self-contained migration bundle executable during publish +var apiMigrations = api.AddEFMigrations("api-migrations", "MyApp.Data.MyDbContext") + .PublishAsMigrationBundle(); +``` + +Both methods add pipeline steps that run during `aspire publish` and write their artifacts into the publish output directory under `efmigrations/`. + +### Publish the migration bundle as a container image + +Pass `publishContainer: true` to `PublishAsMigrationBundle` to wrap the generated bundle in a container image. The migration resource becomes a compute resource that each compute environment (Docker Compose, Azure Container Apps, Kubernetes, and so on) deploys like any other container: + +```csharp title="C# — AppHost.cs" +var db = builder.AddPostgres("pg").AddDatabase("appdb"); +var api = builder.AddProject("api").WithReference(db); + +var apiMigrations = api.AddEFMigrations("api-migrations", "MyApp.Data.MyDbContext") + .WaitFor(db) + .PublishAsMigrationBundle(publishContainer: true); +``` + +:::note +`.WaitFor(database)` is required when using `publishContainer: true`. The connection string for the database is wired up automatically via that reference, exactly as it is for any other compute resource. +::: + +Key behaviors: + +- The generated `Dockerfile` uses `mcr.microsoft.com/dotnet/runtime:10.0` (or `mcr.microsoft.com/dotnet/runtime-deps:10.0` when `selfContained: true`). +- The `targetRuntime` argument defaults to `linux-x64` when `publishContainer: true`; set it explicitly for other architectures (for example, `linux-arm64`). +- Local run mode (`aspire run`) is unaffected — no container image is built, and the migration resource still appears in the dashboard with its tool commands. + +#### Preventing container restarts per environment + +A migration bundle is idempotent (running it twice is safe), but different compute environments need explicit configuration to avoid restarting the container after it exits. + + + + +Publish the migration resource as an [Azure Container App Job](https://learn.microsoft.com/azure/container-apps/jobs) so it runs once and stops: + +```csharp title="C# — AppHost.cs" +var apiMigrations = api.AddEFMigrations("api-migrations", "MyApp.Data.MyDbContext") + .WaitFor(db) + .PublishAsMigrationBundle(publishContainer: true) + .PublishAsAzureContainerAppJob(); // requires Aspire.Hosting.Azure.AppContainers +``` + + + + +Tell Compose not to restart the container after it exits: + +```csharp title="C# — AppHost.cs" +var apiMigrations = api.AddEFMigrations("api-migrations", "MyApp.Data.MyDbContext") + .WaitFor(db) + .PublishAsMigrationBundle(publishContainer: true) + .PublishAsDockerComposeService((_, service) => service.Restart = "no"); // requires Aspire.Hosting.Docker +``` + + + + +Customize the generated manifest to use a `Job` workload or set `restartPolicy: OnFailure` using your Kubernetes publisher customization hook. + + + + +#### Generate the bundle artifact only + +If you want `aspire publish` to produce the bundle executable without wrapping it in a container image (for example, to run it from a deployment pipeline or CI job), omit `publishContainer: true`: + +```csharp title="C# — AppHost.cs" +var apiMigrations = api.AddEFMigrations("api-migrations", "MyApp.Data.MyDbContext") + .PublishAsMigrationBundle(); // artifact only, no Dockerfile +``` + +This writes the platform-native executable to `/efmigrations/[.exe]` without adding any compute resource to the deployment manifest. You can then execute the bundle as appropriate for your deployment flow: + +```bash title="Bash" +.//efmigrations/api-migrations --connection "$CONNECTION_STRING" +``` + +You can also combine both `PublishAsMigrationScript` and `PublishAsMigrationBundle` on the same resource to produce both artifacts. From c7340b919b500a42a230c439f905bfafffcdb869 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 29 Apr 2026 21:36:54 +0000 Subject: [PATCH 2/2] Address PR review feedback on migrations.mdx Agent-Logs-Url: https://github.com/microsoft/aspire.dev/sessions/e98f02d1-22c1-45c0-bac2-d26789008422 Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com> --- .../databases/efcore/migrations.mdx | 57 ++++++++----------- 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/src/frontend/src/content/docs/integrations/databases/efcore/migrations.mdx b/src/frontend/src/content/docs/integrations/databases/efcore/migrations.mdx index 70bec587b..f311aa772 100644 --- a/src/frontend/src/content/docs/integrations/databases/efcore/migrations.mdx +++ b/src/frontend/src/content/docs/integrations/databases/efcore/migrations.mdx @@ -549,22 +549,28 @@ var db = builder.AddPostgres("pg").AddDatabase("appdb"); var api = builder.AddProject("api") .WithReference(db); -var apiMigrations = api.AddEFMigrations("api-migrations", "MyApp.Data.MyDbContext"); +var apiMigrations = api.AddEFMigrations("api-migrations"); ``` -The first argument is a resource name for the migration resource; the second is the fully qualified name of the `DbContext` type. +The first argument is a resource name for the migration resource. The optional second argument is the fully qualified name of the `DbContext` type; it's only required when the target assembly contains more than one `DbContext`. + +If your migrations live in a separate project (for example, `Projects.Data`), reference that project from your AppHost and point to it using `.WithMigrationsProject()`: + +```csharp title="C# — AppHost.cs" +var apiMigrations = api.AddEFMigrations("api-migrations") + .WithMigrationsProject(); +``` ### Run migrations on startup To apply migrations automatically when running locally with `aspire run`, chain `RunDatabaseUpdateOnStart()`: ```csharp title="C# — AppHost.cs" -var apiMigrations = api.AddEFMigrations("api-migrations", "MyApp.Data.MyDbContext") +var apiMigrations = api.AddEFMigrations("api-migrations") .RunDatabaseUpdateOnStart(); -// Other resources can wait for migrations to complete before starting -builder.AddProject("worker") - .WaitForCompletion(apiMigrations); +// The api project waits for migrations to complete before starting +api.WaitForCompletion(apiMigrations); ``` :::note @@ -585,12 +591,9 @@ When `RunDatabaseUpdateOnStart()` is called, a health check is registered for th To generate migration artifacts during `aspire publish`, use `PublishAsMigrationScript` or `PublishAsMigrationBundle`: ```csharp title="C# — AppHost.cs" -// Generate a SQL migration script during publish -var apiMigrations = api.AddEFMigrations("api-migrations", "MyApp.Data.MyDbContext") - .PublishAsMigrationScript(); - -// Generate a self-contained migration bundle executable during publish -var apiMigrations = api.AddEFMigrations("api-migrations", "MyApp.Data.MyDbContext") +// Generate both a SQL script and a self-contained bundle during publish +var apiMigrations = api.AddEFMigrations("api-migrations") + .PublishAsMigrationScript() .PublishAsMigrationBundle(); ``` @@ -604,18 +607,19 @@ Pass `publishContainer: true` to `PublishAsMigrationBundle` to wrap the generate var db = builder.AddPostgres("pg").AddDatabase("appdb"); var api = builder.AddProject("api").WithReference(db); -var apiMigrations = api.AddEFMigrations("api-migrations", "MyApp.Data.MyDbContext") +var apiMigrations = api.AddEFMigrations("api-migrations") + .WithReference(db) .WaitFor(db) .PublishAsMigrationBundle(publishContainer: true); ``` :::note -`.WaitFor(database)` is required when using `publishContainer: true`. The connection string for the database is wired up automatically via that reference, exactly as it is for any other compute resource. +Both `.WithReference(db)` and `.WaitFor(db)` are required when using `publishContainer: true`. `.WithReference(db)` wires up the connection string so the migration container can reach the database, and `.WaitFor(db)` ensures the database is healthy before the container starts. ::: Key behaviors: -- The generated `Dockerfile` uses `mcr.microsoft.com/dotnet/runtime:10.0` (or `mcr.microsoft.com/dotnet/runtime-deps:10.0` when `selfContained: true`). +- Use the `baseImage` argument to specify a custom base image for the generated container. - The `targetRuntime` argument defaults to `linux-x64` when `publishContainer: true`; set it explicitly for other architectures (for example, `linux-arm64`). - Local run mode (`aspire run`) is unaffected — no container image is built, and the migration resource still appears in the dashboard with its tool commands. @@ -629,7 +633,8 @@ A migration bundle is idempotent (running it twice is safe), but different compu Publish the migration resource as an [Azure Container App Job](https://learn.microsoft.com/azure/container-apps/jobs) so it runs once and stops: ```csharp title="C# — AppHost.cs" -var apiMigrations = api.AddEFMigrations("api-migrations", "MyApp.Data.MyDbContext") +var apiMigrations = api.AddEFMigrations("api-migrations") + .WithReference(db) .WaitFor(db) .PublishAsMigrationBundle(publishContainer: true) .PublishAsAzureContainerAppJob(); // requires Aspire.Hosting.Azure.AppContainers @@ -641,7 +646,8 @@ var apiMigrations = api.AddEFMigrations("api-migrations", "MyApp.Data.MyDbContex Tell Compose not to restart the container after it exits: ```csharp title="C# — AppHost.cs" -var apiMigrations = api.AddEFMigrations("api-migrations", "MyApp.Data.MyDbContext") +var apiMigrations = api.AddEFMigrations("api-migrations") + .WithReference(db) .WaitFor(db) .PublishAsMigrationBundle(publishContainer: true) .PublishAsDockerComposeService((_, service) => service.Restart = "no"); // requires Aspire.Hosting.Docker @@ -654,20 +660,3 @@ Customize the generated manifest to use a `Job` workload or set `restartPolicy: - -#### Generate the bundle artifact only - -If you want `aspire publish` to produce the bundle executable without wrapping it in a container image (for example, to run it from a deployment pipeline or CI job), omit `publishContainer: true`: - -```csharp title="C# — AppHost.cs" -var apiMigrations = api.AddEFMigrations("api-migrations", "MyApp.Data.MyDbContext") - .PublishAsMigrationBundle(); // artifact only, no Dockerfile -``` - -This writes the platform-native executable to `/efmigrations/[.exe]` without adding any compute resource to the deployment manifest. You can then execute the bundle as appropriate for your deployment flow: - -```bash title="Bash" -.//efmigrations/api-migrations --connection "$CONNECTION_STRING" -``` - -You can also combine both `PublishAsMigrationScript` and `PublishAsMigrationBundle` on the same resource to produce both artifacts.