From 53210aadaa3a0957a44a29eef84ef825064225b0 Mon Sep 17 00:00:00 2001 From: Cam Soper Date: Tue, 9 Apr 2024 15:12:06 -0500 Subject: [PATCH 01/17] wip --- docs/database/ef-core-migrations.md | 85 +++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 docs/database/ef-core-migrations.md diff --git a/docs/database/ef-core-migrations.md b/docs/database/ef-core-migrations.md new file mode 100644 index 0000000000..71c94a7b38 --- /dev/null +++ b/docs/database/ef-core-migrations.md @@ -0,0 +1,85 @@ +--- +title: Apply EF Core migrations in .NET Aspire +description: Learn about how to to apply Entity Framework Core migrations in .NET Aspire +ms.date: 3/20/2024 +ms.topic: how-to +--- + +# Apply EF Core migrations in .NET Aspire + +In this article, you learn how to configure .NET Aspire apps to seed data in a database during app startup. .NET Aspire enables you to seed data using database scripts or Entity Framework Core for common platforms such as SQL Server, PostgreSQL and MySQL. + +## When to run migrations + +You should run migrations whenever you make changes to your database schema. This includes creating new tables, modifying existing tables, or adding/removing columns. By running migrations, you ensure that your database schema is in sync with your application code. + +# Database migrations with Entity Framework Core sample app + +This sample demonstrates how to use Entity Framework Core's [migrations feature](https://learn.microsoft.com/ef/core/managing-schemas/migrations) with Aspire. + +The sample has three important projects: + +- `DatabaseMigrations.ApiService` - A web app that calls the database. +- `DatabaseMigrations.MigrationService` - A background worker app that applies migrations when it starts up. +- `DatabaseMigrations.ApiModel` - The EF Core context, entities, and migrations. This project is used by the API and migration service. + +`DatabaseMigrations.ApiService` and `DatabaseMigrations.MigrationService` reference a SQL Server resource. During local development the SQL Server resource is launched in a container. + +## Demonstrates + +- How to create migrations in an Aspire solution +- How to apply migrations in an Aspire solution + +## Sample prerequisites + +This sample is written in C# and targets .NET 8.0. It requires the [.NET 8.0 SDK](https://dotnet.microsoft.com/download/dotnet/8.0) or later. + +## Create migration + +The `DatabaseMigrations.ApiModel` project contains the EF Core model and migrations. The [`dotnet ef` command-line tool](https://learn.microsoft.com/ef/core/managing-schemas/migrations/#install-the-tools) can be used to create new migrations: + +1. Update the `Entry` entity in database context in `MyDb1Context.cs`. Add a `Name` property: + + ```cs + public class Entry + { + public Guid Id { get; set; } = Guid.NewGuid(); + public string? Name { get; set; } + } + ``` + +2. Open a command prompt in the `DatabaseMigrations.ApiService` directory and run the EF Core migration tool to create a migration named **MyNewMigration**. + + ```bash + dotnet ef migrations add MyNewMigration --project ..\DatabaseMigrations.ApiModel\DatabaseMigrations.ApiModel.csproj + ``` + + The proceeding command: + + * Runs EF Core migration command-line tool in the `DatabaseMigrations.ApiService` directory. `dotnet ef` is run in this location because the API service is where the DB context is used. + * Creates a migration named `MyNewMigration`. + * Creates the migration in the `DatabaseMigrations.ApiModel` project. + +4. View the new migration files in the `DatabaseMigrations.ApiModel` project. + +## Run the app + +If using Visual Studio, open the solution file `DatabaseMigrations.sln` and launch/debug the `DatabaseMigrations.AppHost` project. + +If using the .NET CLI, run `dotnet run` from the `DatabaseContainers.AppHost` directory. + +When the app starts up, the `DatabaseMigrations.MigrationService` background worker runs migrations on the SQL Server container. The migration service: + +* Creates a database in the SQL Server container. +* Creates the database schema. +* Stops itself once the migration is complete. + + + +## Next steps + +Database seeding is useful in a variety of app development scenarios. Try combining these techniques with the resource implementations demonstrated in the following tutorials: + +- [Tutorial: Connect an ASP.NET Core app to SQL Server using .NET Aspire and Entity Framework Core](../database/sql-server-components.md) +- [Tutorial: Connect an ASP.NET Core app to .NET Aspire storage components](../storage/azure-storage-components.md) +- [.NET Aspire orchestration overview](../fundamentals/app-host-overview.md) From f9c9fa534b9f8e1a6977bf2eafa818b10ab1bdf6 Mon Sep 17 00:00:00 2001 From: Cam Soper Date: Fri, 3 May 2024 16:49:52 -0500 Subject: [PATCH 02/17] wip --- docs/database/ef-core-migrations.md | 74 ++++++++++++++---- .../ef-core-migrations/new-worker-service.png | Bin 0 -> 23183 bytes .../Models/SupportTicket.cs | 14 ++++ 3 files changed, 71 insertions(+), 17 deletions(-) create mode 100644 docs/database/media/ef-core-migrations/new-worker-service.png create mode 100644 docs/database/snippets/tutorial/ef-core-migrations/SupportTicketApi.Data/Models/SupportTicket.cs diff --git a/docs/database/ef-core-migrations.md b/docs/database/ef-core-migrations.md index 71c94a7b38..ca4a4f212d 100644 --- a/docs/database/ef-core-migrations.md +++ b/docs/database/ef-core-migrations.md @@ -5,37 +5,77 @@ ms.date: 3/20/2024 ms.topic: how-to --- -# Apply EF Core migrations in .NET Aspire +# Apply Entity Framework Core migrations in .NET Aspire -In this article, you learn how to configure .NET Aspire apps to seed data in a database during app startup. .NET Aspire enables you to seed data using database scripts or Entity Framework Core for common platforms such as SQL Server, PostgreSQL and MySQL. +Since .NET Aspire apps use a containerized architecture, databases are ephemeral and can be recreated at any time. Entity Framework Core (EF Core) uses a feature called [migrations](/ef/core/managing-schemas/migrations) to create and update database schemas. Since databases are recreated when the app starts, you need to apply migrations to initialize the database schema each time your app starts. This is accomplished by registering a migration service project in your app that runs migrations during startup. -## When to run migrations +In this tutorial, you learn how to configure .NET Aspire apps to run EF Core migrations during app startup. -You should run migrations whenever you make changes to your database schema. This includes creating new tables, modifying existing tables, or adding/removing columns. By running migrations, you ensure that your database schema is in sync with your application code. +[!INCLUDE [aspire-prereqs](../includes/aspire-prereqs.md)] -# Database migrations with Entity Framework Core sample app +## Obtain the starter app -This sample demonstrates how to use Entity Framework Core's [migrations feature](https://learn.microsoft.com/ef/core/managing-schemas/migrations) with Aspire. +This tutorial uses a sample app that demonstrates how to apply EF Core migrations in .NET Aspire. Use Visual Studio to clone [the sample app from GitHub](https://github.com/MicrosoftDocs/mslearn-aspire-starter) or use the following command: -The sample has three important projects: +```bash +git clone https://github.com/MicrosoftDocs/mslearn-aspire-starter +``` -- `DatabaseMigrations.ApiService` - A web app that calls the database. -- `DatabaseMigrations.MigrationService` - A background worker app that applies migrations when it starts up. -- `DatabaseMigrations.ApiModel` - The EF Core context, entities, and migrations. This project is used by the API and migration service. +The sample app is in the *SupportTicketApi* folder. Open the solution in Visual Studio or VS Code and take a moment to review the sample app and make sure it runs before proceeding. The sample app is a rudimentary support ticket API, and it contains the following projects: -`DatabaseMigrations.ApiService` and `DatabaseMigrations.MigrationService` reference a SQL Server resource. During local development the SQL Server resource is launched in a container. +- **SupporTicketApi.Api**: The ASP.NET Core project that hosts the API. +- **SupportTicketApi.Data**: Contains the EF Core contexts and models. +- **SupportTicketApi.AppHost**: Contains the Aspire app host and configuration. +- **SupportTicketApi.ServiceDefaults**: Contains the default service configurations. -## Demonstrates +Run the app to ensure it works as expected. From the Aspire dashboard, select the **https** Swagger endpoint and test the API's **GET /api/SupportTickets** endpoint by expanding the operation and selecting **Try it out**. Select **Execute** to send the request and view the response: -- How to create migrations in an Aspire solution -- How to apply migrations in an Aspire solution +```json +[ + { + "id": 1, + "title": "Initial Ticket", + "description": "Test ticket, please ignore." + } +] +``` -## Sample prerequisites +## Create migrations -This sample is written in C# and targets .NET 8.0. It requires the [.NET 8.0 SDK](https://dotnet.microsoft.com/download/dotnet/8.0) or later. +Start by creating some migrations to apply. -## Create migration +1. Open a terminal (Ctrl+\` in Visual Studio) and set *SupportTicketApi\SupportTicketApi.Api* as the current directory. +1. Use the [`dotnet ef` command-line tool](https://learn.microsoft.com/ef/core/managing-schemas/migrations/#install-the-tools) to create a new migration to capture the initial state of the database schema: + ```dotnetcli + dotnet ef migrations add InitialCreate --project ..\SupportTicketApi.Data\SupportTicketApi.Data.csproj + ``` + + The proceeding command: + + * Runs EF Core migration command-line tool in the *SupportTicketApi.Api* directory. `dotnet ef` is run in this location because the API service is where the DB context is used. + * Creates a migration named *InitialCreate*. + * Creates the migration in the in the *Migrations* folder in the *SupportTicketApi.Data* project. + +1. Modify the model so that it includes a new property. Open *SupportTicketApi.Data\Models\SupportTicket.cs* and add a new property to the `SupportTicket` class: + + :::code source="snippets/tutorial/SupportTicketApi/SupportTicketApi.Data/Models/SupportTicket.cs" range="5-13" highlight="12" + +1. Create another migration to apply the changes: + + ```dotnetcli + dotnet ef migrations add AddClosedStatus --project ..\SupportTicketApi.Data\SupportTicketApi.Data.csproj + ``` + +## Apply migrations during app startup + +Now that you have migrations to apply, you can configure the app to run them during startup. + +1. Add a new Worker Service project to the solution named *SupportTicketApi.MigrationService*. This project will contain a background service that runs migrations during app startup. + + :::image type="content" source="media/ef-core-migrations/new-worker-service.png" lightbox="media/ef-core-migrations/new-worker-service.png" alt-text="A screenshot of the new project dialog in Visual Studio for a new Worker Service project."::: + +1. The `DatabaseMigrations.ApiModel` project contains the EF Core model and migrations. The [`dotnet ef` command-line tool](https://learn.microsoft.com/ef/core/managing-schemas/migrations/#install-the-tools) can be used to create new migrations: 1. Update the `Entry` entity in database context in `MyDb1Context.cs`. Add a `Name` property: diff --git a/docs/database/media/ef-core-migrations/new-worker-service.png b/docs/database/media/ef-core-migrations/new-worker-service.png new file mode 100644 index 0000000000000000000000000000000000000000..8357b535b0ff7e1b8a87fc568d704e170525e5cb GIT binary patch literal 23183 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 Hmwxzf_g{UT literal 0 HcmV?d00001 diff --git a/docs/database/snippets/tutorial/ef-core-migrations/SupportTicketApi.Data/Models/SupportTicket.cs b/docs/database/snippets/tutorial/ef-core-migrations/SupportTicketApi.Data/Models/SupportTicket.cs new file mode 100644 index 0000000000..d13a84bb5e --- /dev/null +++ b/docs/database/snippets/tutorial/ef-core-migrations/SupportTicketApi.Data/Models/SupportTicket.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; + +namespace SupportTicketApi.Data.Models +{ + 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 IsClosed { get; set; } + } +} From e135e3aea53d5d52db5152f11b263c5640447550 Mon Sep 17 00:00:00 2001 From: Cam Soper Date: Fri, 3 May 2024 17:02:14 -0500 Subject: [PATCH 03/17] wip --- docs/database/ef-core-migrations.md | 11 +++++------ .../SupportTicketApi.Data/Models/Models.csproj | 9 +++++++++ 2 files changed, 14 insertions(+), 6 deletions(-) create mode 100644 docs/database/snippets/tutorial/ef-core-migrations/SupportTicketApi.Data/Models/Models.csproj diff --git a/docs/database/ef-core-migrations.md b/docs/database/ef-core-migrations.md index ca4a4f212d..87432c5c22 100644 --- a/docs/database/ef-core-migrations.md +++ b/docs/database/ef-core-migrations.md @@ -9,7 +9,7 @@ ms.topic: how-to Since .NET Aspire apps use a containerized architecture, databases are ephemeral and can be recreated at any time. Entity Framework Core (EF Core) uses a feature called [migrations](/ef/core/managing-schemas/migrations) to create and update database schemas. Since databases are recreated when the app starts, you need to apply migrations to initialize the database schema each time your app starts. This is accomplished by registering a migration service project in your app that runs migrations during startup. -In this tutorial, you learn how to configure .NET Aspire apps to run EF Core migrations during app startup. +In this tutorial, you learn how to configure .NET Aspire apps to run EF Core migrations during app startup. [!INCLUDE [aspire-prereqs](../includes/aspire-prereqs.md)] @@ -42,7 +42,7 @@ Run the app to ensure it works as expected. From the Aspire dashboard, select th ## Create migrations -Start by creating some migrations to apply. +Start by creating some migrations to apply. 1. Open a terminal (Ctrl+\` in Visual Studio) and set *SupportTicketApi\SupportTicketApi.Api* as the current directory. 1. Use the [`dotnet ef` command-line tool](https://learn.microsoft.com/ef/core/managing-schemas/migrations/#install-the-tools) to create a new migration to capture the initial state of the database schema: @@ -53,9 +53,9 @@ Start by creating some migrations to apply. The proceeding command: - * Runs EF Core migration command-line tool in the *SupportTicketApi.Api* directory. `dotnet ef` is run in this location because the API service is where the DB context is used. - * Creates a migration named *InitialCreate*. - * Creates the migration in the in the *Migrations* folder in the *SupportTicketApi.Data* project. + - Runs EF Core migration command-line tool in the *SupportTicketApi.Api* directory. `dotnet ef` is run in this location because the API service is where the DB context is used. + - Creates a migration named *InitialCreate*. + - Creates the migration in the in the *Migrations* folder in the *SupportTicketApi.Data* project. 1. Modify the model so that it includes a new property. Open *SupportTicketApi.Data\Models\SupportTicket.cs* and add a new property to the `SupportTicket` class: @@ -75,7 +75,6 @@ Now that you have migrations to apply, you can configure the app to run them dur :::image type="content" source="media/ef-core-migrations/new-worker-service.png" lightbox="media/ef-core-migrations/new-worker-service.png" alt-text="A screenshot of the new project dialog in Visual Studio for a new Worker Service project."::: -1. The `DatabaseMigrations.ApiModel` project contains the EF Core model and migrations. The [`dotnet ef` command-line tool](https://learn.microsoft.com/ef/core/managing-schemas/migrations/#install-the-tools) can be used to create new migrations: 1. Update the `Entry` entity in database context in `MyDb1Context.cs`. Add a `Name` property: diff --git a/docs/database/snippets/tutorial/ef-core-migrations/SupportTicketApi.Data/Models/Models.csproj b/docs/database/snippets/tutorial/ef-core-migrations/SupportTicketApi.Data/Models/Models.csproj new file mode 100644 index 0000000000..3a4487deaf --- /dev/null +++ b/docs/database/snippets/tutorial/ef-core-migrations/SupportTicketApi.Data/Models/Models.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + \ No newline at end of file From a24b5d15ec80a0a48ce6776c7ebd524d4b4f1f78 Mon Sep 17 00:00:00 2001 From: Cam Soper Date: Fri, 3 May 2024 17:13:49 -0500 Subject: [PATCH 04/17] wip --- .openpublishing.publish.config.json | 8 ++- docs/database/ef-core-migrations.md | 64 +------------------ .../Models/Models.csproj | 9 --- .../Models/SupportTicket.cs | 14 ---- 4 files changed, 8 insertions(+), 87 deletions(-) delete mode 100644 docs/database/snippets/tutorial/ef-core-migrations/SupportTicketApi.Data/Models/Models.csproj delete mode 100644 docs/database/snippets/tutorial/ef-core-migrations/SupportTicketApi.Data/Models/SupportTicket.cs diff --git a/.openpublishing.publish.config.json b/.openpublishing.publish.config.json index be049224fc..b26821ba7f 100644 --- a/.openpublishing.publish.config.json +++ b/.openpublishing.publish.config.json @@ -45,7 +45,13 @@ "url": "https://github.com/dotnet/aspire-samples", "branch": "main", "branch_mapping": {} - } + }, + { + "path_to_root": "aspire-docs-samples-solution", + "url": "https://github.com/MicrosoftDocs/aspire-docs-samples", + "branch": "solution", + "branch_mapping": {} + } ], "branch_target_mapping": { "live": [ diff --git a/docs/database/ef-core-migrations.md b/docs/database/ef-core-migrations.md index 87432c5c22..6509650c8d 100644 --- a/docs/database/ef-core-migrations.md +++ b/docs/database/ef-core-migrations.md @@ -59,66 +59,4 @@ Start by creating some migrations to apply. 1. Modify the model so that it includes a new property. Open *SupportTicketApi.Data\Models\SupportTicket.cs* and add a new property to the `SupportTicket` class: - :::code source="snippets/tutorial/SupportTicketApi/SupportTicketApi.Data/Models/SupportTicket.cs" range="5-13" highlight="12" - -1. Create another migration to apply the changes: - - ```dotnetcli - dotnet ef migrations add AddClosedStatus --project ..\SupportTicketApi.Data\SupportTicketApi.Data.csproj - ``` - -## Apply migrations during app startup - -Now that you have migrations to apply, you can configure the app to run them during startup. - -1. Add a new Worker Service project to the solution named *SupportTicketApi.MigrationService*. This project will contain a background service that runs migrations during app startup. - - :::image type="content" source="media/ef-core-migrations/new-worker-service.png" lightbox="media/ef-core-migrations/new-worker-service.png" alt-text="A screenshot of the new project dialog in Visual Studio for a new Worker Service project."::: - -The `DatabaseMigrations.ApiModel` project contains the EF Core model and migrations. The [`dotnet ef` command-line tool](https://learn.microsoft.com/ef/core/managing-schemas/migrations/#install-the-tools) can be used to create new migrations: - -1. Update the `Entry` entity in database context in `MyDb1Context.cs`. Add a `Name` property: - - ```cs - public class Entry - { - public Guid Id { get; set; } = Guid.NewGuid(); - public string? Name { get; set; } - } - ``` - -2. Open a command prompt in the `DatabaseMigrations.ApiService` directory and run the EF Core migration tool to create a migration named **MyNewMigration**. - - ```bash - dotnet ef migrations add MyNewMigration --project ..\DatabaseMigrations.ApiModel\DatabaseMigrations.ApiModel.csproj - ``` - - The proceeding command: - - * Runs EF Core migration command-line tool in the `DatabaseMigrations.ApiService` directory. `dotnet ef` is run in this location because the API service is where the DB context is used. - * Creates a migration named `MyNewMigration`. - * Creates the migration in the `DatabaseMigrations.ApiModel` project. - -4. View the new migration files in the `DatabaseMigrations.ApiModel` project. - -## Run the app - -If using Visual Studio, open the solution file `DatabaseMigrations.sln` and launch/debug the `DatabaseMigrations.AppHost` project. - -If using the .NET CLI, run `dotnet run` from the `DatabaseContainers.AppHost` directory. - -When the app starts up, the `DatabaseMigrations.MigrationService` background worker runs migrations on the SQL Server container. The migration service: - -* Creates a database in the SQL Server container. -* Creates the database schema. -* Stops itself once the migration is complete. - - - -## Next steps - -Database seeding is useful in a variety of app development scenarios. Try combining these techniques with the resource implementations demonstrated in the following tutorials: - -- [Tutorial: Connect an ASP.NET Core app to SQL Server using .NET Aspire and Entity Framework Core](../database/sql-server-components.md) -- [Tutorial: Connect an ASP.NET Core app to .NET Aspire storage components](../storage/azure-storage-components.md) -- [.NET Aspire orchestration overview](../fundamentals/app-host-overview.md) + :::code source="aspire-docs-samples-solution/SupportTicketApi/SupportTicketApi/Models/SupportTicket.cs" range="5-14" highlight="13" diff --git a/docs/database/snippets/tutorial/ef-core-migrations/SupportTicketApi.Data/Models/Models.csproj b/docs/database/snippets/tutorial/ef-core-migrations/SupportTicketApi.Data/Models/Models.csproj deleted file mode 100644 index 3a4487deaf..0000000000 --- a/docs/database/snippets/tutorial/ef-core-migrations/SupportTicketApi.Data/Models/Models.csproj +++ /dev/null @@ -1,9 +0,0 @@ - - - - net8.0 - enable - enable - - - \ No newline at end of file diff --git a/docs/database/snippets/tutorial/ef-core-migrations/SupportTicketApi.Data/Models/SupportTicket.cs b/docs/database/snippets/tutorial/ef-core-migrations/SupportTicketApi.Data/Models/SupportTicket.cs deleted file mode 100644 index d13a84bb5e..0000000000 --- a/docs/database/snippets/tutorial/ef-core-migrations/SupportTicketApi.Data/Models/SupportTicket.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace SupportTicketApi.Data.Models -{ - 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 IsClosed { get; set; } - } -} From 3254bed24ee49791aa2972f30ff7d85a292bc8d9 Mon Sep 17 00:00:00 2001 From: Cam Soper Date: Fri, 3 May 2024 17:15:12 -0500 Subject: [PATCH 05/17] more wip --- docs/database/ef-core-migrations.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/database/ef-core-migrations.md b/docs/database/ef-core-migrations.md index 6509650c8d..fcd193cdb8 100644 --- a/docs/database/ef-core-migrations.md +++ b/docs/database/ef-core-migrations.md @@ -44,7 +44,7 @@ Run the app to ensure it works as expected. From the Aspire dashboard, select th Start by creating some migrations to apply. -1. Open a terminal (Ctrl+\` in Visual Studio) and set *SupportTicketApi\SupportTicketApi.Api* as the current directory. +1. Open a terminal (Ctrl+\` in Visual Studio) and set *SupportTicketApi\SupportTicketApi.Api* as the current directory. 1. Use the [`dotnet ef` command-line tool](https://learn.microsoft.com/ef/core/managing-schemas/migrations/#install-the-tools) to create a new migration to capture the initial state of the database schema: ```dotnetcli @@ -59,4 +59,5 @@ Start by creating some migrations to apply. 1. Modify the model so that it includes a new property. Open *SupportTicketApi.Data\Models\SupportTicket.cs* and add a new property to the `SupportTicket` class: - :::code source="aspire-docs-samples-solution/SupportTicketApi/SupportTicketApi/Models/SupportTicket.cs" range="5-14" highlight="13" + :::code source="aspire-docs-samples-solution/SupportTicketApi/SupportTicketApi/Models/SupportTicket.cs" range="5-14" highlight="13"::: + \ No newline at end of file From dfcc4a2da2a4fd2f0269de04e9db30ee4ca3ec32 Mon Sep 17 00:00:00 2001 From: Cam Soper Date: Fri, 3 May 2024 17:32:51 -0500 Subject: [PATCH 06/17] code reference --- docs/database/ef-core-migrations.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/database/ef-core-migrations.md b/docs/database/ef-core-migrations.md index fcd193cdb8..806a618894 100644 --- a/docs/database/ef-core-migrations.md +++ b/docs/database/ef-core-migrations.md @@ -59,5 +59,4 @@ Start by creating some migrations to apply. 1. Modify the model so that it includes a new property. Open *SupportTicketApi.Data\Models\SupportTicket.cs* and add a new property to the `SupportTicket` class: - :::code source="aspire-docs-samples-solution/SupportTicketApi/SupportTicketApi/Models/SupportTicket.cs" range="5-14" highlight="13"::: - \ No newline at end of file + :::code source="~/aspire-docs-samples-solution/SupportTicketApi/SupportTicketApi/Models/SupportTicket.cs" range="5-14" highlight="13"::: From 7b496be629e26f0508d395ed79d0e66ceb62ac18 Mon Sep 17 00:00:00 2001 From: Cam Soper Date: Fri, 3 May 2024 17:37:18 -0500 Subject: [PATCH 07/17] Code ref --- docs/database/ef-core-migrations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/database/ef-core-migrations.md b/docs/database/ef-core-migrations.md index 806a618894..1dfac28b8f 100644 --- a/docs/database/ef-core-migrations.md +++ b/docs/database/ef-core-migrations.md @@ -59,4 +59,4 @@ Start by creating some migrations to apply. 1. Modify the model so that it includes a new property. Open *SupportTicketApi.Data\Models\SupportTicket.cs* and add a new property to the `SupportTicket` class: - :::code source="~/aspire-docs-samples-solution/SupportTicketApi/SupportTicketApi/Models/SupportTicket.cs" range="5-14" highlight="13"::: + :::code source="~/aspire-docs-samples-solution/SupportTicketApi/SupportTicketApi/Models/SupportTicket.cs" range="5-14" highlight="9" ::: From adbebee0c6f54c2671a8c71ddb17dab66efd850a Mon Sep 17 00:00:00 2001 From: Cam Soper Date: Fri, 3 May 2024 18:05:18 -0500 Subject: [PATCH 08/17] wip --- docs/database/ef-core-migrations.md | 45 +++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/docs/database/ef-core-migrations.md b/docs/database/ef-core-migrations.md index 1dfac28b8f..79afb653ef 100644 --- a/docs/database/ef-core-migrations.md +++ b/docs/database/ef-core-migrations.md @@ -60,3 +60,48 @@ Start by creating some migrations to apply. 1. Modify the model so that it includes a new property. Open *SupportTicketApi.Data\Models\SupportTicket.cs* and add a new property to the `SupportTicket` class: :::code source="~/aspire-docs-samples-solution/SupportTicketApi/SupportTicketApi/Models/SupportTicket.cs" range="5-14" highlight="9" ::: + +1. Create a new migration to capture the changes to the model: + + ```dotnetcli + dotnet ef migrations add AddCompleted --project ..\SupportTicketApi.Data\SupportTicketApi.Data.csproj + ``` + +Now you've got some migrations to apply. Next, you'll create a migration service that applies these migrations during app startup. + +## Create the migration service + +To run the migrations at startup, you need to create a service that applies the migrations. + +1. Add a new Worker Service project to the solution. If using Visual Studio, right-click the solution in Solution Explorer and select **Add** > **New Project**. Select **Worker Service** and name the project *SupportTicketApi.Migrations*. If using the command line, use the following commands from the solution directory: + + ```dotnetcli + dotnet new worker -n SupportTicketApi.Migrations + dotnet sln add SupportTicketApi.Migrations + ``` + +1. Add the following project references the *ServiceDefaults* and *Data* project to the *SupportTicketApi.Migrations* project using Visual Studio or the command line: + + ```dotnetcli + dotnet add SupportTicketApi.Migrations reference SupportTicketApi.Data + dotnet add SupportTicketApi.Migrations reference SupportTicketApi.ServiceDefaults + ``` + +1. Add the following NuGet package references to the *SupportTicketApi.Migrations* project using Visual Studio or the command line: + + ```dotnetcli + dotnet add package Aspire.Microsoft.EntityFrameworkCore.SqlServer + ``` + +1. Replace the contents of the *Worker.cs* file in the *SupportTicketApi.Migrations* project with the following code: + + **CODE HERE** + + The preceding code: + + 1. explain code here (wip) + +Remaining steps are WIP: + +1. Add the migration service to the app host. +1. Remove the seed logic in the API From 1f601d13b2398ccefdb76283a84aaae2e877b482 Mon Sep 17 00:00:00 2001 From: Cam Soper Date: Tue, 7 May 2024 18:11:54 -0500 Subject: [PATCH 09/17] wip --- docs/database/ef-core-migrations.md | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/docs/database/ef-core-migrations.md b/docs/database/ef-core-migrations.md index 79afb653ef..d495bbedef 100644 --- a/docs/database/ef-core-migrations.md +++ b/docs/database/ef-core-migrations.md @@ -44,7 +44,8 @@ Run the app to ensure it works as expected. From the Aspire dashboard, select th Start by creating some migrations to apply. -1. Open a terminal (Ctrl+\` in Visual Studio) and set *SupportTicketApi\SupportTicketApi.Api* as the current directory. +1. Open a terminal (Ctrl+\` in Visual Studio). +1. Set *SupportTicketApi\SupportTicketApi.Api* as the current directory. 1. Use the [`dotnet ef` command-line tool](https://learn.microsoft.com/ef/core/managing-schemas/migrations/#install-the-tools) to create a new migration to capture the initial state of the database schema: ```dotnetcli @@ -59,7 +60,7 @@ Start by creating some migrations to apply. 1. Modify the model so that it includes a new property. Open *SupportTicketApi.Data\Models\SupportTicket.cs* and add a new property to the `SupportTicket` class: - :::code source="~/aspire-docs-samples-solution/SupportTicketApi/SupportTicketApi/Models/SupportTicket.cs" range="5-14" highlight="9" ::: + :::code source="~/aspire-docs-samples-solution/SupportTicketApi/SupportTicketApi.Data/Models/SupportTicket.cs" range="5-13" highlight="8" ::: 1. Create a new migration to capture the changes to the model: @@ -80,14 +81,14 @@ To run the migrations at startup, you need to create a service that applies the dotnet sln add SupportTicketApi.Migrations ``` -1. Add the following project references the *ServiceDefaults* and *Data* project to the *SupportTicketApi.Migrations* project using Visual Studio or the command line: +1. Add the *SupportTicketApi.Data* and *SupportTicketApi.ServiceDefaults* project references to the *SupportTicketApi.Migrations* project using Visual Studio or the command line: ```dotnetcli dotnet add SupportTicketApi.Migrations reference SupportTicketApi.Data dotnet add SupportTicketApi.Migrations reference SupportTicketApi.ServiceDefaults ``` -1. Add the following NuGet package references to the *SupportTicketApi.Migrations* project using Visual Studio or the command line: +1. Add the *Aspire.Microsoft.EntityFrameworkCore.SqlServer* NuGet package reference to the *SupportTicketApi.Migrations* project using Visual Studio or the command line: ```dotnetcli dotnet add package Aspire.Microsoft.EntityFrameworkCore.SqlServer @@ -95,11 +96,17 @@ To run the migrations at startup, you need to create a service that applies the 1. Replace the contents of the *Worker.cs* file in the *SupportTicketApi.Migrations* project with the following code: - **CODE HERE** + :::code source="~/aspire-docs-samples-solution/SupportTicketApi/SupportTicketApi.Migrations/Worker.cs" ::: - The preceding code: + In the preceding code: - 1. explain code here (wip) + - The `ExecuteAsync` method is called when the worker starts. It in turn performs the following steps: + 1. Gets a reference to the `TicketContext` service from the service provider. + 1. Calls `EnsureDatabaseAsync` to create the database if it doesn't exist. + 1. Calls `RunMigrationAsync` to apply any pending migrations. + 1. Calls `SeedDataAsync` to seed the database with initial data. + 1. Stops the worker with `StopApplication`. + - `EnsureDatabaseAsync`, `RunMigrationAsync`, and `SeedDataAsync` all encapsulate their database operations using execution strategies to handle transient errors that may occur when interacting with the database. To learn more about execution strategies, see [Connection Resiliency](/ef/core/miscellaneous/connection-resiliency). Remaining steps are WIP: From 6e051248126ed502d24e1f3f1238df5476e42420 Mon Sep 17 00:00:00 2001 From: Cam Soper Date: Tue, 7 May 2024 18:18:38 -0500 Subject: [PATCH 10/17] fix code sample --- docs/database/ef-core-migrations.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/database/ef-core-migrations.md b/docs/database/ef-core-migrations.md index d495bbedef..4aa8176381 100644 --- a/docs/database/ef-core-migrations.md +++ b/docs/database/ef-core-migrations.md @@ -74,29 +74,29 @@ Now you've got some migrations to apply. Next, you'll create a migration service To run the migrations at startup, you need to create a service that applies the migrations. -1. Add a new Worker Service project to the solution. If using Visual Studio, right-click the solution in Solution Explorer and select **Add** > **New Project**. Select **Worker Service** and name the project *SupportTicketApi.Migrations*. If using the command line, use the following commands from the solution directory: +1. Add a new Worker Service project to the solution. If using Visual Studio, right-click the solution in Solution Explorer and select **Add** > **New Project**. Select **Worker Service** and name the project *SupportTicketApi.MigrationService*. If using the command line, use the following commands from the solution directory: ```dotnetcli - dotnet new worker -n SupportTicketApi.Migrations - dotnet sln add SupportTicketApi.Migrations + dotnet new worker -n SupportTicketApi.MigrationService + dotnet sln add SupportTicketApi.MigrationService ``` -1. Add the *SupportTicketApi.Data* and *SupportTicketApi.ServiceDefaults* project references to the *SupportTicketApi.Migrations* project using Visual Studio or the command line: +1. Add the *SupportTicketApi.Data* and *SupportTicketApi.ServiceDefaults* project references to the *SupportTicketApi.MigrationService* project using Visual Studio or the command line: ```dotnetcli - dotnet add SupportTicketApi.Migrations reference SupportTicketApi.Data - dotnet add SupportTicketApi.Migrations reference SupportTicketApi.ServiceDefaults + dotnet add SupportTicketApi.MigrationService reference SupportTicketApi.Data + dotnet add SupportTicketApi.MigrationService reference SupportTicketApi.ServiceDefaults ``` -1. Add the *Aspire.Microsoft.EntityFrameworkCore.SqlServer* NuGet package reference to the *SupportTicketApi.Migrations* project using Visual Studio or the command line: +1. Add the *Aspire.Microsoft.EntityFrameworkCore.SqlServer* NuGet package reference to the *SupportTicketApi.MigrationService* project using Visual Studio or the command line: ```dotnetcli dotnet add package Aspire.Microsoft.EntityFrameworkCore.SqlServer ``` -1. Replace the contents of the *Worker.cs* file in the *SupportTicketApi.Migrations* project with the following code: +1. Replace the contents of the *Worker.cs* file in the *SupportTicketApi.MigrationService* project with the following code: - :::code source="~/aspire-docs-samples-solution/SupportTicketApi/SupportTicketApi.Migrations/Worker.cs" ::: + :::code source="~/aspire-docs-samples-solution/SupportTicketApi/SupportTicketApi.MigrationService/Worker.cs" ::: In the preceding code: @@ -106,7 +106,7 @@ To run the migrations at startup, you need to create a service that applies the 1. Calls `RunMigrationAsync` to apply any pending migrations. 1. Calls `SeedDataAsync` to seed the database with initial data. 1. Stops the worker with `StopApplication`. - - `EnsureDatabaseAsync`, `RunMigrationAsync`, and `SeedDataAsync` all encapsulate their database operations using execution strategies to handle transient errors that may occur when interacting with the database. To learn more about execution strategies, see [Connection Resiliency](/ef/core/miscellaneous/connection-resiliency). + - The `EnsureDatabaseAsync`, `RunMigrationAsync`, and `SeedDataAsync` methods all encapsulate their respective database operations using execution strategies to handle transient errors that may occur when interacting with the database. To learn more about execution strategies, see [Connection Resiliency](/ef/core/miscellaneous/connection-resiliency). Remaining steps are WIP: From 005c72484bec7c1ed4e71c57449ad83d29d43e4c Mon Sep 17 00:00:00 2001 From: Cam Soper Date: Wed, 8 May 2024 15:50:48 -0500 Subject: [PATCH 11/17] wip --- .openpublishing.publish.config.json | 8 +++++++- docs/database/ef-core-migrations.md | 14 +++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/.openpublishing.publish.config.json b/.openpublishing.publish.config.json index b26821ba7f..e635a883af 100644 --- a/.openpublishing.publish.config.json +++ b/.openpublishing.publish.config.json @@ -51,7 +51,13 @@ "url": "https://github.com/MicrosoftDocs/aspire-docs-samples", "branch": "solution", "branch_mapping": {} - } + }, + { + "path_to_root": "aspire-docs-samples-main", + "url": "https://github.com/MicrosoftDocs/aspire-docs-samples", + "branch": "main", + "branch_mapping": {} + } ], "branch_target_mapping": { "live": [ diff --git a/docs/database/ef-core-migrations.md b/docs/database/ef-core-migrations.md index 4aa8176381..bd33f1a1ee 100644 --- a/docs/database/ef-core-migrations.md +++ b/docs/database/ef-core-migrations.md @@ -62,7 +62,7 @@ Start by creating some migrations to apply. :::code source="~/aspire-docs-samples-solution/SupportTicketApi/SupportTicketApi.Data/Models/SupportTicket.cs" range="5-13" highlight="8" ::: -1. Create a new migration to capture the changes to the model: +1. Create another new migration to capture the changes to the model: ```dotnetcli dotnet ef migrations add AddCompleted --project ..\SupportTicketApi.Data\SupportTicketApi.Data.csproj @@ -108,7 +108,15 @@ To run the migrations at startup, you need to create a service that applies the 1. Stops the worker with `StopApplication`. - The `EnsureDatabaseAsync`, `RunMigrationAsync`, and `SeedDataAsync` methods all encapsulate their respective database operations using execution strategies to handle transient errors that may occur when interacting with the database. To learn more about execution strategies, see [Connection Resiliency](/ef/core/miscellaneous/connection-resiliency). -Remaining steps are WIP: +## Add the migration service to the Aspire orchestration 1. Add the migration service to the app host. -1. Remove the seed logic in the API + +## Remove existing seeding logic + +Since the migration service seeds the database, you should remove the existing seeding logic from the API project. + +1. In the *SupportTicketApi.Api* project, open the *Program.cs* file. +1. Delete the highlighted lines. + + :::code source="~/aspire-docs-samples-main/SupportTicketApi/SupportTicketApi.Api/Program.cs" range="20-36" highlight="6-16" ::: From 777dacf0ad40f55d153f50db61c55a98a3a2c02f Mon Sep 17 00:00:00 2001 From: Cam Soper Date: Wed, 8 May 2024 17:22:16 -0500 Subject: [PATCH 12/17] First draft done --- docs/database/ef-core-migrations.md | 32 ++++++++++++++++-- .../dashboard-post-migration.png | Bin 0 -> 39865 bytes .../ef-core-migrations/new-worker-service.png | Bin 23183 -> 0 bytes 3 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 docs/database/media/ef-core-migrations/dashboard-post-migration.png delete mode 100644 docs/database/media/ef-core-migrations/new-worker-service.png diff --git a/docs/database/ef-core-migrations.md b/docs/database/ef-core-migrations.md index bd33f1a1ee..cc9cd5aaaf 100644 --- a/docs/database/ef-core-migrations.md +++ b/docs/database/ef-core-migrations.md @@ -110,13 +110,39 @@ To run the migrations at startup, you need to create a service that applies the ## Add the migration service to the Aspire orchestration -1. Add the migration service to the app host. +The migration service is created, but it needs to be added to the Aspire app host so that it runs when the app starts. -## Remove existing seeding logic +1. In the *SupportTicketApi.AppHost* project, open the *Program.cs* file. +1. Add the following highlighted code to the `ConfigureServices` method: -Since the migration service seeds the database, you should remove the existing seeding logic from the API project. + :::code source="~/aspire-docs-samples-solution/SupportTicketApi/SupportTicketApi.AppHost/Program.cs" highlight="9-10" ::: + + This enlists the *SupportTicketApi.MigrationService* project as a service in the Aspire app host. + + > [!IMPORTANT] + > If you are using Visual Studio, and you selected the **Enlist in Aspire orchestration** option when creating the Worker Service project, similar code is added automatically with the service name `supportticketapi-migrationservice`. Replace that code with the preceding code. + +## Remove existing seeding code + +Since the migration service seeds the database, you should remove the existing data seeding code from the API project. 1. In the *SupportTicketApi.Api* project, open the *Program.cs* file. 1. Delete the highlighted lines. :::code source="~/aspire-docs-samples-main/SupportTicketApi/SupportTicketApi.Api/Program.cs" range="20-36" highlight="6-16" ::: + +## Test the migration service + +Now that the migration service is configured, run the app to test the migrations. + +1. Run the app and observe the SupportTicketApi dashboard. +1. After a short wait, the `migrations` service state will display **Finished**. + + :::image type="content" source="media/ef-core-migrations/dashboard-post-migration.png" lightbox="media/ef-core-migrations/dashboard-post-migration.png" alt-text="A screenshot of the Aspire dashboard with the migration service in a Finished state." ::: + +1. Select the **View** link on the migration service to investigate the logs showing the SQL commands that were executed. + +## Get the code + +You can find the [completed sample app on GitHub](https://github.com/MicrosoftDocs/aspire-docs-samples/tree/solution/SupportTicketApi). + diff --git a/docs/database/media/ef-core-migrations/dashboard-post-migration.png b/docs/database/media/ef-core-migrations/dashboard-post-migration.png new file mode 100644 index 0000000000000000000000000000000000000000..5e21d4de0f264dea33b1c827d89989d99ac03496 GIT binary patch literal 39865 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 Db1_uK literal 0 HcmV?d00001 diff --git a/docs/database/media/ef-core-migrations/new-worker-service.png b/docs/database/media/ef-core-migrations/new-worker-service.png deleted file mode 100644 index 8357b535b0ff7e1b8a87fc568d704e170525e5cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23183 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 Hmwxzf_g{UT From 1787c54c802be95fa2f1b7b124e187d519a43d7a Mon Sep 17 00:00:00 2001 From: Cam Soper Date: Wed, 8 May 2024 17:36:18 -0500 Subject: [PATCH 13/17] lint fix --- docs/database/ef-core-migrations.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/database/ef-core-migrations.md b/docs/database/ef-core-migrations.md index cc9cd5aaaf..5c234b6d35 100644 --- a/docs/database/ef-core-migrations.md +++ b/docs/database/ef-core-migrations.md @@ -145,4 +145,3 @@ Now that the migration service is configured, run the app to test the migrations ## Get the code You can find the [completed sample app on GitHub](https://github.com/MicrosoftDocs/aspire-docs-samples/tree/solution/SupportTicketApi). - From a3ef4ca9514660733e5bbea28e7c5f6932670447 Mon Sep 17 00:00:00 2001 From: Cam Soper Date: Thu, 9 May 2024 15:39:06 -0500 Subject: [PATCH 14/17] Merge branch 'main' of https://github.com/dotnet/docs-aspire into camsoper/488 From 2570e85162f8f4c20cd09b114e1fb13ed642c60a Mon Sep 17 00:00:00 2001 From: Cam Soper Date: Thu, 9 May 2024 15:44:54 -0500 Subject: [PATCH 15/17] added toc and index --- docs/index.yml | 3 +++ docs/toc.yml | 2 ++ 2 files changed, 5 insertions(+) diff --git a/docs/index.yml b/docs/index.yml index bf34a748ef..4612c9b9a5 100644 --- a/docs/index.yml +++ b/docs/index.yml @@ -109,6 +109,9 @@ conceptualContent: - itemType: how-to-guide text: SQL Database with EF Core url: database/sql-server-entity-framework-component.md + - itemType: how-to-guide + text: Entity Framework Core migrations + url: database/ef-core-migrations.md - itemType: how-to-guide text: MySqlConnector Database url: database/mysql-component.md diff --git a/docs/toc.yml b/docs/toc.yml index c1884fb72c..1090cc094e 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -118,6 +118,8 @@ items: href: database/oracle-entity-framework-component.md - name: Seed data in a database href: database/seed-database-data.md + - name: Entity Framework Core migrations + href: database/ef-core-migrations.md - name: Real-time communication items: From 1a0b206e15d54492cdab3577bd4747278a95c5c6 Mon Sep 17 00:00:00 2001 From: Cam Soper Date: Fri, 10 May 2024 10:02:46 -0500 Subject: [PATCH 16/17] Apply suggestions from code review Thanks for the suggestions, @IEvangelist! Co-authored-by: David Pine --- docs/database/ef-core-migrations.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/database/ef-core-migrations.md b/docs/database/ef-core-migrations.md index 5c234b6d35..3d3e481026 100644 --- a/docs/database/ef-core-migrations.md +++ b/docs/database/ef-core-migrations.md @@ -25,10 +25,10 @@ The sample app is in the *SupportTicketApi* folder. Open the solution in Visual - **SupporTicketApi.Api**: The ASP.NET Core project that hosts the API. - **SupportTicketApi.Data**: Contains the EF Core contexts and models. -- **SupportTicketApi.AppHost**: Contains the Aspire app host and configuration. +- **SupportTicketApi.AppHost**: Contains the .NET Aspire app host and configuration. - **SupportTicketApi.ServiceDefaults**: Contains the default service configurations. -Run the app to ensure it works as expected. From the Aspire dashboard, select the **https** Swagger endpoint and test the API's **GET /api/SupportTickets** endpoint by expanding the operation and selecting **Try it out**. Select **Execute** to send the request and view the response: +Run the app to ensure it works as expected. From the .NET Aspire dashboard, select the **https** Swagger endpoint and test the API's **GET /api/SupportTickets** endpoint by expanding the operation and selecting **Try it out**. Select **Execute** to send the request and view the response: ```json [ @@ -108,16 +108,16 @@ To run the migrations at startup, you need to create a service that applies the 1. Stops the worker with `StopApplication`. - The `EnsureDatabaseAsync`, `RunMigrationAsync`, and `SeedDataAsync` methods all encapsulate their respective database operations using execution strategies to handle transient errors that may occur when interacting with the database. To learn more about execution strategies, see [Connection Resiliency](/ef/core/miscellaneous/connection-resiliency). -## Add the migration service to the Aspire orchestration +## Add the migration service to the orchestrator -The migration service is created, but it needs to be added to the Aspire app host so that it runs when the app starts. +The migration service is created, but it needs to be added to the .NET Aspire app host so that it runs when the app starts. 1. In the *SupportTicketApi.AppHost* project, open the *Program.cs* file. 1. Add the following highlighted code to the `ConfigureServices` method: :::code source="~/aspire-docs-samples-solution/SupportTicketApi/SupportTicketApi.AppHost/Program.cs" highlight="9-10" ::: - This enlists the *SupportTicketApi.MigrationService* project as a service in the Aspire app host. + This enlists the *SupportTicketApi.MigrationService* project as a service in the .NET Aspire app host. > [!IMPORTANT] > If you are using Visual Studio, and you selected the **Enlist in Aspire orchestration** option when creating the Worker Service project, similar code is added automatically with the service name `supportticketapi-migrationservice`. Replace that code with the preceding code. @@ -138,7 +138,7 @@ Now that the migration service is configured, run the app to test the migrations 1. Run the app and observe the SupportTicketApi dashboard. 1. After a short wait, the `migrations` service state will display **Finished**. - :::image type="content" source="media/ef-core-migrations/dashboard-post-migration.png" lightbox="media/ef-core-migrations/dashboard-post-migration.png" alt-text="A screenshot of the Aspire dashboard with the migration service in a Finished state." ::: + :::image type="content" source="media/ef-core-migrations/dashboard-post-migration.png" lightbox="media/ef-core-migrations/dashboard-post-migration.png" alt-text="A screenshot of the .NET Aspire dashboard with the migration service in a Finished state." ::: 1. Select the **View** link on the migration service to investigate the logs showing the SQL commands that were executed. From 6c7502000469ddbded9ab8f3972810b63136f5de Mon Sep 17 00:00:00 2001 From: David Pine Date: Fri, 10 May 2024 10:45:16 -0500 Subject: [PATCH 17/17] Update docs/database/ef-core-migrations.md --- docs/database/ef-core-migrations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/database/ef-core-migrations.md b/docs/database/ef-core-migrations.md index 3d3e481026..cfc4b315fd 100644 --- a/docs/database/ef-core-migrations.md +++ b/docs/database/ef-core-migrations.md @@ -46,7 +46,7 @@ Start by creating some migrations to apply. 1. Open a terminal (Ctrl+\` in Visual Studio). 1. Set *SupportTicketApi\SupportTicketApi.Api* as the current directory. -1. Use the [`dotnet ef` command-line tool](https://learn.microsoft.com/ef/core/managing-schemas/migrations/#install-the-tools) to create a new migration to capture the initial state of the database schema: +1. Use the [`dotnet ef` command-line tool](/ef/core/managing-schemas/migrations/#install-the-tools) to create a new migration to capture the initial state of the database schema: ```dotnetcli dotnet ef migrations add InitialCreate --project ..\SupportTicketApi.Data\SupportTicketApi.Data.csproj