diff --git a/docs/database/seed-database-data.md b/docs/database/seed-database-data.md index 493a49b443..ad5a59622b 100644 --- a/docs/database/seed-database-data.md +++ b/docs/database/seed-database-data.md @@ -36,11 +36,11 @@ By default, .NET Aspire database integrations rely on containerized databases, w ## Seed data using SQL scripts -In .NET Aspire 9.2, the recommended method for executing database seeding scripts depends on the database server you use: +The recommended method for executing database seeding scripts depends on the database server you use: ### [SQL Server](#tab/sql-server) -In .NET Aspire 9.2 and later versions, you can use the method to ensure a T-SQL script is run when the database is created. Add SQL code to this script that creates and populates the database, the necessary tables, and other database objects. +Starting with .NET Aspire 9.2, you can use the method to ensure a T-SQL script is run when the database is created. Add SQL code to this script that creates and populates the database, the necessary tables, and other database objects. The following code is an example T-SQL script that creates and populates an address book database: @@ -56,7 +56,7 @@ Next, in the app host's *AppHost.cs* (or *Program.cs*) file, create the database :::code source="~/aspire-samples/samples/DatabaseContainers/DatabaseContainers.AppHost/AppHost.cs" range="40-49" ::: -This code: +The preceding code: - Create a SQL Server container by calling `builder.AddSqlServer()`. - Ensures that data is persisted across debugging sessions by calling `WithDataVolume()` and `WithLifetime(ContainerLifetime.Persistent)`. @@ -65,7 +65,7 @@ This code: ### [PostgreSQL](#tab/postgresql) -In .NET Aspire 9.2, the `WithCreationScript()` method isn't supported for the PostgreSQL integration. Instead, you must use a bind mount and deploy the setup SQL script to it, so that the data is seeded when the container initializes the database. +Starting with .NET Aspire 9.3, the `WithCreationScript()` method is supported for the PostgreSQL integration but, because there is no `USE DATABASE` in PostgreSQL, it only supports operations against the default database. For example, you can issue `CREATE DATABASE` statements to create other databases, but you can't populate them with tables and data. Instead, you must use a bind mount and deploy the setup SQL script to it, so that the data is seeded when the container initializes the database. The following code is an example PostgreSQL script that creates and populates a to do list database: @@ -77,15 +77,21 @@ In the app host's *AppHost.cs* (or *Program.cs*) file, create the database and m ### [MySQL](#tab/mysql) -In .NET Aspire 9.2, the `WithCreationScript()` method isn't supported for the MySQL integration. Instead, you must use a bind mount and deploy the setup SQL script to it, so that the data is seeded when the container initializes the database. +Starting with .NET Aspire 9.3, you can use the method to ensure a MySQL script is run when the database is created. Add SQL code to this script that creates and populates the database, the necessary tables, and other database objects. -The following code is an example MySQL script that creates and populates a product catalog database: +In the following App Host code, the script is created as a string and passed to the `WithCreationScript` method: -:::code source="~/aspire-samples/samples/DatabaseContainers/DatabaseContainers.ApiService/data/mysql/init.sql" ::: +:::code source="snippets/mysql-seed-data/AppHost.cs" ::: -In the app host's *AppHost.cs* (or *Program.cs*) file, create the database and mount the folder that contains the SQL script as a bind mount: +The preceding code: + +- Create a MySQL container by calling `builder.AddMySql()`. +- Uses the `MYSQL_DATABASE` environment variable to name the database `catalog`. +- Ensures that data is persisted across debugging sessions by calling `WithDataVolume()` and `WithLifetime(ContainerLifetime.Persistent)`. +- Create a second container that runs the PHP My Admin user interface for MySQL. +- Calls `WithCreationScript()` to create and seed the database. -:::code source="~/aspire-samples/samples/DatabaseContainers/DatabaseContainers.AppHost/AppHost.cs" range="21-36" ::: +If you run this code, you can use the PHP My Admin resource to check that a table called **catalog** has been created and populated with products. --- @@ -96,7 +102,7 @@ You can also seed data in .NET Aspire projects using EF Core by explicitly runni > [!IMPORTANT] > These types of configurations should only be done during development, so make sure to add a conditional that checks your current environment context. -Add the following code to the _:::no-loc text="Program.cs"::: file of your **API Service** project. +Add the following code to the _:::no-loc text="Program.cs":::_ file of your **API Service** project. ### [SQL Server](#tab/sql-server) diff --git a/docs/database/snippets/mysql-seed-data/.aspire/settings.json b/docs/database/snippets/mysql-seed-data/.aspire/settings.json new file mode 100644 index 0000000000..17c5d25ab1 --- /dev/null +++ b/docs/database/snippets/mysql-seed-data/.aspire/settings.json @@ -0,0 +1,3 @@ +{ + "appHostPath": "../MySQLSeedData.csproj" +} \ No newline at end of file diff --git a/docs/database/snippets/mysql-seed-data/AppHost.cs b/docs/database/snippets/mysql-seed-data/AppHost.cs new file mode 100644 index 0000000000..b7d2ef03ec --- /dev/null +++ b/docs/database/snippets/mysql-seed-data/AppHost.cs @@ -0,0 +1,37 @@ +var builder = DistributedApplication.CreateBuilder(args); + +var catalogDbName = "catalog"; + +var mysql = builder.AddMySql("mysql") + .WithEnvironment("MYSQL_DATABASE", catalogDbName) + .WithDataVolume() + .WithLifetime(ContainerLifetime.Persistent) + .WithPhpMyAdmin(); + +var creationScript = $$""" +USE catalog; +CREATE TABLE IF NOT EXISTS `catalog` +( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL, + `description` varchar(255) NOT NULL, + `price` DECIMAL(18,2) NOT NULL, + PRIMARY KEY (`id`) +); + +-- Insert some sample data into the Catalog table only if the table is empty +INSERT INTO catalog (name, description, price) +SELECT * +FROM ( + SELECT '.NET Bot Black Hoodie', 'This hoodie will keep you warm while looking cool and representing .NET!', 19.5 UNION ALL + SELECT '.NET Black & White Mug', 'The perfect place to keep your favorite beverage while you code.', 8.5 UNION ALL + SELECT 'Prism White T-Shirt', "It's a t-shirt, it's white, and it can be yours.", 12 + ) data +-- This clause ensures the rows are only inserted if the table is empty +WHERE NOT EXISTS (SELECT NULL FROM catalog) +"""; + +var mysqldb = mysql.AddDatabase(catalogDbName) + .WithCreationScript(creationScript); + +builder.Build().Run(); diff --git a/docs/database/snippets/mysql-seed-data/MySQLSeedData.csproj b/docs/database/snippets/mysql-seed-data/MySQLSeedData.csproj new file mode 100644 index 0000000000..5709a9f183 --- /dev/null +++ b/docs/database/snippets/mysql-seed-data/MySQLSeedData.csproj @@ -0,0 +1,18 @@ + + + + + + Exe + net9.0 + enable + enable + 2f0f3679-5f3c-4fe7-957d-ec0d9fabfda4 + + + + + + + + diff --git a/docs/database/snippets/mysql-seed-data/Properties/launchSettings.json b/docs/database/snippets/mysql-seed-data/Properties/launchSettings.json new file mode 100644 index 0000000000..f8f95f112b --- /dev/null +++ b/docs/database/snippets/mysql-seed-data/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:17022;http://localhost:15236", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21048", + "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22281" + } + }, + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:15236", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19255", + "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20128" + } + } + } +} diff --git a/docs/database/snippets/mysql-seed-data/appsettings.Development.json b/docs/database/snippets/mysql-seed-data/appsettings.Development.json new file mode 100644 index 0000000000..0c208ae918 --- /dev/null +++ b/docs/database/snippets/mysql-seed-data/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/docs/database/snippets/mysql-seed-data/appsettings.json b/docs/database/snippets/mysql-seed-data/appsettings.json new file mode 100644 index 0000000000..31c092aa45 --- /dev/null +++ b/docs/database/snippets/mysql-seed-data/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Aspire.Hosting.Dcp": "Warning" + } + } +} diff --git a/docs/database/snippets/mysql-seed-data/mysql-seed-data.sln b/docs/database/snippets/mysql-seed-data/mysql-seed-data.sln new file mode 100644 index 0000000000..1eb903c01c --- /dev/null +++ b/docs/database/snippets/mysql-seed-data/mysql-seed-data.sln @@ -0,0 +1,24 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.2.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MySQLSeedData", "MySQLSeedData.csproj", "{62FFBCD3-B001-5E15-DD62-F84BFCFEA366}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {62FFBCD3-B001-5E15-DD62-F84BFCFEA366}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {62FFBCD3-B001-5E15-DD62-F84BFCFEA366}.Debug|Any CPU.Build.0 = Debug|Any CPU + {62FFBCD3-B001-5E15-DD62-F84BFCFEA366}.Release|Any CPU.ActiveCfg = Release|Any CPU + {62FFBCD3-B001-5E15-DD62-F84BFCFEA366}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {349C2004-BE30-4CFC-93D4-5995A9408BF3} + EndGlobalSection +EndGlobal